This commit is contained in:
2026-09-09 17:35:23 +08:00
parent 2a98ae7556
commit 161af68fe3
1271 changed files with 58251 additions and 98639 deletions
@@ -0,0 +1,600 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Controller Brand | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Controller Brand | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerBrandSwitch">
<h1 id="controller-brand">Controller Brand</h1>
<p>Controller Brand is the first leaf of the Controller branch's machine plane, and the one leaf whose
write replaces the entire NC runner instead of one field of one table — the branch root's
Object-Management Load and Paste replace it by the other route, described below. It sits on
the General Setup page at <code>/general-setup</code> under the Control-Tree id
<code>equipment/controller/machine/brand</code>, and it is the only leaf whose write changes which of the
branch's other nodes exist. The tree row reads <strong>Controller Brand</strong>; the select inside the panel is
labelled <strong>Controller brand</strong>.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that the id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<h2 id="what-the-brand-actually-selects">What the Brand Actually Selects</h2>
<p>The brand marker itself is a plain string carried by
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.CncBrandDependency.html">CncBrandDependency</a>, and nothing on the switch path reads it: the preset
that answers a pick is chosen by the token the request carries, and what the installed runner does
afterwards follows from that preset's own dependency list. What the panel writes is a <em>preset</em>: a
whole <a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html">SoftNcRunner</a> built fresh from the engine's brand property, carrying its own
syntax list, its own segmenter, its own initializers and its own dependency list. Four of the five
segment one block per line and Heidenhain brings a segmenter of its own; Fanuc, Syntec and Mazak
share an ISO initializer while Siemens and Heidenhain each bring theirs. So a brand switch changes
the dialect a program is read in, not only the tables the branch shows.</p>
<p>Away from this panel the marker is not inert. NC optimization reads it back out of the session's
effective dependency list for the writeback patch grammar, which takes five things from the marker:
its variable prefix, its comment spans, its keyword set, the place an inserted <code>F</code> word lands — on
Heidenhain after the rightmost <code>DR+</code>/<code>DR-</code> and <code>RL</code>/<code>RR</code>/<code>R0</code>, so the element order a TNC enforces
holds — and the shape of the source note the optimizer embeds, a <code>;</code> comment on Heidenhain and a
parenthesized one on the rest. The radius-compensation syntax all five presets
carry reads it too, raising its negative-radius validation warning only where the marker is
Heidenhain. <strong>The piece classifier does not read it</strong>: none of its re-interpolation guards is
keyed on the brand, and the guard that singles a klartext arc out is keyed on where the arc's
centre came from. So an arc centred on a modal <code>CC</code> line splits like any other, and the one
refused (<code>NcOpt--SplitionStartPointCenterUnsupported</code>) is the arc whose <code>CC</code> chain left an
in-plane coordinate for its own start point to supply. An arc whose centre falls entirely on its
start point never reaches that guard: with no radius it is not an arc at all, so it warns
<code>Arc-CircleCenter--OnStartPoint</code> and is degraded to a chord as the program is read.</p>
<p>Each of the five properties returns a <strong>new instance per read</strong>, so no two projects can share one
preset and the returned runner is safe to mutate before it is installed. The five brand tokens are
declared once as constants on the brand dependency and are then enumerated again, independently, in
the engine's preset writer, in the REST action's own switch and in the client's option constant —
the first two by reference to the constants, the client by spelling the names out. Nothing joins
those lists, and they are not the only places in the engine where the five are spelled out again, so
the option a reader picks and the preset that answers it agree by convention rather than by
construction.</p>
<p>The option list this panel offers is the client-side constant: <strong>Fanuc</strong>, <strong>Siemens</strong>,
<strong>Heidenhain</strong>, <strong>Syntec</strong>, <strong>Mazak</strong>, in that order. Brand names never translate.</p>
<h2 id="what-names-the-current-brand-and-what-names-the-target">What Names the Current Brand, and What Names the Target</h2>
<p>Every string on the panel and in its dialog is either the brand in force or the brand about to
replace it, and the same interpolation token means the opposite thing in two adjacent places. The
select is seeded from the snapshot's brand and re-seeded by a watch whenever the snapshot's brand
changes, so before any pick it reads the brand in force; from the moment a different option is
picked, <strong>nothing left on the panel names the brand in force</strong> — only the confirmation dialog names
both at once.</p>
<table>
<thead>
<tr>
<th>Where it is read</th>
<th>What it names</th>
</tr>
</thead>
<tbody>
<tr>
<td>The <strong>Controller brand</strong> select, before a pick</td>
<td>the brand in force, from the shared snapshot</td>
</tr>
<tr>
<td>The <strong>Controller brand</strong> select, after a pick</td>
<td>the target — the staged brand, not yet applied</td>
</tr>
<tr>
<td>The orange banner's <code>{brand}</code></td>
<td>the target</td>
</tr>
<tr>
<td>The dialog's <code>{brand}</code></td>
<td>the brand in force, or the literal word <em>current</em> when the snapshot carries no brand marker</td>
</tr>
<tr>
<td>The dialog's <code>{next}</code></td>
<td>the target</td>
</tr>
<tr>
<td>The success toast's <code>{brand}</code></td>
<td>the target</td>
</tr>
</tbody>
</table>
<p>The select is not a placeholder surface. It has no placeholder text: with a brand marker present it
shows that marker, and with none it shows only its own label. Because the select maps its value
through the option list and falls back to the raw value when no option matches, a runner whose marker
is a string outside the five — a controller file authored elsewhere, or a marker edited by hand —
displays that string verbatim in the closed select even though no option in the list can reproduce
it. Such a marker is not only cosmetic: the marker's other readers compare it against the five
tokens, so the writeback grammar falls back to its Fanuc-family form in every choice it makes — the
Fanuc variable prefix and comment spans, no keyword set, the conventional <code>F</code> position and a
parenthesized source note — and the negative-radius validation warning stays off.</p>
<h2 id="staging-and-the-two-buttons">Staging, and the Two Buttons</h2>
<p>The panel is one of the branch's two draft panels: the pick stages, it does not write.</p>
<ul>
<li><strong>Apply brand</strong> is enabled only while the staged value is non-empty <em>and</em> differs from the brand
the snapshot reports. It carries a spinner and both buttons disable while a request is in flight.</li>
<li><strong>Revert</strong> drops the staging by re-reading the snapshot's brand, and is enabled under the same
condition.</li>
</ul>
<p>Two consequences follow from that enablement rule. First, the REST action's same-brand re-flash — a
POST naming the brand already in force, which the action documents as per-case lossless — <strong>cannot be
issued from this node</strong>, because staging the current brand is not a change. Second, on a runner whose
marker is blank or outside the five options, every option counts as a change, so all five become
applicable.</p>
<p>Nothing guards a switch away from the node. The panel registers no before-switch gate, so selecting
another tree row discards the staging silently; returning to the node re-seeds the select from the
snapshot. The carry checkbox is not part of that staging and is not persisted anywhere: it is on when
the panel mounts and keeps whatever state it was left in for as long as the panel stays mounted.</p>
<h2 id="the-confirmation-flow">The Confirmation Flow</h2>
<p><strong>Apply brand</strong> opens a confirm dialog before any request is sent. Its title is <em>Switch controller
brand</em>, and it declares no button labels of its own, so both buttons come from Quasar's language
pack — which follows the app locale, and reads <strong>OK</strong> and <strong>Cancel</strong> in English. <strong>Cancel</strong> closes
the dialog and leaves the staging untouched. The body is one of two whole sentences chosen by the
carry checkbox — the two are separate strings rather than a concatenation, because the clause order
differs between languages:</p>
<ul>
<li>Unticked — <em>&quot;Replace the {brand} runner with the {next} preset? Machine settings reset to the
preset's defaults; the old brand's program-data tables are removed.&quot;</em></li>
<li>Ticked — <em>&quot;Replace the {brand} runner with the {next} preset? Machine settings reset to the
preset's defaults; the old brand's program-data tables are removed after their work-coordinate XYZ
is carried over.&quot;</em></li>
</ul>
<p>While a different brand is staged, an orange banner stands above the buttons with the same warning in
the target's voice: <em>&quot;Switching brand replaces the whole runner with the {brand} preset. Machine
settings reset to that preset's defaults and the old brand's program-data tables are removed —
switching back does not restore them.&quot;</em></p>
<p>Both sentences generalise, in opposite directions, and the exact reading is
<a href="#what-survives-and-what-is-swept">What Survives</a> below. The sweep is narrower than <em>the old brand's
program-data tables are removed</em>: the generic tool-offset table is program data and is never removed,
the retained common-variable table survives a switch among the three brands that proxy it, and
between Fanuc and Mazak the brand parameter table survives too. The reset is broader than a
cross-brand switch: the runner-owned machine entries return to the preset's defaults on <em>every</em>
apply, including the ones where nothing at all is swept.</p>
<h3 id="what-the-request-can-be-refused-with">What the request can be refused with</h3>
<p>The action refuses before it touches anything in three cases, and each reaches the same toast:</p>
<table>
<thead>
<tr>
<th>Refusal</th>
<th>Answer</th>
</tr>
</thead>
<tbody>
<tr>
<td>No project loaded</td>
<td><em>not found</em>, with the coded no-project payload — the one refusal here that a non-English locale re-renders</td>
</tr>
<tr>
<td>An NC program is playing</td>
<td><em>conflict</em>, rather than swapping the parser under a running session</td>
</tr>
<tr>
<td>A brand token outside the five</td>
<td><em>bad request</em>, naming the token it was given</td>
</tr>
</tbody>
</table>
<p>The third is unreachable from this node: the select offers only the five, and staging is only
possible for a value that differs from the marker in force, so the request always carries a token the
action's own switch recognises. It stays live for any other client of the endpoint.</p>
<p>Anything thrown after the work has begun is reported inside a success envelope instead: HTTP 200
carrying an unsuccessful flag and the exception's own message. The shared fetch helper raises both
shapes as the same kind of error, so the panel cannot tell a refusal from a failure part-way through.</p>
<h3 id="what-the-operator-sees-afterwards">What the operator sees afterwards</h3>
<p>Success raises an informational toast, <em>&quot;Controller brand switched to {brand}&quot;</em>, naming the target.
Failure raises a negative toast composed as <em>&ldquo;Switch controller brand&rdquo;</em> followed by the server's own
sentence — untranslated, because the envelope carries no code to re-render. Controller Brand is the
one panel in the branch with no local error handler at all: its failures surface through the shared
runner state's global notification, so the console line names that shared state rather than this
panel.</p>
<h2 id="what-the-switch-does">What the Switch Does</h2>
<p>The action runs four steps in a fixed order, and the order is what decides the outcome.</p>
<ol>
<li><strong>Capture.</strong> The outgoing work-coordinate provider — the first
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> in the runner's proxy-resolved list — is
asked for every coordinate id it currently holds a value for, and those id/offset pairs are held
aside. With the carry checkbox unticked the captured list is empty.</li>
<li><strong>Swap.</strong> The preset is assigned through
<a class="xref" href="../../../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SoftNcRunner">SoftNcRunner</a>, the single rewiring entry point. That
setter first hoists any brand parameter table baked directly into the outgoing runner's pipeline
list into the project's per-case list, so the incoming proxy can claim it; assigns the runner;
re-wires every proxy, the kinematics solver and the session script dictionaries; resets the
per-session runner state so the next run re-parses from scratch and clears the NC diagnostics; and
finally drops any hoisted table the incoming runner claimed through no proxy. Proxy re-wiring is
also where the new brand's own per-case tables are materialised: each get-or-create proxy installs
a deep clone of its seed only where the project holds no table of that type.</li>
<li><strong>Carry.</strong> The held offsets are written into the <em>new</em> provider, and only for ids that provider
also exposes. The step is skipped entirely when the new provider is the same object as the old
one, which is exactly the Fanuc-to-Mazak case.</li>
<li><strong>Sweep.</strong> Every per-case table the new runner resolves through no proxy is removed from the
project's per-case list, so the previous brand's tables do not linger unread.</li>
</ol>
<h2 id="what-is-carried">What Is Carried</h2>
<p>The carry moves <strong>only the work-coordinate XYZ offsets</strong>. No other table's values cross, and nothing
else on either plane is copied.</p>
<p>The accepted set is the intersection of what the outgoing provider currently holds with what the
incoming provider exposes, and the four providers do not expose the same ids. What each one allocates
by default:</p>
<table>
<thead>
<tr>
<th>Provider</th>
<th>Brands</th>
<th>Coordinate ids it exposes</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a></td>
<td>Fanuc, Mazak</td>
<td>G54–G59 and G54.1 P1–P48, all seeded</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a></td>
<td>Syntec</td>
<td>the same set, through the same address map — spelled <code>Pr</code> rather than <code>#</code></td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a></td>
<td>Siemens</td>
<td>G54–G57, plus the extended series G505–G599</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a></td>
<td>Heidenhain</td>
<td>G54–G59, aliased onto preset rows 1–6</td>
</tr>
</tbody>
</table>
<p>So the carry is lossy in ways the checkbox's caption does not say. A switch out of Fanuc, Syntec or
Mazak into Siemens keeps G54 through G57 and drops G58, G59 and all forty-eight G54.1 P offsets.
The reverse switch keeps G54 through G57 and drops the whole G505–G599 extended series. Into
Heidenhain from one of those three, G54 through G59 cross and the G54.1 P offsets do not; from
Siemens, only G54 through G57 have anywhere to land. Between Fanuc, Syntec and Mazak the two id sets
match, so nothing is lost.</p>
<p>Whatever is dropped is dropped silently: an id the target does not expose is skipped without a
message, and the table that held it is removed by the sweep in the same request.</p>
<h2 id="what-survives-and-what-is-swept">What Survives and What Is Swept</h2>
<p>The sweep's keep set is the new runner's proxy-resolved list, so a per-case table survives exactly
when the target brand's preset proxies its type.</p>
<table>
<thead>
<tr>
<th>Per-case table</th>
<th>Proxied by</th>
<th>Behaviour on a switch</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTable.html">ToolOffsetTable</a></td>
<td>all five</td>
<td>survives every switch</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.RetainedCommonVariableTable.html">RetainedCommonVariableTable</a></td>
<td>Fanuc, Syntec, Mazak</td>
<td>survives among those three; swept into Siemens or Heidenhain</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a></td>
<td>Fanuc, Mazak</td>
<td>survives between those two; swept otherwise</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a></td>
<td>Syntec</td>
<td>swept on leaving Syntec</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html">SiemensMachineDataTable</a></td>
<td>Siemens</td>
<td>swept on leaving Siemens</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a></td>
<td>Siemens</td>
<td>swept on leaving Siemens</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensRParameterTable.html">SiemensRParameterTable</a></td>
<td>Siemens</td>
<td>swept on leaving Siemens</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a></td>
<td>Siemens</td>
<td>swept on leaving Siemens, tool-name map included</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a></td>
<td>Heidenhain</td>
<td>swept on leaving Heidenhain</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Heidenhain.HeidenhainParameterTable.html">HeidenhainParameterTable</a></td>
<td>Heidenhain</td>
<td>swept on leaving Heidenhain</td>
</tr>
<tr>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Heidenhain.HeidenhainQParameterTable.html">HeidenhainQParameterTable</a></td>
<td>Heidenhain</td>
<td>swept on leaving Heidenhain, and exposed by no node or endpoint in the branch</td>
</tr>
</tbody>
</table>
<p>Switching back does not restore a swept table. The returning proxy finds nothing of its type on the
project and clones a fresh table from its seed, so the rows come back at the preset's defaults and
the previous values are gone. The tool-name map is worth naming separately because it is not a table
of its own: it lives on the Siemens tool-offset table and leaves with it.</p>
<p>Beside the per-case tables, the preset's <strong>plain</strong> entries are replaced outright on every apply,
whatever the target brand. Those are the runner-owned machine settings, and they reset to that
preset's defaults even in the Fanuc-to-Mazak case where nothing at all is swept:</p>
<ul>
<li><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolingMcConfig.html">ToolingMcConfig</a> — the tool-change position, back to its
three-axis default, where X and Y are left unset and Z returns to zero.</li>
<li><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.GenericBlockSkipConfig.html">GenericBlockSkipConfig</a> — the block-skip layers, back to
layer 1 alone. The Heidenhain preset carries no block-skip config at all, so the node itself is
gone after a switch to that brand.</li>
<li><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.SubProgramFolderConfig.html">SubProgramFolderConfig</a> — the subprogram lookup folders,
back to an internal folder of <code>NC</code> and no external folder.</li>
<li>The macro iteration guards, back to the target preset's own guard set.</li>
</ul>
<p>The Object-Management Load and Paste on the branch root install a runner through the same setter and
the same sweep, so the same table survives or is lost by the same rule — but that path has no carry
step and no confirmation of its own.</p>
<h2 id="what-the-tree-does-afterwards">What the Tree Does Afterwards</h2>
<p>The panel derives its rebuild scope from its own id by stripping the fixed <code>/machine/brand</code> suffix,
so the scope is the branch root rather than this leaf, and it reports the structural change only on a
successful switch. The Control-Tree host then rebuilds that branch and adds the root to the expansion
list so the fresh children are visible. The rebuild makes its <strong>own</strong> request for the runner snapshot,
independent of the shared one the panels gate on.</p>
<p>The shared snapshot is re-read either way, in the call's finally block, together with the runner's
index key. Three things follow:</p>
<ul>
<li><strong>The selection does not move.</strong> Controller Brand exists on every brand, and the editor row's
remount key is composed from the node id and the node key, both unchanged — so the panel is not
remounted. The select is re-seeded by its watch rather than by a fresh mount, which is why the
staged value clears after a success and stays put after a failure that left the brand alone.</li>
<li><strong>A failure that reached the server after the swap leaves the tree describing the previous shape.</strong>
The regrow is skipped on failure, but the snapshot re-read is not; the select and the root's brand
badge then show whatever brand the server now reports, over a branch built before the attempt. The
staging clears with the brand, so <strong>Apply brand</strong> is disabled and the branch cannot be regrown from
this node. Moving the selection does not regrow it either — only a structural change reported on
that branch, a whole-tree rebuild, or the page rebuild a project change forces will bring the two
back into agreement.</li>
<li><strong>A failed snapshot re-read empties the branch's panels.</strong> That read resets the shared snapshot to
its empty value and raises its own toast, <em>&ldquo;Load controller settings&rdquo;</em> followed by the server's
message; every leaf panel then renders the shared no-runner line even where the tree still lists
the leaves.</li>
</ul>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>Controller Brand</strong> Node Row — <code>equipment/controller/machine/brand</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — <strong>Controller Brand</strong> Panel
<ul>
<li>Shared Empty Line — <em>&quot;No NC runner — load a project first.&quot;</em>, in place of everything below it
while the shared snapshot reports no runner</li>
<li><strong>Controller brand</strong> Select — dense, outlined; five options in list order, <strong>Fanuc</strong>,
<strong>Siemens</strong>, <strong>Heidenhain</strong>, <strong>Syntec</strong>, <strong>Mazak</strong>; no clear button and no search field</li>
<li><strong>Carry work-coordinate XYZ (G54…) into the new brand's table</strong> CheckBox — dense, ticked when the
panel mounts</li>
<li>Warning Banner — orange, dense, rounded; present only while a different brand is staged</li>
<li>Button Row
<ul>
<li><strong>Apply brand</strong> Button — primary, disabled unless a different brand is staged, spinner while
the request is in flight</li>
<li><strong>Revert</strong> Button — flat, same enablement</li>
</ul>
</li>
</ul>
</li>
<li>Confirm Dialog — title <strong>Switch controller brand</strong>, one of the two message variants, <strong>Cancel</strong> and
<strong>OK</strong></li>
</ul>
<p>No controller item type registers a large content view, so the General Setup content column shows its
own empty hint while this node is selected.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcBrandPanel.vue</code> — this panel: the staged brand and
its watch, the carry checkbox and its default, the dirty rule behind both buttons, the confirm
dialog and its two message variants, and the rebuild scope derived by stripping the leaf's own id
suffix.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line this
panel renders in place of its body.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the select is seeded from,
the brand-switch call with its success and failure toasts, and the snapshot and key re-read that
runs whichever way the call ends.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the five-brand option constant, the snapshot shape, and the
typed brand-switch request carrying the target and the carry flag.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — why a refusal and a part-way failure arrive as the same kind of
error, and why only a coded payload is re-rendered in the app locale.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — this node's id, label and label key,
and the branch builder that regrows the whole child set from a fresh snapshot after the switch.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — the branch root the scope resolves
to, and the rebuild that expands it and re-resolves the selection.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcRunnerRootPanel.vue</code> — the brand badge and runner-file
caption that change with the snapshot, and the Object-Management install that replaces the runner
by the other route.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the tree label, the select label, the checkbox caption, the
banner sentence, the button captions and the two toast contexts.</li>
<li><code>wwwroot-src/src/i18n/en/dialog.ts</code> — the dialog title, the two message variants and the <em>current</em>
fallback used when the snapshot carries no brand marker.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the brand-switch action: its refusals, the four ordered steps,
the sweep of per-case tables the new runner references through no proxy, and the runner-install
action that shares that sweep.</li>
<li><code>Common/ApiError.cs</code> — the coded no-project payload, the one refusal here a non-English locale can
re-render.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets as literal dependency lists, each
property returning a fresh instance, together with the segmenter, initializers and syntax list each
brand brings; and the proxy resolution every read on this branch goes through.</li>
<li><code>HiMech/NcParsers/NcRunnerSuit.cs</code> — the suit that holds the runner beside the project's own
per-case dependency list, and the proxy wiring the swap triggers.</li>
<li><code>HiMech/NcParsers/ControllerPresetWriter.cs</code> — the brand token list and preset factory that mirror
the REST action's switch, and the controller-file extension a saved runner takes.</li>
<li><code>HiMech/NcParsers/Dependencys/CncBrandDependency.cs</code> — the brand marker itself: five tokens and
one free-form string property, with no behaviour of its own; its readers live elsewhere.</li>
<li><code>HiMech/NcOpt/SoftNcOptProc.cs</code> — where NC optimization reads the marker back out of the session's
effective dependency list, for the writeback grammar.</li>
<li><code>HiMech/NcOpt/NcOptPieceClassifier.cs</code> — the re-interpolation guards, none of them keyed on the
brand; the arc guard keys on the centre's provenance instead.</li>
<li><code>HiMech/NcParsers/Keywords/Generic/ArcCenterSource.cs</code> — the two centre-provenance stamps: the one
the arc guard refuses, and the one that tells the writeback a split arc's fragments share the <code>CC</code>
line unchanged. An arc whose own block states its centre carries neither.</li>
<li><code>HiMech/NcParsers/NcWriteback/NcPatchWriter.cs</code> — the writeback grammar the marker selects: the
variable prefix, the comment spans, the Heidenhain keyword set, the pre-feed word patterns that
place an inserted <code>F</code>, and the trailing-comment shape the embedded source note takes.</li>
<li><code>HiMech/NcParsers/PostLogicSyntaxs/RadiusCompensationSyntax.cs</code> — the negative-radius validation
warning raised only where the marker reads Heidenhain, in a syntax all five presets carry.</li>
<li><code>HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs</code> — the work-coordinate provider contract the
carry reads and writes through, including the id enumeration that decides what crosses.</li>
<li><code>HiMech/NcParsers/Dependencys/IsoCoordinateAddressMap.cs</code> — the G54–G59 and G54.1 P1–P48 id set the
Fanuc-family and Syntec tables expose, and the seeding that makes every one of them present.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTable.cs</code> — the frame table's own allocation,
G54–G57 plus the extended G505–G599 series, which is what a carry into Siemens accepts.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs</code> — the datum table's preset rows
and the six of them aliased onto G54–G59.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the get-or-create proxy shape
the brand tables share: a serialized seed on the runner, a deep clone installed only where the
project holds no table of that type, and a take thereafter.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTableProxy.cs</code> — the proxy every preset carries,
and therefore the reason the generic tool-offset table survives every switch.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolingMcConfig.cs</code> — the tool-change position's preset
default, one of the runner-owned values every apply resets.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/GenericBlockSkipConfig.cs</code> — the block-skip layers' preset
default, and the config the Heidenhain preset does not carry.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/SubProgramFolderConfig.cs</code> — the subprogram folders' preset
defaults.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the facade setter the swap assigns through: the
hoist of pre-proxy baked tables, the proxy and kinematics re-wiring, the per-session runner state
reset, and the drop of hoisted tables no proxy claimed.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane this leaf opens, and the storage column that says
which of its other leaves this control resets</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — which nodes each brand grows, so what the branch looks like
after the switch is a lookup rather than a surprise</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the commit and error rules the rest of the branch
follows, and which this panel departs from on purpose</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,489 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Machine and Controller Plane | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Machine and Controller Plane | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerMachine">
<h1 id="machine-and-controller-plane">Machine and Controller Plane</h1>
<p>Machine / Controller is the first of the Controller branch's two plane stems: what describes the
machine and the control that drives it, edited one leaf at a time. It lives on the General Setup
page at <code>/general-setup</code> under the Control-Tree id <code>equipment/controller/machine</code>, and each of its
leaves takes that id plus one segment — <code>brand</code>, <code>limits</code>, <code>rapid</code>, <code>home</code>, <code>tool-change</code>,
<code>parameters</code>, <code>m-codes</code>, <code>canned-cycle</code>, <code>block-skip</code>, <code>subprograms</code>, <code>indexing-positions</code>,
<code>native</code>. The stem is not gated: it is part of the branch builder's return value rather than a
conditional entry, so it appears wherever a runner resolves and always carries the leaves no flag
guards.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<p>Selecting the stem shows the shared group editor: its own introduction over a clickable list of its
children. The introduction reads <em>&quot;Machine and controller presets that travel with the runner file —
brand, travel limits, rapid rates, home / G28 reference, the tool-change position and the controller
parameters (in both a domain-grouped interface form and the native parameter form).&quot;</em></p>
<h2 id="what-travels-with-the-runner-file-means">What &ldquo;Travels With the Runner File&rdquo; Means</h2>
<p>The phrase names an <strong>ownership</strong> boundary, not a file on disk, and the boundary does not fall where
the introduction's list suggests. Half of what this plane edits is stored on the project.</p>
<h3 id="the-suits-two-members">The suit's two members</h3>
<p><a class="xref" href="../../../../api/Hi.NcParsers.NcRunnerSuit.html">NcRunnerSuit</a> carries the runner, <a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html">SoftNcRunner</a>, and the
project's own <a class="xref" href="../../../../api/Hi.NcParsers.NcRunnerSuit.html#Hi_NcParsers_NcRunnerSuit_PerCaseNcDependencyList">PerCaseNcDependencyList</a>, and serializes the two side
by side. The runner half honours <a class="xref" href="../../../../api/Hi.NcParsers.NcRunnerSuit.html#Hi_NcParsers_NcRunnerSuit_SoftNcRunnerFile">SoftNcRunnerFile</a>: with a
project-relative path recorded there the runner XML is written to that side file and referenced from
the project; with none recorded it is inlined in the project file. The per-case half is always
inlined — the suit writes it as a nested element with no file reference of its own.</p>
<p>The web service reads that recorded path and never writes one. On a project that has never been
given a side file, both halves therefore sit inside the same project file, in two slots whose
contents behave differently the moment a runner is swapped. A controller file is one serialized
runner. Its canonical extension is <code>.Controller</code>
(<a class="xref" href="../../../../api/Hi.NcParsers.ControllerPresetWriter.html#Hi_NcParsers_ControllerPresetWriter_FileExtension">FileExtension</a>), and the branch root's Load browser
accepts the earlier <code>.SoftNcRunner</code> spelling beside it.</p>
<h3 id="which-half-a-value-lands-in">Which half a value lands in</h3>
<p>A runner's <a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_PipelineNcDependencyList">PipelineNcDependencyList</a> holds two shapes of entry.</p>
<p>A <strong>plain dependency</strong> is the runner's own. Its edited values serialize with the runner, and
installing another runner replaces the instance outright.</p>
<p>A <strong>proxy</strong> — <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.INcDependencyProxy.html">INcDependencyProxy</a> — carries no live data. Each brand
parameter-table proxy holds a fixed machine-config seed and serializes only that seed;
<a class="xref" href="../../../../api/Hi.NcParsers.NcRunnerSuit.html#Hi_NcParsers_NcRunnerSuit_WireNcDependencyProxies_">WireNcDependencyProxies</a><small>(API)</small> deep-clones the seed
into the project's per-case list only when the project holds no table of that type, and an existing
table wins. Reads and writes then resolve through
<a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_GetEffectiveNcDependencyList_">GetEffectiveNcDependencyList</a><small>(API)</small>, which
substitutes the project's table for the placeholder. All four brand tables' proxies are written to
the same shape, and the seed is set once at construction and never rewritten by an edit.</p>
<p>Two consequences follow, and they are what the introduction's phrasing hides:</p>
<ul>
<li>The per-axis rows, the M-code declarations and the native parameter rows are stored on the
<strong>project</strong>, because they are all rows of the brand parameter table. Installing a same-brand
runner file leaves them where they are, apart from the rotary reference and rapid rows that any
re-bind of the suit re-stamps.</li>
<li>Saving a runner file captures the proxy's seed, not the table the panels have been editing. A
<code>.Controller</code> written after the axis tables were tuned carries the preset seed those tables were
cloned from.</li>
</ul>
<p>Why the table is per-case at all splits by brand. The Fanuc-family and Syntec tables mix machine
configuration with the per-case work-coordinate offsets the program-data plane edits, which the two
planes could not otherwise separate. The Siemens machine-data and Heidenhain tables carry no work
coordinates — those two brands keep their offsets in a separate per-case table — and are per-case
for the other half of the same reason: the project's own table wins over the seed, so machine data
and machine parameters already edited survive a same-brand runner install instead of resetting to
the preset's.</p>
<h2 id="the-planes-leaves">The Plane's Leaves</h2>
<p>Ordered as the branch builder pushes them: the ungated leaves first, then each gated leaf behind the
snapshot flag that grows it. Which brands satisfy each flag is
<a class="xref" href="../brand-matrix.html">Brand Matrix</a>; the column that page does not carry is the last
one here.</p>
<table>
<thead>
<tr>
<th>Node segment</th>
<th>Label the tree shows</th>
<th>Gate</th>
<th>What backs it</th>
<th>Stored on</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>brand</code></td>
<td>Controller Brand</td>
<td>ungated</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.CncBrandDependency.html">CncBrandDependency</a></td>
<td>runner</td>
</tr>
<tr>
<td><code>limits</code></td>
<td>Machine Limits (Stroke)</td>
<td>ungated</td>
<td>the brand parameter table's stroke-limit rows</td>
<td>project</td>
</tr>
<tr>
<td><code>rapid</code></td>
<td>Rapid Feedrates</td>
<td>ungated</td>
<td>the brand parameter table's rapid-rate rows</td>
<td>project</td>
</tr>
<tr>
<td><code>home</code></td>
<td>Home / G28 Reference</td>
<td>ungated</td>
<td>the brand parameter table's reference-position rows</td>
<td>project</td>
</tr>
<tr>
<td><code>tool-change</code></td>
<td>Tool-Change Position</td>
<td>ungated</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolingMcConfig.html">ToolingMcConfig</a></td>
<td>runner</td>
</tr>
<tr>
<td><code>parameters</code></td>
<td>Controller Parameters</td>
<td>ungated</td>
<td>the brand parameter table, plus the runner's iteration guards</td>
<td>split</td>
</tr>
<tr>
<td><code>m-codes</code></td>
<td>M-Code Declarations</td>
<td><code>hasNativeTable</code></td>
<td>the brand parameter table's declaration map and tool-change trigger</td>
<td>project</td>
</tr>
<tr>
<td><code>canned-cycle</code></td>
<td>Canned Cycle (Peck)</td>
<td><code>hasCannedCycle</code></td>
<td>the brand parameter table, or <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.FallbackConfig.html">FallbackConfig</a></td>
<td>brand-dependent</td>
</tr>
<tr>
<td><code>block-skip</code></td>
<td>Block Skip / Delete</td>
<td><code>hasBlockSkip</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.GenericBlockSkipConfig.html">GenericBlockSkipConfig</a></td>
<td>runner</td>
</tr>
<tr>
<td><code>subprograms</code></td>
<td>Subprogram Folders</td>
<td><code>hasSubprogramFolders</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.SubProgramFolderConfig.html">SubProgramFolderConfig</a></td>
<td>runner</td>
</tr>
<tr>
<td><code>indexing-positions</code></td>
<td>Indexing Position Tables</td>
<td><code>hasIndexingTables</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html">SiemensMachineDataTable</a></td>
<td>project</td>
</tr>
<tr>
<td><code>native</code></td>
<td>Parameters (Native)</td>
<td><code>hasNativeTable</code></td>
<td>the brand parameter table, as its raw dictionaries</td>
<td>project</td>
</tr>
</tbody>
</table>
<p>Every label above is a translated role string rather than a type name, so the tree renders the
translation and a locale change rewrites the rows without touching an id.</p>
<p>One flag grows two of these leaves: <code>hasNativeTable</code> gates both M-Code Declarations and Parameters
(Native), because the declarations live on the same table the native form exposes.</p>
<h3 id="two-leaves-that-straddle-the-boundary">Two leaves that straddle the boundary</h3>
<p><strong>Controller Parameters</strong> is the one leaf whose fields do not share a storage half. Max spindle
speed, the cutter-compensation startup type and the tool-axis direction are written onto the brand
parameter table, so they are project-owned; the macro loop guards under <em>Macro loop guards
(advanced)</em> are written onto the runner's own iteration-guard dependencies, so they are
runner-owned. The panel gates its whole body on the parameter table's presence, which is the
project-owned half.</p>
<p><strong>Canned Cycle (Peck)</strong> is the one leaf whose storage half depends on the brand. Where the brand
table supplies the clearance it is a row of that table and project-owned; where the preset supplies
it through the generic fallback config it is a plain runner entry and runner-owned. The read reports
which of the two answered, and the panel names the source in the caption under the field.</p>
<h2 id="what-an-install-or-a-brand-switch-does">What an Install or a Brand Switch Does</h2>
<p>Object-Management Load, Paste and XML apply all install a runner through
<a class="xref" href="../../../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SoftNcRunner">SoftNcRunner</a>, and a brand switch assigns the brand's
preset through the same setter. Both are followed by the same sweep, and for this plane the outcome
splits exactly along the storage column.</p>
<p>The <strong>runner-owned</strong> leaves take the incoming runner's values, so a brand switch returns the
tool-change position, the block-skip layers, the subprogram folders and the macro guards to the new
preset's defaults. Installing a controller file of the brand already in force replaces those same
four with the file's values while the project keeps its own parameter table — the same-brand
re-flash the REST surface's own remark calls per-case lossless, naming those four as what resets.
That re-flash is not reachable from the brand select, whose apply is enabled only while the staged
brand differs from the active one (<a class="xref" href="brand-switch.html">Brand Switch</a>); Load, Paste
and XML apply are the surfaces that issue it.</p>
<p>The <strong>project-owned</strong> leaves keep the project's table: the incoming proxy re-binds to it rather than
cloning its seed over it. Before the swap, any brand parameter table baked directly into the
outgoing runner's pipeline list — the storage a project file saved before that table went per-case
still uses — is moved into the per-case list so the incoming proxy can claim it, and a moved table
no proxy claims is dropped again afterwards. The chain walk that follows the re-bind is the only
part of the swap that writes into the surviving table: it stamps the chain's axes onto it, and with
them every rotary axis's reference position and rapid rate.</p>
<p>A switch to a <strong>different</strong> brand ends with the per-case tables the new runner resolves through no
proxy removed, so the previous brand's parameter table does not linger unread. Switching back does
not restore it; a fresh table is cloned from the new preset's seed instead.</p>
<p>Both writes are refused while an NC program is playing, and each answers with a conflict rather than
swapping the parser under a running session.</p>
<h2 id="one-table-two-forms">One Table, Two Forms</h2>
<p>Machine Limits, Rapid Feedrates, Home / G28 Reference, M-Code Declarations, most of Controller
Parameters and all of Parameters (Native) read and write one object: the brand parameter table.
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a> declares the role accessors — home
reference, axis set, rapid feedrates, stroke limits, spindle control, M-code declarations, the
tool-change trigger — and each brand subclass maps those roles onto its own parameter numbers. The
domain-grouped leaves call the accessors; the native leaf writes the raw dictionary cell under the
number. Editing one form changes what the other shows.</p>
<p>Where a brand stores a role in its own unit the two forms differ by that unit, and the peck
clearance is the case that ships: the Syntec table stores it in microns and its accessor converts,
while the Fanuc-family table stores millimetres directly. The interface leaf therefore shows the
same number on every brand and the native leaf shows the brand's raw stored value.</p>
<p>What a freshly switched brand shows is decided by that brand's default table. Every one of them
declares a linear X, Y and Z with a reference position of zero and a rapid rate per axis, so Rapid
Feedrates and Home / G28 Reference open populated. None of them declares a stroke limit, so Machine
Limits opens with empty cells on every brand; and only the Siemens default table pre-declares any
M-codes, so M-Code Declarations opens empty on the other four brands, Mazak included, because it
carries the Fanuc table. An axis with no rapid-rate row of its own reads a fixed default rather than
a blank cell.</p>
<p>The axis rows those leaves list are the parameter table's own axis entries. Whenever the suit or the
machine chain is re-bound, a chain walk stamps the chain's axis codes onto that table: it adds axes
and removes none, and a linear axis keeps what the table already holds, gaining a reference position
only where it had none. A rotary axis is the exception — every walk rewrites its reference position
to 0 deg and its rapid rate to 36000 deg/min, so a tuned rotary value does not survive the next
stamp. The chain itself is edited on <a class="xref" href="../../machine-tool.html">Machine Tool</a>.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row — <code>equipment/controller/machine</code>
<ul>
<li><strong>Controller Brand</strong> Node Row</li>
<li><strong>Machine Limits (Stroke)</strong> Node Row</li>
<li><strong>Rapid Feedrates</strong> Node Row</li>
<li><strong>Home / G28 Reference</strong> Node Row</li>
<li><strong>Tool-Change Position</strong> Node Row</li>
<li><strong>Controller Parameters</strong> Node Row</li>
<li><strong>M-Code Declarations</strong> Node Row — grown while the snapshot reports a brand parameter table</li>
<li><strong>Canned Cycle (Peck)</strong> Node Row — grown while it reports a peck-clearance provider</li>
<li><strong>Block Skip / Delete</strong> Node Row — grown while it reports a block-skip config</li>
<li><strong>Subprogram Folders</strong> Node Row — grown while it reports a subprogram-folder config</li>
<li><strong>Indexing Position Tables</strong> Node Row — grown while the brand table is the Siemens
machine-data table</li>
<li><strong>Parameters (Native)</strong> Node Row — grown on the same flag as <strong>M-Code Declarations</strong></li>
</ul>
</li>
<li><strong>Program Data</strong> Group Row — the plane beside this one</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Machine / Controller</strong> Group Panel
<ul>
<li>Intro Caption — the stem's introduction line</li>
<li>Child List — one bordered, separated row per leaf above, each showing the leaf's label in the
theme's primary colour with a right chevron; a click selects that leaf</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The stem's own editor holds no field, so nothing on this plane is edited from the group row itself.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: the machine
stem's id, label key and intro key, the leaves it pushes unconditionally, and the snapshot flag
each further leaf is pushed behind, in the order the table above lists them.</li>
<li><code>wwwroot-src/src/components/controlTree/GroupInfoPanel.vue</code> — the stem's editor: the intro caption
and the clickable child list that selects a leaf.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — builds the <code>equipment/controller</code>
root this stem hangs from, inside the equipment group.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the snapshot shape the builder reads its flags from, and
the typed wrappers over each leaf's reader and writers.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the leaf panels gate on, and
the install and brand-switch calls that regrow the branch.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the stem's introduction line and every leaf label in the
table above.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the plane's REST surface: the presence flags, the per-group
readers and writers, the runner install, and the brand switch with its carry and its sweep of
per-case tables the new runner references through no proxy.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/NcRunnerSuit.cs</code> — the suit: the runner, its optional side-file path, the
per-case list, the flat serialization that inlines the per-case half and file-references the
runner half, and the proxy wiring.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the pipeline list this plane's dependencies sit in, the five
brand presets that decide which of them a brand carries, the proxy resolution every read goes
through, and the chain configuration that stamps axis codes onto the table.</li>
<li><code>HiMech/NcParsers/ControllerPresetWriter.cs</code> — the canonical controller-file extension and the
writer that renders one brand preset as a standalone runner file.</li>
<li><code>HiMech/NcParsers/Dependencys/INcDependencyProxy.cs</code> — the maker-and-taker contract: what a proxy
may serialize, and why its resolved data is never written into the runner.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the get-or-create proxy behind
the Fanuc-family table the Fanuc and Mazak presets share, and the shape the other three brand
tables each repeat in a proxy class of their own: a serialized seed on the runner, a deep clone
installed into the project only when the project has no table of that type, and a take thereafter.</li>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the role accessors the
domain-grouped leaves call, the per-brand parameter numbers they map onto, and the raw
dictionaries the native leaf edits.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTable.cs</code>,
<code>HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTable.cs</code>,
<code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code>,
<code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs</code> — the four brand tables and
their default seeds: the axis rows, the rapid rates, the peck clearance and its unit, and the only
pre-declared M-codes on the plane.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolingMcConfig.cs</code>,
<code>HiMech/NcParsers/Dependencys/Generic/GenericBlockSkipConfig.cs</code>,
<code>HiMech/NcParsers/Dependencys/Generic/SubProgramFolderConfig.cs</code>,
<code>HiMech/NcParsers/Dependencys/Generic/FallbackConfig.cs</code> — the plain runner-owned dependencies
behind the tool-change, block-skip, subprogram and fallback-clearance leaves, and the values each
one serializes with the runner.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucGotoIterationDependency.cs</code> — a macro guard, the
runner-owned half of the Controller Parameters leaf.</li>
<li><code>HiMech/NcParsers/Dependencys/CncBrandDependency.cs</code> — the brand marker the first leaf edits.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the facade setter every install and brand switch
assigns through: the hoist of pre-proxy baked tables, the drop of the ones no proxy claims, the
proxy re-binding and the chain-driven axis stamp.</li>
<li><code>HiGeom/Common/XmlUtils/XmlUtil.cs</code> — the file-reference helper that decides whether the runner is
written to its side file or inlined.</li>
</ul>
<h2 id="pages">Pages</h2>
<p>Ordered by the first node each page owns, as the plane lists them.</p>
<ul>
<li><a class="xref" href="brand-switch.html">Controller Brand</a> — The brand select and its staged apply,
and what a brand change carries, resets and destroys</li>
<li><a class="xref" href="per-axis-tables.html">Per-Axis Tables</a> — The four leaves whose rows follow the
machine chain: stroke limits, rapid rates, the G28 reference and the tool-change position</li>
<li><a class="xref" href="interface-parameters.html">Interface Parameters</a> — The domain-grouped parameter form and
the peck-clearance leaf beside it, including the fields whose storage half differs</li>
<li><a class="xref" href="m-code-declarations.html">M-Code Declarations</a> — What a declared machine M-code consumes, the
tool-change trigger mode, and the codes an undeclared M word still warns about</li>
<li><a class="xref" href="program-reading.html">Program Reading</a> — The two leaves that decide how a program
is read rather than how the machine moves: block-skip layers and the subprogram lookup folders</li>
<li><a class="xref" href="indexing-positions.html">Indexing Positions</a> — The coded-position tables and the
per-axis assignment that decides which axis reads which</li>
<li><a class="xref" href="native-parameters.html">Native Parameters</a> — The raw parameter form: brand
numbering, raw stored units, and the same table every other leaf on this plane edits</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../index.html">Controller Branch</a> — the branch this plane is half of, its runner root and the program-data
plane beside it</li>
<li><a class="xref" href="../program-data/index.html">Program Data Plane</a> — that plane itself, whose leaves are project-owned throughout
where this one's ownership splits between the runner file and the project</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — which brand satisfies each gate named above, and what each
flag actually probes</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the commit, rollback and rendering rules every leaf on
this plane inherits, and the panels that depart from them</li>
<li><a class="xref" href="brand-switch.html">Controller Brand</a> — the control that replaces the runner, and therefore
everything in the runner-owned column here</li>
<li><a class="xref" href="per-axis-tables.html">Per-Axis Tables</a> — the per-axis leaves whose rows come from the machine chain
and whose values are stored on the project</li>
<li><a class="xref" href="interface-parameters.html">Interface Parameters</a> — the interface parameter form and the peck-clearance leaf, the
two entries that straddle the storage boundary</li>
<li><a class="xref" href="m-code-declarations.html">M-Code Declarations</a> — the declaration map on the same table the per-axis leaves edit</li>
<li><a class="xref" href="program-reading.html">Program Reading</a> — the block-skip and subprogram leaves, the two settings
that change how a program is read rather than how the machine moves</li>
<li><a class="xref" href="indexing-positions.html">Indexing Position Tables</a> — the leaf that grows only where the brand table is the
Siemens machine-data table</li>
<li><a class="xref" href="native-parameters.html">Native Parameters</a> — the raw form of the table behind most of this plane</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,448 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Indexing Position Tables | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Indexing Position Tables | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerIndexingPositions">
<h1 id="indexing-position-tables">Indexing Position Tables</h1>
<p>Indexing Position Tables is the machine plane's single-brand leaf: the two global position lists a
Sinumerik control resolves coded-position words against, edited as drafts and saved whole. It lives
on the General Setup page at <code>/general-setup</code> under the Control-Tree id
<code>equipment/controller/machine/indexing-positions</code>, and of the five controller brands only Siemens
grows it. The tables it edits are machine data of the Siemens machine-data table; the assignment
that decides which axis reads which of them is shown here and edited on the sibling leaf,
<a class="xref" href="native-parameters.html">Parameters (Native)</a>.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<h2 id="the-one-single-brand-node">The One Single-Brand Node</h2>
<p>The branch builder pushes this leaf behind the <code>hasIndexingTables</code> snapshot flag, and that flag is a
<strong>pure type probe</strong>: it reports whether the runner's brand parameter table <em>is</em> the
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html">SiemensMachineDataTable</a>. Nothing about the tables' contents
is examined, so the node grows on every Siemens runner whether or not a single position has been
entered. Only the Siemens preset carries that table, which is why this leaf is the machine plane's
one single-brand row in the <a class="xref" href="../brand-matrix.html">Brand Matrix</a>; the program-data
plane beside it has several.</p>
<p>The flag and the panel's own read do not ask quite the same question. The flag inspects the <strong>first</strong>
brand parameter table in the resolved dependency list; the panel's reader looks for a Siemens
machine-data table <strong>anywhere</strong> in it. The two agree on every shipped preset, where the Siemens
machine-data table is the only parameter table present.</p>
<p>That table is also the sole implementer of
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIndexingPositionConfig.html">IIndexingPositionConfig</a> in the engine, so nothing else in a runner
can supply an indexing table and no other brand can grow this node by carrying a different type.</p>
<h2 id="what-the-two-tables-are">What the Two Tables Are</h2>
<p>The leaf edits <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensMachineDataTable_IndexAxPosTab1">IndexAxPosTab1</a> and
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensMachineDataTable_IndexAxPosTab2">IndexAxPosTab2</a> — the general machine
data MD10910 and MD10930. They describe axes that take up discrete stations rather than arbitrary
positions: Hirth-coupled tables, indexing rotaries, turret-style workholders.</p>
<ul>
<li><strong>They are global, not per-axis.</strong> Each is one ordered list shared by every axis assigned to it, so
an edit to Table 1 changes every axis whose assignment reads 1.</li>
<li><strong>Row order is the position number, and numbering is 1-based.</strong> Row 1 is indexing position 1. The
used-length machine data beside each table — MD10900 and MD10920 — is not stored: the list count
<em>is</em> the used length, which is why the panel has no length field.</li>
<li><strong>Values are in the axis' own native units</strong> — degrees on a rotary axis, millimetres on a linear
one — and the panel prints no unit suffix on the cells, because the unit follows whichever axes
read the table.</li>
<li><strong>Values must be strictly ascending</strong>, and where a modular rotary axis reads the table they must
also satisfy <code>0 ≤ position &lt; 360</code>.</li>
<li><strong>At most 60 entries per table</strong>, the MD10910 / MD10930 array size. Finer station counts are what
the equidistant definition below exists for.</li>
</ul>
<h3 id="what-consumes-them">What consumes them</h3>
<p>The tables are read at the write stage of the parsing pipeline, through
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIndexingPositionConfig.html#Hi_NcParsers_Dependencys_IIndexingPositionConfig_TryGetIndexingPosition_">TryGetIndexingPosition</a><small>(API)</small>
and <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIndexingPositionConfig.html#Hi_NcParsers_Dependencys_IIndexingPositionConfig_TryFindIndexingAnchor_">TryFindIndexingAnchor</a><small>(API)</small>,
for the Siemens coded-position coordinate functions <code>CAC()</code>, <code>CIC()</code>, <code>CDC()</code>, <code>CACP()</code> and <code>CACN()</code>
— the family whose argument is a position <strong>number</strong> rather than a coordinate. The Siemens
per-word coordinate syntax unwraps those verbs only on axes that
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIndexingPositionConfig.html#Hi_NcParsers_Dependencys_IIndexingPositionConfig_IsIndexingAxis_">IsIndexingAxis</a><small>(API)</small> reports
as usable, and the rotary-only members of the family additionally require a rotary axis; the rotary
words are then resolved by the ABC write pass and the linear words by the incremental-resolve pass,
both through the shared coded-position helper.</p>
<p><strong>An empty table is a real state with a visible consequence.</strong> An axis assigned to a table that holds
no entries is not a usable indexing axis, so its coded-position words are left unrecognised and
surface as unresolved text rather than being resolved against nothing. The engine lookups themselves
deliberately do not validate the Siemens constraints — a table that breaks them resolves nonsense
positions with no diagnostic — which is why the write endpoint, and this panel with it, is where the
rules are enforced.</p>
<h2 id="machine-data-not-program-data">Machine Data, Not Program Data</h2>
<p>The tables belong to the machine plane rather than the program-data plane: they configure the
machine's stations, not a part's setup, and they carry no per-workpiece meaning the way a work
coordinate or a datum shift does. That is the classification the branch builder gives the node, and
it is the reason the node sits beside Machine Limits and Rapid Feedrates rather than beside Work
Coordinates.</p>
<p><strong>Their storage half is a separate question, and the answer is the project.</strong> The Siemens preset
carries a <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTableProxy.html">SiemensMachineDataTableProxy</a>, not a table: the
proxy holds a fixed machine-config seed, deep-clones it into the project's per-case list only when
the project has no Siemens machine-data table yet, and thereafter resolves the project's own table.
Three consequences follow, and they are the ones the plane's other project-owned leaves live with —
Machine Limits, Rapid Feedrates, Home / G28 Reference, M-Code Declarations and Parameters (Native),
all rows of the same per-case table. The plane's runner-owned leaves get the opposite of all three,
and which half a leaf falls in is listed on
<a class="xref" href="index.html">Machine and Controller Plane</a>:</p>
<ul>
<li>Edited positions are stored on the <strong>project</strong>, serialized with the per-case table as one XML
element per entry carrying its table number and its value.</li>
<li>Installing another Siemens runner, or re-applying the Siemens brand, leaves them alone: the
incoming proxy re-binds to the project's table instead of cloning its seed over it.</li>
<li>Saving a controller file captures the <strong>seed</strong>, not the edited table. A <code>.Controller</code> written after
the tables were filled in carries the preset seed they were cloned from.</li>
</ul>
<p>Switching to any other brand removes the Siemens machine-data table from the project, because no
proxy of the incoming runner resolves it, and the positions go with it. Switching back clones a fresh
table from the seed rather than restoring the removed one — see
<a class="xref" href="brand-switch.html">Brand Switch</a>.</p>
<h2 id="the-assignment-is-shown-here-and-edited-elsewhere">The Assignment Is Shown Here and Edited Elsewhere</h2>
<p>Above the two drafts the panel renders one display-only table: <strong>Axis</strong> against <strong>Indexing assignment
(MD30500)</strong>, the per-axis machine datum that decides what an axis does with the two global lists.
Four values are named:</p>
<table>
<thead>
<tr>
<th>Assignment</th>
<th>Label the user reads</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td><em>Not an indexing axis</em></td>
<td>the axis ignores the coded-position family</td>
</tr>
<tr>
<td>1</td>
<td><em>Table 1 (MD10910)</em></td>
<td>the axis reads the first draft below</td>
</tr>
<tr>
<td>2</td>
<td><em>Table 2 (MD10930)</em></td>
<td>the axis reads the second draft below</td>
</tr>
<tr>
<td>3</td>
<td><em>Equidistant (MD30501–MD30503)</em></td>
<td>positions come from a spacing rule, not from either table</td>
</tr>
</tbody>
</table>
<p>Any other stored value renders as the bare number.</p>
<p><strong>The rows are the axes that carry an MD30500 entry, not the plane's usual axis set.</strong> Every other
per-axis leaf reads the table's axis-type rows — the set a machine-tool install stamps the chain's
axis codes into — while this one reads the keys of the assignment row itself, sorted by axis name. An
axis of the machine with no assignment entry does not appear here at all, and an assignment entry
written against a name the machine does not have does.</p>
<p>An axis the table reports as modular rotary carries the note <em>(wraps 0–360°)</em> beside its name, and
its presence is what turns on the one-revolution rule for whichever table it reads. That report is
the machine-axis contract's default —
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IMachineAxisConfig.html#Hi_NcParsers_Dependencys_IMachineAxisConfig_IsModularRotary_">IsModularRotary</a><small>(API)</small> is not
overridden by any parameter table — so on this branch every rotary and every spindle-mode axis counts
as modular.</p>
<p><strong>Nothing on this leaf writes an assignment.</strong> The panel renders the value as plain text, and the
leaf's REST surface exposes a reader for the two tables with their per-axis assignments and a writer
for one position table; no endpoint of its own writes an assignment. MD30500 is written where every
other raw machine datum is written: on
<a class="xref" href="native-parameters.html">Parameters (Native)</a>, as a row of the <strong>Axis parameters
(integer)</strong> section, added through the <em>Section</em> / <em>Parameter id</em> / <em>Axis</em> / <em>Value (raw)</em> footer with
the section set to <strong>Axis (integer)</strong>. With no assignment row anywhere on the table this panel says
so and points there: <em>&quot;No axis declares an indexing assignment (MD30500) yet — add it as a per-axis
integer parameter in Parameters (Native).&quot;</em></p>
<p>The equidistant definition is edited entirely on that same sibling leaf and has <strong>no</strong> surface here.
Its three machine data — the numerator MD30501, the denominator MD30502 and the offset MD30503 —
are ordinary parameter rows, and an axis on assignment 3 consults neither of the two tables this
panel edits. On a modular rotary axis the numerator is ignored and the revolution is divided into
MD30502 positions; elsewhere the spacing is the numerator over the denominator and the position count
is unbounded.</p>
<h2 id="draft-validate-save-whole">Draft, Validate, Save Whole</h2>
<p>This is the branch's one draft-then-save table, and it departs from the shared commit rule that
<a class="xref" href="../editing-contract.html">Editing Contract</a> sets for its siblings. Every other table
in the branch commits per cell and rolls a failed write back; this one commits nothing until a button
is pressed, and keeps a failed edit on screen.</p>
<ul>
<li><strong>Cells bind straight into the draft.</strong> Each numeric cell is bound with a two-way model and carries
no commit handler, so a cell's commit — which the widget runs on blur and on Enter, never on a
keystroke — lands in the draft row and goes no further. The badge, the validation line and the two
buttons therefore follow those commits rather than the typing. The field itself still refuses text
it cannot parse, with an untranslated message under the box; it carries no minimum and no maximum
here, so every rule below is the draft validator's — see
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>.</li>
<li><strong>Add and delete are draft operations too.</strong> <em>Add Position</em> appends an empty row and is disabled at
60; the row's delete button removes it from the draft with no confirmation and no request.</li>
<li><strong>An orange <em>unsaved</em> badge</strong> appears beside a table's title while the draft differs from what was
last saved, compared row count first and then value by value.</li>
<li><strong>Save Table sends the whole list</strong> for that one table and is disabled while the draft is clean or
while validation fails. <em>Revert</em> restores the draft from the last saved values.</li>
<li><strong>The validator mirrors the endpoint's own rules</strong> and reports the first violation as a line under
the table: the per-table maximum, a <em>fill in position … (or delete its row)</em> line naming an empty
or non-finite cell, a strictly-ascending check naming both offending positions with their values,
and the <code>0 ≤ position &lt; 360</code> range when a modular rotary axis reads that table.</li>
<li><strong>A failed save does not roll the draft back.</strong> The draft stays dirty and the badge stays up, so
the work is not lost — the opposite of every other panel in the branch.</li>
<li><strong>Saving an empty table clears it.</strong> The endpoint accepts an empty list, and the axes assigned to
that table then stop being indexing axes.</li>
</ul>
<p>The endpoint enforces the same four rules independently, so the panel's validation is live feedback
rather than the gate. A save that passes is applied to the live table and the panel does not re-read:
it marks the draft saved from what it sent.</p>
<h2 id="what-a-freshly-switched-siemens-runner-shows">What a Freshly Switched Siemens Runner Shows</h2>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensMachineDataTable_Default3Axis">Default3Axis</a> — the seed the Siemens
proxy clones — declares a linear X, Y and Z, a reference position and a velocity for each, a maximum
spindle speed and six note-only OEM M-codes. It declares <strong>no</strong> MD30500 assignment and <strong>no</strong> entry
in either position table. So a project that has just been switched to Siemens grows this node,
opens it successfully, and finds it completely empty: the assignment caption in place of the axis
table, and two titled tables with headers, no rows and an <em>Add Position</em> button.</p>
<p>This leaf is also the only place the two lists appear. Parameters (Native) shows every row of the
machine-data table's three parameter dictionaries, and the two position tables are not in any of
them — they are separate list properties, so this panel is the whole editable surface for MD10910
and MD10930.</p>
<p>The panel opens through the branch's two-layer empty gate: the shared <em>&quot;No NC runner — load a
project first.&quot;</em> from the runner snapshot, then <em>&quot;No Siemens machine-data table on the active
runner.&quot;</em> from its own read. The second line is also what a failed read leaves on screen, since the
presence flag starts false and the failure path only raises a toast.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>Indexing Position Tables</strong> Node Row — <code>equipment/controller/machine/indexing-positions</code>,
between <strong>Subprogram Folders</strong> and <strong>Parameters (Native)</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of the selected node
<ul>
<li><strong>Indexing Position Tables</strong> Panel — one column, gutter-spaced
<ul>
<li>Description Caption — <em>&quot;Global indexing position tables for coded-position programming
(<code>CAC/CIC/CDC/CACP/CACN</code>). Row order is position-number order — row 1 is indexing position 1.
Values are in the axis' native units (degrees for rotary, mm for linear) and must be strictly
ascending. Which axis reads which table is MD30500, edited in Parameters (Native); an axis
whose table is empty is not an indexing axis (its coded-position words stay unresolved).&quot;</em></li>
<li>Assignment Table — dense, flat, bordered; display-only
<ul>
<li>Header Row — <strong>Axis</strong>, <strong>Indexing assignment (MD30500)</strong></li>
<li>Data Row — the axis name in bold, with <em>(wraps 0–360°)</em> beside it on a modular rotary axis,
then the assignment's label</li>
<li>No-Assignment Caption — replaces the table when no axis carries an assignment</li>
</ul>
</li>
<li>Position Table Block — one per table, twice
<ul>
<li>Title Row — <strong>Table 1 (MD10910)</strong> or <strong>Table 2 (MD10930)</strong>, then either <em>used by</em> and the
names of the axes assigned to it or <em>not used by any axis</em>, then an orange <strong>unsaved</strong> badge
while the draft is dirty</li>
<li>Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Position #</strong>, <strong>Value</strong>, and an unlabelled action column</li>
<li>Data Row — the 1-based position number in bold, the numeric cell, and a delete button that
removes the row from the draft</li>
</ul>
</li>
<li>Validation Line — the first rule the draft breaks, in the negative colour</li>
<li>Button Row — <strong>Add Position</strong> (disabled at 60 rows), then <strong>Revert</strong> and <strong>Save Table</strong>, both
disabled while the draft is clean and <strong>Save Table</strong> also while validation fails</li>
</ul>
</li>
<li>No-Table Body — replaces everything above: <em>&quot;No Siemens machine-data table on the active
runner.&quot;</em></li>
<li>Shared Empty State — replaces the whole body while the snapshot reports no runner: <em>&quot;No NC
runner — load a project first.&quot;</em></li>
</ul>
</li>
<li>Toast — negative, three seconds, the panel's context followed by the server's own message</li>
</ul>
</li>
</ul>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcIndexingPositionsPanel.vue</code> — this panel: the draft
model over the two tables, the dirty comparison behind the badge, the validator that mirrors the
endpoint's rules, the display-only assignment table with its four labels and its wrap note, and the
save that marks the draft clean without re-reading.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the node's id, label and label key,
and the snapshot flag it is pushed behind, between the subprogram and native leaves.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the reader that parses the two tables and the axis rows,
the whole-table replace and its documented refusals, and the snapshot flag the builder gates on.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared runner snapshot the first empty layer
reads.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the cell: the two-way binding this panel
uses in place of a commit handler, the blur-and-Enter moment that binding emits on, the
empty-to-null parse the validator then reports, and the untranslated parse message.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcNativeParamsPanel.vue</code> — the sibling panel that owns
the assignment: the integer per-axis section and the footer that adds a row by id and axis.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the node label, the description caption, the four assignment
labels, the wrap note, the table titles, the used-by captions, the four validation messages and the
absent-table line quoted above.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Axis</strong> and <strong>Value</strong> column headers.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the read that pairs the two tables with the
per-axis assignments and reports presence from the Siemens table alone, the whole-table replace
with its capacity, finite, ascending and one-revolution checks, the integer per-axis parameter
write the assignment is edited through, and the brand switch whose sweep removes the table on
leaving Siemens.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/IIndexingPositionConfig.cs</code> — the contract: 1-based position
numbering, the native-unit and frame rules, the position count, the cyclic test, the number-to-
coordinate lookup and the anchor search, and the rule that an axis whose table is empty is not a
usable indexing axis.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code> — the two position lists, their
XML round-trip and the statement that the lookups do not validate the Siemens constraints, the
MD30500 assignment dictionary, the equidistant spacing resolution over MD30501–MD30503, the five
contract implementations, and the default seed that declares none of them.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTableProxy.cs</code> — the get-or-create proxy:
the seed serialized on the runner, the deep clone installed only into a project that has no table,
and the deliberate refusal to implement the machine-config interfaces itself.</li>
<li><code>HiMech/NcParsers/Dependencys/IMachineAxisConfig.cs</code> — the axis contract, and the modular-rotary
default that makes every rotary and spindle axis subject to the one-revolution rule.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the Siemens preset that carries the machine-data proxy, and
the proxy resolution every read on this leaf goes through.</li>
<li><code>HiMech/NcParsers/Syntaxs/SiemensSyntaxUtil.cs</code> — the syntax list that wires the coded-position
producer and its two write-stage consumers into the Siemens pipeline.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensAcIcSyntax.cs</code> — the producer: it unwraps the
five coded verbs only on usable indexing axes and stamps the per-word override, leaving the word
untouched elsewhere.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/CodedPositionUtil.cs</code> — the shared resolution: position number to
coordinate, the hold-on-failure semantics, and the diagnostics for an invalid number or a table
that has gone missing between stages.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/McAbcSyntax.cs</code>,
<code>HiMech/NcParsers/LogicSyntaxs/IncrementalResolveSyntax.cs</code> — the two write-stage consumers, rotary
words and linear words.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/McAbcCyclicPathSyntax.cs</code> — the tail pass that turns the resolved
directional approach into a path, and the one other syntax pass that reads the modular-rotary flag
directly.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane this leaf sits on, and which half of the project each
of its neighbours is stored in</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — the gate behind this node, why it is the machine plane's
only single-brand row, and what every other flag probes</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the per-cell commit rule the rest of the branch follows
and this panel deliberately breaks</li>
<li><a class="xref" href="native-parameters.html">Native Parameters</a> — where the MD30500 assignment and the equidistant
machine data this leaf only displays are actually edited</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,614 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Interface Parameters | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Interface Parameters | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerParameters">
<h1 id="interface-parameters">Interface Parameters</h1>
<p>Two leaves of the Controller branch's machine plane put the controller's non-axis parameters into
domain vocabulary instead of parameter numbers: <strong>Controller Parameters</strong> and <strong>Canned Cycle
(Peck)</strong>. They live on the General Setup page at <code>/general-setup</code> under the Control-Tree ids
<code>equipment/controller/machine/parameters</code> and <code>equipment/controller/machine/canned-cycle</code>. Each value
they edit that is a row of the brand parameter table is also reachable as a raw numbered row on the
sibling leaf, <a class="xref" href="native-parameters.html">Parameters (Native)</a>; the values stored
elsewhere are named below, and they reach no native form at all.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<p>Controller Parameters is ungated: the branch builder pushes it with the brand and the four per-axis
leaves, so it exists wherever a runner resolves. Canned Cycle (Peck) is pushed behind the
<code>hasCannedCycle</code> flag, which is true on all five shipped brand presets — see
<a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<h2 id="the-five-fields-and-which-brand-shows-which">The Five Fields, and Which Brand Shows Which</h2>
<p>Controller Parameters renders at most five controls, in one column, in this fixed order. There is no
tab, no group box and no scroll region; a field the brand does not have is not disabled but absent.</p>
<table>
<thead>
<tr>
<th>Field the user reads</th>
<th>Control</th>
<th>Backed by</th>
<th>Fanuc, Mazak</th>
<th>Syntec</th>
<th>Siemens</th>
<th>Heidenhain</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max spindle speed</td>
<td>numeric, <code>rpm</code>, minimum 0</td>
<td>the brand parameter table</td>
<td>#3741</td>
<td>Pr3741</td>
<td>MD35100</td>
<td>MP100</td>
</tr>
<tr>
<td>Cutter compensation startup / cancel type</td>
<td>select — Type A / Type B / Type C</td>
<td>the brand parameter table</td>
<td>#5003</td>
<td>Pr5003</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>Tool-axis direction</td>
<td>select — Z / Y / X</td>
<td>the brand parameter table</td>
<td>—</td>
<td>—</td>
<td>—</td>
<td>MP101</td>
</tr>
<tr>
<td>GOTO — max jumps per N target †</td>
<td>numeric, minimum 1</td>
<td>an iteration-guard dependency</td>
<td>Fanuc guard</td>
<td>Fanuc guard</td>
<td>Siemens guard</td>
<td>—</td>
</tr>
<tr>
<td>WHILE/DO — max iterations per loop †</td>
<td>numeric, minimum 1</td>
<td>an iteration-guard dependency</td>
<td>Fanuc guard</td>
<td>Fanuc guard</td>
<td>Siemens guard</td>
<td>—</td>
</tr>
</tbody>
</table>
<p>† The Fanuc-family spelling. Siemens spells both guard labels differently — the two pairs are below.</p>
<p>The last two sit under a separator and the caption <em>Macro loop guards (advanced)</em>, and that whole
group is rendered only when at least one of the two guard values resolves. On Heidenhain neither
does, so the separator, the caption and both fields are absent together; that brand shows two fields
in total.</p>
<p><strong>The two guard labels are chosen from the brand marker, not from the dependency that answered.</strong> The
Siemens spellings — <em>GOTOF/GOTOB — max jumps per label</em> and <em>WHILE / FOR / REPEAT / LOOP — max
iterations per loop</em> — are used when the brand marker string reads <code>Siemens</code>, and the Fanuc spellings
otherwise. Syntec and Mazak therefore read the Fanuc spellings, which is correct: both presets carry
the Fanuc guard types. The label and the value are independent lookups, so a runner whose brand
marker was edited apart from its dependency list can show one brand's wording over the other brand's
guard.</p>
<p>The three select options <em>Type A</em> / <em>Type B</em> / <em>Type C</em> are translated strings; the three tool-axis
options <code>Z</code>, <code>Y</code> and <code>X</code> are literal letters written into the component and are the same in every
locale.</p>
<h2 id="visibility-is-null-driven-not-flag-driven">Visibility Is Null-Driven, Not Flag-Driven</h2>
<p>Which fields appear is decided <strong>per field, by a null in the read</strong>, and not by the shared runner
snapshot the tree shape is built from. The reader answers one object with <code>present</code> plus five
nullable numbers, computed by type-switching on the resolved dependencies:</p>
<ul>
<li><strong>Max spindle speed</strong> is non-null for each of the four brand parameter-table types, so it is
present on all five brands. A <code>ControllerParameterTableBase</code> subclass outside those four answers
null, and the field disappears while <code>present</code> stays true.</li>
<li><strong>Cutter compensation type</strong> is non-null only for the Fanuc and the Syntec table.</li>
<li><strong>Tool-axis direction</strong> is non-null only for the Heidenhain table.</li>
<li>Each <strong>guard</strong> is the Fanuc dependency's value if one resolves, otherwise the Siemens dependency's,
otherwise null.</li>
</ul>
<p>The panel's own conditions are <code>!= null</code> on those five keys. No snapshot flag decides a field on this
leaf; the only one read at all is the shared no-runner guard. The snapshot's brand marker is read
twice more, but only to choose between the two guard label pairs — never to show or hide anything.</p>
<p><strong>Three flags in the snapshot describe exactly these fields and are read by nothing.</strong>
<code>hasCutterComp</code>, <code>hasToolAxisDirection</code> and <code>hasIterationGuards</code> are computed server-side, typed,
defaulted and parsed in the client's API layer, and no component consults any of them. They remain a
live part of the REST snapshot for any other client. Two notes about the third:</p>
<ul>
<li><code>hasIterationGuards</code> probes the Fanuc and the Siemens jump-guard types only. The Heidenhain preset
carries a jump guard of its own type, so the flag reports false for a brand that has one. That
matches what the panel shows on the shipped presets — the reader probes the same two types, so the
Heidenhain guard has no field either — while under-describing the runner.</li>
<li>Its doc comment names the Fanuc GOTO / WHILE-DO guards alone while the code also matches the
Siemens jump guard. The code is what ships.</li>
</ul>
<p>The panel's whole body — the guards included — is hidden behind <code>present</code>, which reports the
<strong>parameter table</strong> and nothing else: <em>&quot;No controller parameter table on the active runner.&quot;</em> The
guards live on separate dependencies with their own write endpoints, so a runner carrying guards but
no parameter table would hide two reachable fields. All five brand presets carry a parameter-table
proxy, so the combination does not arise on a shipped preset.</p>
<h2 id="units-edited-versus-stored">Units: Edited Versus Stored</h2>
<p>Every value but one is stored in the unit it is edited in.</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Edited as</th>
<th>Stored as</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max spindle speed</td>
<td>rpm</td>
<td>rpm, in the table's system-parameter row</td>
</tr>
<tr>
<td>Cutter compensation type</td>
<td>one of three named options</td>
<td>the option's index, 0 / 1 / 2, as a number in the same row set</td>
</tr>
<tr>
<td>Tool-axis direction</td>
<td>one of <code>Z</code> / <code>Y</code> / <code>X</code></td>
<td>0 / 1 / 2 in the same row set</td>
</tr>
<tr>
<td>Macro guards</td>
<td>a plain count</td>
<td>the same count, on the guard dependency</td>
</tr>
<tr>
<td>Peck retraction clearance</td>
<td>mm</td>
<td>mm on Fanuc and Mazak, <strong>microns on Syntec</strong>, mm on the generic fallback</td>
</tr>
</tbody>
</table>
<p><strong>Syntec is the one brand whose stored unit differs from the edited one, and the peck clearance is
the one value it happens to.</strong> The Syntec table's accessor divides its stored row by 1000 on read and
the write multiplies by 1000, so this leaf shows <code>5</code> on every brand while the native leaf shows
<code>5000</code> under Pr4002 on Syntec and <code>5</code> under #4002 on Fanuc and Mazak. An operator copying a number
off a real controller reads the native form; an operator entering a clearance in millimetres reads
this one. The two are the same setting.</p>
<p>The two select fields are the second unit hazard, in a milder form: the native leaf shows their
stored number, <code>0</code>, <code>1</code> or <code>2</code>, with the brand table's own description of that number under the id,
and never the words <em>Type A</em> or <code>Z</code>.</p>
<p>Not every value reaches the native form at all. The two macro guards are properties of their own
runner-owned dependencies rather than rows of any table, so they appear there on no brand; the peck
clearance does the same on Siemens and Heidenhain, where the generic fallback supplies it.</p>
<h2 id="peck-retraction-clearance">Peck Retraction Clearance</h2>
<p>Canned Cycle (Peck) is a single numeric field labelled <em>Peck retraction clearance (G83)</em>, in
millimetres, at zero or above, with a caption naming where the value is stored. Its model is
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ICannedCycleConfig.html#Hi_NcParsers_Dependencys_ICannedCycleConfig_PeckRetractionDistance_mm">PeckRetractionDistance_mm</a>, and the read reports
which implementation answered. Three can, and between them they cover every shipped preset:</p>
<table>
<thead>
<tr>
<th>Caption the user reads</th>
<th>Answered by</th>
<th>Brands</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>Stored in the Fanuc parameter table (#4002, mm).</em></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a></td>
<td>Fanuc, Mazak</td>
</tr>
<tr>
<td><em>Stored in the Syntec parameter table (Pr4002, microns) — edited here in mm.</em></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a></td>
<td>Syntec</td>
</tr>
<tr>
<td><em>This brand takes the clearance per call (CYCLE83 / CYCL DEF); this value is the fallback used when a call omits it.</em></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.FallbackConfig.html">FallbackConfig</a></td>
<td>Siemens, Heidenhain</td>
</tr>
</tbody>
</table>
<p>That third caption describes an override the mapping does not implement. The Siemens and Heidenhain
front-end syntaxes translate their own cycle vocabulary into the shared ISO cycle section, whose key
set is fixed at X, Y, Z, R, Q, F, P and K — and none of those eight is a retraction clearance. The
CYCLE83 mapping fills the R plane, the hole bottom Z and the first peck depth Q; the klartext cycle
mapping fills the same three plus a bottom dwell and a feed, and folds the klartext set-up clearance
into the R plane rather than keeping a slot of its own. The shared G83 expansion then reads the
clearance from this dependency unconditionally. So on those two brands the value here is not a
fallback for an omitted argument: it is the clearance every pecking cycle uses.</p>
<p>An <code>ICannedCycleConfig</code> implementation outside those three answers a fourth source token, which no
caption is written for: the field renders at the value that implementation supplies and the line
under it is blank. It is the peck counterpart of the null a parameter table outside the four returns
on the other leaf.</p>
<p>Which storage answered also decides where the value lives. On Fanuc, Mazak and Syntec it is a row of
the project's parameter table and appears in the native form; on Siemens and Heidenhain it is a field
of a plain runner-owned dependency, so it appears in <strong>no</strong> native form and a brand apply returns it
to the preset default — see <a class="xref" href="index.html">Machine and Controller Plane</a>.</p>
<p>The value is consumed by two cycle expansions and they use it differently. In G83 it is the clearance
the tool rapids down to above the previous stroke bottom before feeding deeper; in G73 it is the
whole chip-break retract distance between strokes. The field's label names G83 only. G73 expands on
Fanuc, Mazak, Syntec and Siemens; the Heidenhain syntax list registers the G83 expansion without it.</p>
<h2 id="what-the-values-reach">What the Values Reach</h2>
<p>Four of the six values are read by the parsing pipeline; the other two are stored declarations.</p>
<ul>
<li><strong>Peck retraction clearance</strong> drives the two cycle expansions above.</li>
<li><strong>Tool-axis direction</strong> is read where a Heidenhain <code>PLANE SPATIAL … COORD ROT</code> block is resolved:
it decides which spatial angle may be non-zero for the rotation to count as purely about the tool
axis, which is the only case where COORD ROT suppresses rotary positioning and rotates the
coordinate system instead.</li>
<li><strong>The macro guards</strong> are soft caps with runtime counters. Above the cap the consuming block emits
an iteration-limit warning and stops firing the jump or the loop back-edge, so a runaway macro ends
as a diagnostic instead of an unbounded run. Each is keyed per source file and per target — per N
target, per label or per loop — and the counters reset on the session-init edge while the cap does
not.</li>
<li><strong>Max spindle speed</strong> and <strong>cutter compensation type</strong> are stored, edited and serialized, and no
syntax or semantic in the shipped pipeline reads either. Cutter radius compensation itself <em>is</em>
resolved, by the G41 / G42 / G40 pass over the motion sections; what that pass does not consult is
the A / B / C startup-and-cancel vector style this field records.</li>
</ul>
<h2 id="defaults-a-freshly-seeded-table-opens-on">Defaults a Freshly Seeded Table Opens On</h2>
<p>Each brand's default table decides what the fields show once that brand's table is cloned fresh into
a project; the guards are plain preset entries and are re-created at their type defaults on every
apply.</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Fanuc, Mazak</th>
<th>Syntec</th>
<th>Siemens</th>
<th>Heidenhain</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max spindle speed</td>
<td>60000</td>
<td>24000</td>
<td>60000</td>
<td>60000</td>
</tr>
<tr>
<td>Cutter compensation type</td>
<td>Type A</td>
<td>Type A</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>Tool-axis direction</td>
<td>—</td>
<td>—</td>
<td>—</td>
<td>Z</td>
</tr>
<tr>
<td>GOTO jump cap</td>
<td>1000</td>
<td>1000</td>
<td>1000</td>
<td>—</td>
</tr>
<tr>
<td>Loop cap</td>
<td>10000</td>
<td>10000</td>
<td>10000</td>
<td>—</td>
</tr>
<tr>
<td>Peck retraction clearance</td>
<td>5 mm</td>
<td>5 mm (5000 stored)</td>
<td>5 mm (fallback)</td>
<td>5 mm (fallback)</td>
</tr>
</tbody>
</table>
<p>The table-backed values — the first three rows, and the peck clearance on the three brands whose
parameter table stores it — are reached only by an apply that leaves the project holding no table of
the incoming proxy's type: the outgoing brand's table is swept and the incoming proxy clones its seed
in place of it. Fanuc and Mazak share one table type, so the hop between those two re-binds the
project's existing table instead, and every value in it survives with its edits. The two guards and
the fallback clearance are runner-owned and return to the preset's on every apply, whichever brands
it runs between.</p>
<p>The two caps differ by an order of magnitude on purpose: a jump is not the legitimate bulk-iteration
primitive, so its cap stays tight, while loops are what generated drill grids and calibration sweeps
are built from.</p>
<h2 id="reading-a-field-can-create-its-row">Reading a Field Can Create Its Row</h2>
<p>Both readers reach the stored values through accessors that <strong>write a default into the table when the
row is absent</strong>. Opening either leaf on a table whose max-spindle, cutter-comp, tool-axis or peck
row was deleted from <a class="xref" href="native-parameters.html">Parameters (Native)</a> therefore
re-creates that row at the type's default, in the live project-owned table, with no edit and no
request from the user. On Heidenhain — which has neither a cutter-comp row nor a peck row — the
max-spindle and tool-axis rows are the whole set. The native leaf's delete removes the row; the next
read of this leaf puts it back.</p>
<p>The guard dependencies have no such behaviour — their values are ordinary properties with field
initializers.</p>
<h2 id="editing-a-field">Editing a Field</h2>
<p>Both leaves follow the branch's shared commit, rollback and toast rules, which are
<a class="xref" href="../editing-contract.html">Editing Contract</a>. Four details are specific to these two
leaves:</p>
<ul>
<li><strong>The numeric fields commit on blur or on Enter</strong>, never per keystroke; their full contract is
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>. The two selects commit on the pick.</li>
<li><strong>Each write is one field.</strong> Every control has its own endpoint and sends only its own value; no
edit here resends a neighbouring field.</li>
<li><strong>A value the handler refuses is left on screen.</strong> Clearing a field parses to null and the handler
returns before the request; a guard field additionally refuses anything that is not a whole number,
and every numeric handler here refuses a non-finite one — the field accepts the words <code>Infinity</code>
and <code>NaN</code> as text and turns them into numbers, and the handlers are what stop them. In each case
the box keeps what was typed — blank, where the field was cleared — while the stored value stands,
because nothing re-writes the box until the panel is remounted by selecting another node and coming
back.</li>
<li><strong>A bound violation never reaches a handler at all.</strong> The numeric field itself refuses a value
below its minimum and shows an untranslated <em>Must be ≥ …</em> under the box. Each handler carries a
lower-bound test of its own — 1 on a guard, 0 on the peck clearance — but the field's own minimum
is the same number and stops every finite value first, so those tests decide nothing.</li>
</ul>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>Controller Parameters</strong> Node Row — <code>equipment/controller/machine/parameters</code></li>
<li><strong>Canned Cycle (Peck)</strong> Node Row — <code>equipment/controller/machine/canned-cycle</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Controller Parameters</strong> Panel — one column, gutter-spaced
<ul>
<li><strong>Max spindle speed</strong> Numeric Field — dense, outlined, suffixed <code>rpm</code>, minimum 0</li>
<li><strong>Cutter compensation startup / cancel type</strong> Select — dense, outlined; options <strong>Type A</strong>,
<strong>Type B</strong>, <strong>Type C</strong></li>
<li><strong>Tool-axis direction</strong> Select — dense, outlined; options <strong>Z</strong>, <strong>Y</strong>, <strong>X</strong></li>
<li>Guard Group — rendered only when at least one guard value resolves
<ul>
<li>Separator</li>
<li><strong>Macro loop guards (advanced)</strong> Caption</li>
<li>Jump-Guard Numeric Field — minimum 1; labelled <strong>GOTO — max jumps per N target</strong> or
<strong>GOTOF/GOTOB — max jumps per label</strong></li>
<li>Loop-Guard Numeric Field — minimum 1; labelled <strong>WHILE/DO — max iterations per loop</strong> or
<strong>WHILE / FOR / REPEAT / LOOP — max iterations per loop</strong></li>
</ul>
</li>
<li>No-Table Block — replaces every field above:
<em>&quot;No controller parameter table on the active runner.&quot;</em></li>
</ul>
</li>
<li><strong>Canned Cycle (Peck)</strong> Panel — one column, gutter-spaced
<ul>
<li><strong>Peck retraction clearance (G83)</strong> Numeric Field — dense, outlined, suffixed <code>mm</code>, minimum 0</li>
<li>Source Caption — one of the three sentences in the table above, or empty where a fourth
implementation answered</li>
<li>No-Config Block — replaces both:
<em>&quot;No canned-cycle config on the active runner.&quot;</em></li>
</ul>
</li>
<li>Shared Empty State — replaces either panel's whole body while the snapshot reports no runner:
<em>&quot;No NC runner — load a project first.&quot;</em></li>
</ul>
</li>
</ul>
<p>Neither panel has a table, an add-row control, a delete control or a Save button.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcControllerParamsPanel.vue</code> — the five-field panel:
the per-field <code>!= null</code> conditions, the guard group's combined condition, the brand-marker choice
between the two guard label pairs, the translated compensation-type options beside the literal
tool-axis letters, and the three commit handlers with their null, non-finite and integer refusals
beside the lower-bound tests the field's own minimum shadows.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcCannedCyclePanel.vue</code> — the peck panel: the single
field, the presence layer, and the caption chosen from the read's source token.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line both
panels open with.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the numeric field: the unit rendered as a
suffix, commit on blur or Enter, the empty-text-to-null parse both panels reject, and the
untranslated bound message that stops a value before it is emitted.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the two node ids and their labels:
Controller Parameters among the unconditional machine leaves, Canned Cycle (Peck) behind the
peck-provider flag.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the two readers and their setters, the five nullable
parameter keys, the peck source token, and the three snapshot flags that describe these fields and
are declared, defaulted and parsed without a consumer.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared runner snapshot the first empty layer
reads.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the two node labels, the five field labels, the two guard
label pairs, the compensation-type options, the macro-guard caption, the three peck source
sentences and the two empty-state lines quoted above.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the parameter read's type switches and its
table-only presence flag, one write per field, the guard writes that fall back from the Fanuc
dependency to the Siemens one, the peck read with its source token, and the peck write that
converts to microns for the Syntec table.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the base every brand table
extends, and the raw system-parameter dictionary the first three fields are rows of.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTable.cs</code> — the Fanuc and Mazak table: the
max-spindle, peck-clearance and cutter-comp parameter numbers, the compensation-type enum with its
three vector styles, the millimetre peck accessor, and the default seed.</li>
<li><code>HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTable.cs</code> — the Syntec table: the same three
parameter numbers, the micron peck row and the accessor that converts it, and the default seed.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code> — the Siemens table: the
max-spindle machine datum, and the absence of any compensation-type or peck member.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs</code> — the Heidenhain table: the
max-spindle and tool-axis-direction machine parameters and their default seed.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the proxy shape all four brand
tables share: the seed clone that fires only where the project holds no table of that type, and
the pure take afterwards.</li>
<li><code>HiMech/NcParsers/Dependencys/ICannedCycleConfig.cs</code> — the peck-clearance contract and its three
implementations.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/FallbackConfig.cs</code> — the runner-owned clearance the Siemens
and Heidenhain presets carry, and its millimetre default.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucGotoIterationDependency.cs</code>,
<code>HiMech/NcParsers/Dependencys/Fanuc/FanucWhileDoIterationDependency.cs</code>,
<code>HiMech/NcParsers/Dependencys/Siemens/SiemensGotoIterationDependency.cs</code>,
<code>HiMech/NcParsers/Dependencys/Siemens/SiemensLoopIterationDependency.cs</code> — the four guards the two
advanced fields write: their caps, their per-file counters and the session reset that clears the
counters alone.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainGotoIterationDependency.cs</code> — the fifth guard,
the one the reader does not probe and no field edits.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets: which parameter-table proxy, which
guards and whether a fallback clearance each one carries.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/CannedCycleSyntaxUtil.cs</code> — the shared ISO cycle section: the fixed
eight-key parameter set none of whose keys is a retraction clearance, and the modal merge that
consumes them.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/PeckDrillingCycleSyntax.cs</code> — the G83 expansion, which reads the
clearance from the dependency rather than from the block.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/HighSpeedPeckCycleSyntax.cs</code> — the G73 expansion that spends the
same value as the chip-break retract distance.</li>
<li><code>HiMech/NcParsers/Syntaxs/FanucSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/MazakSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SyntecSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SiemensSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/HeidenhainSyntaxUtil.cs</code> — the five brand syntax lists, and which of
them register both peck expansions against the one that registers the G83 expansion alone.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Siemens/SiemensModalCycleSyntax.cs</code> — the CYCLE83 mapping onto the
shared cycle section, and the arguments it drops.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainCannedCycleSyntax.cs</code> — the klartext cycle
mapping onto the same section.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainPlaneTiltSyntax.cs</code> — the one consumer of the
tool-axis direction: the pure-tool-axis test that decides whether COORD ROT suppresses rotary
positioning.</li>
<li><code>HiMech/NcParsers/PostLogicSyntaxs/RadiusCompensationSyntax.cs</code> — the cutter radius compensation
that is resolved, and that does not consult the stored startup-and-cancel type.</li>
<li><code>HiGeom/Common/Collections/DictionaryUtil.cs</code> — the get-or-create the table accessors read through,
and therefore why a read re-creates a deleted parameter row.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane these two leaves sit on, and which half of the project
each of their values is stored in</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — why one of the two leaves is gated and the other is not, and
which brand resolves which parameter table</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the fetch, commit, rollback and empty-layer rules these
panels share with the rest of the branch</li>
<li><a class="xref" href="native-parameters.html">Native Parameters</a> — the same rows by number, in their raw stored units,
including the peck clearance this page shows converted</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,452 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>M-Code Declarations | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="M-Code Declarations | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerMCodes">
<h1 id="m-code-declarations">M-Code Declarations</h1>
<p>M-Code Declarations is the leaf that gives a machine's own M-codes a meaning the parser can act on —
what each machine-specific (OEM or PLC) code does — and it carries beside them the tool-change
trigger mode that decides whether a bare <code>T</code> word changes the tool by itself. It lives on the
General Setup page at <code>/general-setup</code> under the Control-Tree id
<code>equipment/controller/machine/m-codes</code>, grown while the runner snapshot reports a brand controller
parameter table. Both the declaration map and the trigger flag are stored on that one table, so this
leaf appears and disappears together with
<a class="xref" href="native-parameters.html">Parameters (Native)</a>, and the rows declared on it travel
with the project rather than with the runner file, which carries only the preset seed a fresh
project's table is cloned from.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<h2 id="the-one-object-behind-both-halves">The One Object Behind Both Halves</h2>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a> implements
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IMCodeDeclarationConfig.html">IMCodeDeclarationConfig</a> and
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IToolChangeTriggerConfig.html">IToolChangeTriggerConfig</a> over its own storage, so the table every
brand carries supplies the whole leaf: the declaration map keyed on the code as parsed, and the
single trigger boolean. A third face, <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ISpindleControlConfig.html">ISpindleControlConfig</a>, reads
the same map for spindle direction alone rather than keeping a second one, which is why one code is
never half-recognized by two separate dictionaries.</p>
<p>The tree gate and the panel's own second empty layer probe the same type. The branch builder grows
the node while the snapshot's <code>hasNativeTable</code> flag is set, and the read answers a presence flag
computed from the same resolved parameter table; every shipped brand preset carries one, so in
practice the leaf is unconditional and the <em>&quot;No controller parameter table on the active runner.&quot;</em>
line is reachable only on a runner assembled by hand or loaded from XML. Which brand satisfies which
gate is <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>Because the map is a member of the brand parameter table, and that table is the project's per-case
copy rather than the runner's, declarations survive installing another runner file of the same brand
— see <a class="xref" href="index.html">Machine and Controller Plane</a> for which half of a project each
value on this plane is written into.</p>
<h2 id="what-a-declaration-carries">What a Declaration Carries</h2>
<p>One row is one <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.MCodeEffects.html">MCodeEffects</a>, and a real OEM code is frequently
composite, so the row records every effect the code performs rather than a single meaning.</p>
<table>
<thead>
<tr>
<th>Column</th>
<th>Effect</th>
<th>What the code expands to</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Tool change</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.MCodeEffects.html#Hi_NcParsers_Dependencys_MCodeEffects_IsToolChange">IsToolChange</a></td>
<td><code>M06</code></td>
</tr>
<tr>
<td><strong>Spindle</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.MCodeEffects.html#Hi_NcParsers_Dependencys_MCodeEffects_SpindleDirection">SpindleDirection</a></td>
<td><code>M03</code> for CW, <code>M04</code> for CCW, <code>M05</code> for STOP</td>
</tr>
<tr>
<td><strong>Coolant</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.MCodeEffects.html#Hi_NcParsers_Dependencys_MCodeEffects_CoolantMode">CoolantMode</a></td>
<td><code>M07</code> for Mist, <code>M08</code> for Flood, <code>M09</code> for Off</td>
</tr>
<tr>
<td><strong>Not-simulated note</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.MCodeEffects.html#Hi_NcParsers_Dependencys_MCodeEffects_UnmodeledNote">UnmodeledNote</a></td>
<td>nothing — it raises one informational diagnostic per occurrence</td>
</tr>
</tbody>
</table>
<p>The two pickers each open on a dash, which is the null option: a declaration that touches neither
the spindle nor the coolant carries neither. The note is free text and is itself an effect — a code
whose real behaviour is a chip conveyor or a door interlock is declared with the note alone, and
every occurrence then reports <code>DeclaredMCode--UnmodeledEffects</code> at message severity instead of the
unknown-code warning. A declaration with no effect and no note is the fourth legal state and means
<em>consume this code silently</em>, which the panel's hint says in as many words: <em>&quot;A declaration with
nothing set consumes its code silently.&quot;</em></p>
<p>That fourth state is what a freshly declared row is. <strong>Declare</strong> writes a row whose tool-change box
is clear, whose two pickers sit on the dash and whose note is empty, so the act of naming a code
already silences it; the fields are then filled in against a live row.</p>
<h2 id="how-a-declared-code-is-read">How a Declared Code Is Read</h2>
<p><a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.MCodeExpansionSyntax.html">MCodeExpansionSyntax</a> rewrites the declared flag into the canonical
ISO flags the ordinary consumers already understand, and it runs ahead of them:
<a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.SpindleSpeedSyntax.html">SpindleSpeedSyntax</a>,
<a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.CoolantSyntax.html">CoolantSyntax</a> and
<a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.ToolChangeSyntax.html">ToolChangeSyntax</a> each then see the flag they know. Expanding once,
early, is what lets a single composite code feed several consumers without any of them fighting over
which removes the original flag.</p>
<p>Three rules of the rewrite are visible in a program:</p>
<ul>
<li><strong>The expansion sits at the declared flag's own position</strong>, and a repeat of the same declared code
later in the block is dropped. Textual order therefore keeps deciding last-wins conflicts exactly
as it did before any declaration existed — a composite code followed by a raw <code>M05</code> still ends
with the spindle stopped.</li>
<li><strong>A canonical flag that also appears raw and undeclared in the same block is not emitted twice.</strong>
The raw occurrence keeps its position, so the expansion cannot reorder what the block already said.</li>
<li><strong>Undeclared codes are untouched.</strong> One that no other syntax consumes then reaches
<a class="xref" href="../../../../api/Hi.NcParsers.InspectionSyntaxs.UnconsumedCheckSyntax.html">UnconsumedCheckSyntax</a>, which reports it as
<code>Parsing--Unconsumed</code> — the warning this leaf exists to answer.</li>
</ul>
<h3 id="the-spindle-only-row-takes-a-different-path">The spindle-only row takes a different path</h3>
<p>A declaration whose sole content is a spindle direction is deliberately <strong>not</strong> expanded.
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.MCodeEffects.html#Hi_NcParsers_Dependencys_MCodeEffects_IsSpindleDirectionOnly">IsSpindleDirectionOnly</a> marks it, the expansion skips
it, and <a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.SpindleSpeedSyntax.html">SpindleSpeedSyntax</a> resolves it in place through
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ISpindleControlConfig.html#Hi_NcParsers_Dependencys_ISpindleControlConfig_TryResolveDirection_">TryResolveDirection</a><small>(API)</small>.
What separates the two paths is mechanism, not reach. The resolver is consulted <strong>before</strong> the
built-in ISO mapping, so a spindle-only row overrides a canonical code where the code stands, with
no rewrite at all — declaring <code>M03</code> as CCW makes <code>M03</code> turn the spindle counter-clockwise. Add any
second effect to the same row and it leaves that path for the expansion, which arrives at the same
place by rewriting: the declared flag is replaced by the ISO flags its effects name, so declaring
<code>M08</code> as Mist makes the block emit <code>M07</code> instead. On either path a declaration over a canonical code wins against that
code's built-in meaning.</p>
<p>The split also reaches the project file. A spindle-only row serializes as the legacy
<code>&lt;SpindleMCode&gt;</code> element so an older reader still resolves it; every other row serializes as
<code>&lt;MCode&gt;</code> with one attribute per effect that is set. A row with nothing set writes an <code>&lt;MCode&gt;</code>
element carrying only its code, so the silent-consume state round-trips.</p>
<h3 id="what-the-writer-refuses">What the writer refuses</h3>
<p>The declare endpoint validates before it stores. An empty code is answered unsuccessfully; so is a
spindle direction that is not <code>CW</code> / <code>CCW</code> / <code>STOP</code> and a coolant mode that is not <code>Mist</code> / <code>Flood</code> /
<code>Off</code>, and those two answers quote the rejected value back alongside the set that was expected. A
typo therefore cannot degrade into a declaration that consumes a code silently. The panel's two
pickers cannot produce such a value; the guard is there for the other writers of the same table.</p>
<p>A coolant mode that names no known mode can still arrive from a project file, because the XML reader
keeps the attribute raw rather than normalizing it and write-then-read stays an identity. The
expansion is where that surfaces: it reports <code>DeclaredMCode--UnknownCoolantMode</code>, skips the coolant
half, and emits the rest of the declaration.</p>
<h2 id="the-tool-change-trigger-mode">The Tool-Change Trigger Mode</h2>
<p>The toggle above the table reads <em>&quot;T word performs the tool change itself (turret / lathe)&quot;</em> and
writes <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IToolChangeTriggerConfig.html#Hi_NcParsers_Dependencys_IToolChangeTriggerConfig_ToolWordTriggersChange">ToolWordTriggersChange</a>. It defaults
off, which is machining-centre behaviour: a <code>T</code> word only pre-selects, the magazine rotates without
moving a feed axis, and the trigger M-code performs the change.</p>
<p>Turned on, <a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.ToolChangeSyntax.html">ToolChangeSyntax</a> treats a block carrying a <code>T</code> word and
no tool-change flag as the change itself and records <code>T</code> as the triggering term. The test is per
block: a block carrying both a <code>T</code> word and a tool-change flag is unaffected — the flag wins and
<code>M06</code> is recorded — so no single block is counted twice. Where the two words sit in different blocks
— a <code>T</code> pre-select first, an <code>M06</code> later — the mode counts the <code>T</code> block as a change on its own, and
the <code>M06</code> block then records a second one, its tool number arriving modally. The toggle therefore
belongs on a machine whose <code>T</code> word really performs the change; on a machining centre it turns every
pre-select into a change of its own.</p>
<p><strong>On the Siemens machine-data table the flag is not a field of its own.</strong> That table binds the
property to its tool-change-mode row,
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensMachineDataTable_MdToolChangeMode">MdToolChangeMode</a>: present, the row
decides — a stored zero means the <code>T</code> word changes the tool — and the toggle writes back into it as
0 or 1 rather than into the brand-neutral field; absent, the table behaves like every other brand.
The write is skipped when the effective value already matches, so copying a table never rewrites a
raw machine-data value of 2 down to 1. The same row is visible and editable by number on
<a class="xref" href="native-parameters.html">Parameters (Native)</a>, and the two forms are one storage.</p>
<h2 id="the-rows">The Rows</h2>
<p>The read returns the declarations sorted by code as <strong>text</strong>, case-insensitively, not by numeric
value. <code>M106</code> therefore sorts ahead of <code>M12</code>, and <code>M6</code> lands after <code>M331</code>.</p>
<p>Keys are matched case-insensitively too, so <code>m13</code> and <code>M13</code> are one declaration and cannot both
exist; re-declaring a code in a different spelling replaces its effects and leaves the spelling
already stored on display. The <strong>Code</strong> cell is plain bold text with no editor — a code is changed
by declaring the new one and deleting the old.</p>
<p>Only one brand's default parameter table pre-declares anything. The Siemens machine-data default
seeds six auxiliary codes — <code>M12</code>, <code>M13</code>, <code>M22</code>, <code>M23</code>, <code>M330</code> and <code>M331</code> — each carrying no effect
and the same note, <em>&ldquo;machine-specific auxiliary function (OEM/PLC); the exact behavior depends on
the machine&rdquo;</em>. Those recur often enough in real Siemens programs to be worth silencing with an
explanation rather than a warning, and a machine table that knows their actual effects overrides the
seeded note. On the other four brands the table opens empty.</p>
<h2 id="editing-adding-and-removing">Editing, Adding and Removing</h2>
<p>Every cell commits on its own, and the write is the whole declaration: the handler merges the edited
field into the row and sends all four values, so an edit rewrites the row from what the panel is
currently showing. The checkbox and the two pickers commit on the click or the pick; the note cell
commits through its field's native change event, which is this branch's third text-commit wiring and
is recorded as such in <a class="xref" href="../editing-contract.html">Editing Contract</a> — that page also
carries the optimistic-write, rollback and toast rules these cells share with the rest of the branch.</p>
<p>The footer holds a single field labelled <strong>M-code</strong>, hinted <code>M106</code>, and a <strong>Declare</strong> button. Enter
in that field submits, which no other add-row footer in the branch does. A blank code raises the
toast <em>&quot;M-code must not be empty.&quot;</em> locally and never reaches the server. A successful declare clears
the field and re-reads the whole table, so the new row appears in sort position rather than at the
end.</p>
<p>Removal is the trash button at the end of each row. It carries no confirmation dialog, and the row
leaves the list only after the server has answered — the delete is not optimistic. The removed code
returns to what it was before the declaration: <code>Parsing--Unconsumed</code> wherever a program uses it,
unless a shared or brand syntax already knew the code, in which case it goes back to that built-in
meaning.</p>
<h2 id="what-this-table-does-not-show">What This Table Does Not Show</h2>
<p>The stored map is not the whole of what a machine honours, and three families of M-code behaviour
live outside it.</p>
<p><strong>The canonical ISO codes.</strong> <code>M06</code>, <code>M03</code> / <code>M04</code> / <code>M05</code> and <code>M07</code> / <code>M08</code> / <code>M09</code> are consumed by
the shared syntaxes with no declaration at all; this table exists for the codes those consumers do
not already know. Declaring a canonical code is still legal and still honoured — a note-only
declaration on <code>M08</code>, for instance, consumes the raw <code>M08</code> for its own declaration while a composite
code's flood half continues to emit <code>M08</code>.</p>
<p><strong>A brand's own M-function dialect.</strong> The Heidenhain preset's
<a class="xref" href="../../../../api/Hi.NcParsers.LogicSyntaxs.Heidenhain.HeidenhainMFunctionSyntax.html">HeidenhainMFunctionSyntax</a> owns <code>M126</code> / <code>M127</code>
(shortest-path rotary traverse) and <code>M140</code> (tool-axis retract) as brand meanings, not as
declarations, so none of them is a row here. That preset's syntax list places the expansion <strong>ahead</strong>
of the brand syntax precisely so a machine that redefines one of those codes can say so: a
declaration for <code>M126</code> is expanded and consumed before the brand meaning is reached, and the
declaration wins.</p>
<p><strong>The Siemens tool-change M function.</strong> The Siemens machine-data table's
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensMachineDataTable_MdToolChangeMCode">MdToolChangeMCode</a> row names the
M function that performs a tool change, and the table overlays a tool-change declaration for it onto
the view the parsers resolve — merged into a clone of any explicit declaration on the same code, so a
note or coolant half is kept, and never written into the stored map. <strong>The panel reads the stored
map, so that overlaid row is not on screen.</strong> A Siemens machine whose machine data names a
non-standard trigger honours it in every program while this table lists nothing for it; the value
lives on <a class="xref" href="native-parameters.html">Parameters (Native)</a> as the machine-data row, in
the raw form an operator reads off the real controller. The overlay's own rules matter when the two
views are compared: the raw number is zero-padded to the parsed flag form, so 6 becomes <code>M06</code> and
106 becomes <code>M106</code>; a value that is not a positive whole number overlays nothing; and a code already
declared as a tool change here is left exactly as stored.</p>
<p>The trigger toggle above the table does <strong>not</strong> share that blind spot. It is read through the
property the Siemens table overrides, so it shows the machine data's answer, while the rows beside it
show only what was declared. The asymmetry is the single most useful thing to know about this leaf:
the mode is the effective value, the table is the stored one.</p>
<p>One further consequence of the overlay is invisible in both views. Spindle-only resolution also runs
against the overlaid view, so a code that the machine data names as the tool-change trigger and that
is <em>also</em> declared here with a spindle direction alone stops being spindle-only: it leaves the
in-place resolver and is expanded instead, emitting both the tool change and the direction.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>M-Code Declarations</strong> Node Row — <code>equipment/controller/machine/m-codes</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — <strong>M-Code Declarations</strong> Panel
<ul>
<li>No-Runner Line — <em>&quot;No NC runner — load a project first.&quot;</em>, the branch's shared empty state, shown
while the snapshot reports no runner</li>
<li>No-Table Line — <em>&quot;No controller parameter table on the active runner.&quot;</em>, shown when the read
itself reports no table</li>
<li><strong>T word performs the tool change itself (turret / lathe)</strong> Toggle</li>
<li>Hint Caption — <em>&quot;Declares machine-specific M-codes: a tool-change trigger other than <code>M6</code>,
composite OEM codes (e.g. <code>M13</code> = spindle CW + flood coolant), or known-but-unsimulated codes
(note only, one info message per occurrence). A declaration with nothing set consumes its code
silently. Undeclared codes keep the unconsumed warning.&quot;</em></li>
<li>Declaration Table — dense, flat and bordered, one row per declared code
<ul>
<li>Header Row — <strong>Code</strong>, <strong>Tool change</strong>, <strong>Spindle</strong>, <strong>Coolant</strong>, <strong>Not-simulated note</strong>, and
an unlabelled action column</li>
<li><strong>Code</strong> Cell — bold plain text, not editable</li>
<li><strong>Tool change</strong> CheckBox</li>
<li><strong>Spindle</strong> Select — a dash, <strong>CW (M03)</strong>, <strong>CCW (M04)</strong>, <strong>STOP (M05)</strong></li>
<li><strong>Coolant</strong> Select — a dash, <strong>Mist (M07)</strong>, <strong>Flood (M08)</strong>, <strong>Off (M09)</strong></li>
<li><strong>Not-simulated note</strong> Field — placeholder <em>e.g. chip conveyor forward</em></li>
<li>Delete Button — a trash icon, no confirmation</li>
</ul>
</li>
<li>Add Row Footer
<ul>
<li><strong>M-code</strong> Field — placeholder <code>M106</code>; Enter submits</li>
<li><strong>Declare</strong> Button</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The panel mounts against one node and takes nothing from it: unlike the per-axis leaves it serves a
single tree id, so it reads no role off the node's path.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcMCodesPanel.vue</code> — this panel: the two empty layers,
the trigger toggle, the two option lists with their dash entry, the whole-declaration write behind
every cell, the change-event note cell, the Enter-submitting add footer and the
confirmation-free delete.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line the panel
opens with.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the node id, its label and label key,
the snapshot flag it is grown behind, and the item type that binds it to the panel.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the declaration row and snapshot shapes, the reader that
coerces an empty string to a null field, the whole-declaration writer, the delete, and the trigger
setter.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared runner snapshot the first empty layer
reads.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the node label, the toggle label, the hint, the column
headers, the two option label sets, the note placeholder, the empty-code toast and the two empty
strings quoted above.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the read with its presence flag and its
case-insensitive text ordering, the declare endpoint's direction and coolant validation, the
delete, the trigger write, and the shared helper that answers a missing dependency with an
unsuccessful body rather than an error status.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/IMCodeDeclarationConfig.cs</code> — the declaration contract: the map, the
lookup that returns false for an undeclared code, and the declare and remove mutators whose stored
effects are copied rather than aliased.</li>
<li><code>HiMech/NcParsers/Dependencys/MCodeEffects.cs</code> — the four effects, the empty and spindle-only
predicates that partition the two consumption paths, and the coolant-mode normalizer.</li>
<li><code>HiMech/NcParsers/Dependencys/IToolChangeTriggerConfig.cs</code> — the trigger contract and the
magazine-versus-turret distinction it encodes.</li>
<li><code>HiMech/NcParsers/Dependencys/ISpindleControlConfig.cs</code> — the narrower spindle face over the same
storage, and the resolver the spindle-only rows take.</li>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the storage every brand inherits:
the case-insensitive map, the overridable effective view, the virtual trigger property, and the
XML round trip that writes a spindle-only row as the legacy element.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code> — the two machine-data rows that
change this leaf's meaning: the tool-change mode bound to the trigger property, the tool-change
M function overlaid onto the effective view with its zero-padding rule, and the six auxiliary
codes the default table pre-declares.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/MCodeExpansionSyntax.cs</code> — the rewrite: in-place expansion, the
duplicate and raw-twin guards, the spindle-only skip, and the two diagnostics a declaration can
raise.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/ToolChangeSyntax.cs</code> — the consumer of the expanded tool change and
of the trigger mode, and the term it records for each.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/SpindleSpeedSyntax.cs</code> — the consumer that tries the machine map
before the ISO defaults, which is what lets a spindle-only row remap a canonical code.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/CoolantSyntax.cs</code> — the consumer of the three expanded coolant
flags.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainMFunctionSyntax.cs</code> — the brand meanings a
declaration can pre-empt on that preset.</li>
<li><code>HiMech/NcParsers/InspectionSyntaxs/UnconsumedCheckSyntax.cs</code> — the warning an undeclared code
keeps, and the one a declaration removes.</li>
<li><code>HiMech/NcParsers/Syntaxs/FanucSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SiemensSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SyntecSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/MazakSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/HeidenhainSyntaxUtil.cs</code> — where the expansion sits in each brand's
syntax list, and why the Heidenhain list anchors it earlier than the rest.</li>
<li><code>HiMech/NcParsers/Keywords/Generic/IsoKeywords.cs</code> — the canonical flag spellings the expansion
emits, <code>M06</code> among them.</li>
<li><code>HiMech/NcParsers/Keywords/ICoolantDef.cs</code> — the three coolant constants the picker's values map
to.</li>
<li><code>HiGeom/Numerical/SpindleDirection.cs</code> — the direction enum behind the spindle column.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane this leaf sits on, and which half of a project the
declaration map is written into</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — the flag that grows this node, and why it is never false on
a shipped brand preset</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the fetch, commit, rollback and toast rules this panel
shares with the branch, and the two places it is recorded as departing from them</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,561 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Native Parameters | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Native Parameters | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerNativeParameters">
<h1 id="native-parameters">Native Parameters</h1>
<p>Parameters (Native) is the machine plane's escape hatch: the brand parameter table rendered as the
flat numbered form an operator reads off the real control, with no domain vocabulary and no unit
conversion in between. It lives on the General Setup page at <code>/general-setup</code> under the Control-Tree
id <code>equipment/controller/machine/native</code>, and it is one of the two leaves the <code>hasNativeTable</code>
snapshot flag grows — the other, M-Code Declarations, edits a different part of the same object.
Every value the domain-grouped leaves beside it edit <em>that is a row of the brand parameter table</em> is
a row here; the ones stored elsewhere — the tool-change pose, the block-skip layers, the subprogram
folders and the indexing position lists among them — reach no native form at all. Several rows here
reach no domain-grouped face.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<p>The leaf is pushed behind <code>hasNativeTable</code>, which reports whether any
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a> resolves in the runner's effective
dependency list. All five shipped presets carry one, so the flag is a gate in code and unconditional
in practice — see <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<h2 id="how-a-parameter-is-addressed">How a Parameter Is Addressed</h2>
<p>The table stores its numbers in three dictionaries, and the panel is those three dictionaries with a
heading over each:</p>
<table>
<thead>
<tr>
<th>Section heading the user reads</th>
<th>Backing storage</th>
<th>Cell</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>System parameters</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html#Hi_NcParsers_Dependencys_ControllerParameterTableBase_SystemParams">SystemParams</a></td>
<td>one value per id</td>
</tr>
<tr>
<td><strong>Axis parameters</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html#Hi_NcParsers_Dependencys_ControllerParameterTableBase_AxisParams">AxisParams</a></td>
<td>one floating-point value per id <strong>per axis</strong></td>
</tr>
<tr>
<td><strong>Axis parameters (integer)</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html#Hi_NcParsers_Dependencys_ControllerParameterTableBase_IntAxisParams">IntAxisParams</a></td>
<td>one integer value per id <strong>per axis</strong></td>
</tr>
</tbody>
</table>
<p>An id is a bare number in storage. What the panel prints in front of it is the brand's prefix, and
that prefix is the whole of the addressing convention:</p>
<table>
<thead>
<tr>
<th>Brand marker</th>
<th>Prefix shown</th>
<th>Table that answers</th>
<th>Example row</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc</td>
<td><code>#</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a></td>
<td><code>#1420</code> rapid traverse rate</td>
</tr>
<tr>
<td>Mazak</td>
<td><code>#</code></td>
<td>the <strong>Fanuc</strong> table</td>
<td><code>#1420</code> rapid traverse rate</td>
</tr>
<tr>
<td>Syntec</td>
<td><code>Pr</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a></td>
<td><code>Pr1420</code> rapid traverse rate</td>
</tr>
<tr>
<td>Siemens</td>
<td><code>MD</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html">SiemensMachineDataTable</a></td>
<td><code>MD32000</code> max axis velocity</td>
</tr>
<tr>
<td>Heidenhain</td>
<td><code>MP</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Heidenhain.HeidenhainParameterTable.html">HeidenhainParameterTable</a></td>
<td><code>MP1010</code> rapid traverse rate</td>
</tr>
</tbody>
</table>
<p><strong>Mazak borrows the Fanuc table outright.</strong> The Mazak preset carries the Fanuc parameter-table proxy
rather than one of its own, so the prefix resolves to <code>#</code> and every number on this leaf is a Fanuc
parameter number under a <code>Mazak</code> brand badge. The prefix is not taken from the brand marker string
but from the type of the parameter table that resolved, so the two cannot drift apart the way a
brand-marker label can — see <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>A <code>ControllerParameterTableBase</code> subclass outside those four resolves an empty prefix, and the ids
then render bare. No shipped preset produces that.</p>
<p>The prefix the panel renders comes from the leaf's <strong>own</strong> read of the parameter form. The shared
runner snapshot carries a prefix field of its own, and nothing renders it.</p>
<h3 id="the-caption-under-the-id">The caption under the id</h3>
<p>Each id cell carries the number in bold and, beneath it, a short usage caption — <em>Rapid traverse rate
(mm/min or deg/min)</em>, <em>Axis type (0 linear / 1 rotary / 2 spindle)</em>, <em>G54 X offset</em>, <em>Tool change M
function code</em>. The same text repeats as the cell's hover title.</p>
<p>The caption is the brand table's own description of a well-known number, produced by
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html#Hi_NcParsers_Dependencys_ControllerParameterTableBase_DescribeSystemParam_">DescribeSystemParam</a><small>(API)</small>
and its two per-axis counterparts, and it travels down the wire with the row. It is therefore
<strong>engine text, not an interface string</strong>: it is written in English in the brand table and belongs to
no locale bundle, so it reads the same under every language the application offers. An id the brand
table does not model has no caption at all, which is how a modelled row and a free extra are told
apart on screen.</p>
<h2 id="raw-values-raw-units">Raw Values, Raw Units</h2>
<p>The caption at the top of the panel states the rule: <em>&quot;Raw stored values in native units — no unit
conversion is applied here. The interface-form nodes edit the same backing table.&quot;</em></p>
<p>No cell carries a unit suffix, and none can: the unit belongs to the number, not to the column. What
a cell means is whatever the brand stores at that address — millimetres or degrees on a stroke limit,
mm/min or deg/min on a rapid rate, rpm on a spindle limit, an enumeration ordinal on an axis type, a
count on a controlled-axis parameter.</p>
<p><strong>The peck clearance is the case where the two forms visibly disagree, and it ships.</strong> Canned Cycle
(Peck) always speaks millimetres; the Fanuc-family table stores millimetres at <code>#4002</code>, and the
Syntec table stores <strong>microns</strong> at <code>Pr4002</code>. A machine at a 5 mm clearance therefore reads <code>5</code> on
<a class="xref" href="interface-parameters.html">Interface Parameters</a> on every brand, and reads <code>5</code> here on Fanuc
and Mazak but <code>5000</code> here on Syntec. The write endpoint converts on the way in; this leaf does not,
which is the point of it — an operator copying from a real Syntec control reads the micron form.</p>
<p>Two further asymmetries follow from where a value is stored rather than from its unit:</p>
<ul>
<li><strong>On Siemens and Heidenhain the peck clearance has no row here at all.</strong> Those two presets supply
it through a generic fallback config, a plain runner entry rather than a parameter table, so the
interface leaf shows a field whose backing has no place in this form.</li>
<li><strong>On the Fanuc-family and Syntec tables the work-coordinate offsets are rows of this table.</strong> The
G54–G59 triads start at <code>#5221</code> and the G54.1 P1–P48 triads at <code>#7001</code>, both on a stride of 20, all
in the System parameters section. That mixing is why the machine plane's parameter table is stored
on the project rather than on the runner — see
<a class="xref" href="index.html">Machine and Controller Plane</a>. On the Fanuc-family table those two
address ranges are also what an NC program's <code>#</code>-variable read resolves against, so those rows are
live in both directions.</li>
</ul>
<h2 id="what-only-this-leaf-reaches">What Only This Leaf Reaches</h2>
<p>Most rows have a domain-grouped twin. These do not.</p>
<p><strong>The axis roster itself.</strong> Every per-axis leaf of the branch — Machine Limits (Stroke), Rapid
Feedrates, Home / G28 Reference, Tool-Change Position — lists the axes named by the <strong>axis-type row</strong>
of this table (<code>#1006</code>, <code>Pr1006</code>, <code>MD30300</code>, <code>MP400</code>), because
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html#Hi_NcParsers_Dependencys_ControllerParameterTableBase_AxisNames">AxisNames</a> is that row's key set. None of
those leaves can add an axis, rename one, remove one, or change one from linear to rotary; their
columns are handed to them. Inside the Controller branch the axis-type row is editable here and
nowhere else, through the footer with the section set to <strong>Axis (integer)</strong>, the brand's axis-type
number as the id, an axis name, and a value of <code>0</code> linear, <code>1</code> rotary or <code>2</code> spindle. The one other
writer is outside the branch entirely:
<a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_ConfigureByMachiningChain_">ConfigureByMachiningChain</a><small>(API)</small> stamps the machining
chain's axis codes into the same row when a machine tool is attached to the project; an axis the
chain does not name stays in the row. That call does not stop at the axis-type row: a chain axis it
marks rotary also has its reference position set to <code>0</code> and its rapid rate to <code>36000</code> deg/min,
whatever those two Axis parameters rows held.</p>
<p>The inverse holds, with one exception. Deleting the axis-type row leaves the table with no named
axes, so Machine Limits (Stroke), Rapid Feedrates and Home / G28 Reference have no rows left to
draw. Tool-Change Position keeps its own: its reader falls back to the tool-change configuration's
own axis keys when the axis set is empty, and every shipped preset seeds that configuration with X,
Y and Z. Rows recovered that way carry no axis type and are reported linear.</p>
<p><strong>The Siemens indexing machine data, all four numbers.</strong> <code>MD30500</code>, the per-axis assignment that
decides which axis consumes which position table, is <em>shown</em> on
<a class="xref" href="indexing-positions.html">Indexing Position Tables</a> as display-only text and is
written here as an <strong>Axis parameters (integer)</strong> row; the REST surface carries no assignment writer
at all. The equidistant definition — the numerator <code>MD30501</code> and the offset <code>MD30503</code> as axis
parameters, the denominator <code>MD30502</code> as an integer axis parameter — has no endpoint that reads or
writes it and no panel that offers a cell for it: on the web surface it is three ordinary rows on
this leaf. The engine is its reader: an axis whose <code>MD30500</code> is 3 resolves its station spacing from
the trio, and Indexing Position Tables prints that axis' assignment as <em>Equidistant
(MD30501–MD30503)</em>, naming the three numbers it offers no cell for. The two position lists
themselves are the mirror image, stored as list properties outside all three dictionaries and so
absent from this form entirely.</p>
<p><strong>Numbers with no interface field.</strong> The controlled-axis count (<code>#1020</code>, <code>Pr1020</code>) is read by nothing
in the web service. The Siemens fixed-point position <code>MD30600</code>, the G75 target, likewise. So is the
Siemens tool-change M function <code>MD22560</code>, whose presence overlays a tool-change effect onto the named
code in the declaration view the parsers resolve against. M-Code Declarations renders the stored map
rather than that view, so the overlaid row is on no screen at all: the effect is honoured in every
program the machine runs, and the one number that shows anywhere is this row. The Siemens tool-change
mode <code>MD22550</code> is a half case: where the row already exists, the <em>T word performs the tool change
itself</em> toggle on M-Code Declarations writes it, and where it does not, that toggle writes a
brand-neutral flag instead; creating the row is possible only here.</p>
<p><strong>Any number the model does not name.</strong> The three writers are get-or-create, so an id outside the
brand's well-known set is accepted, stored, serialized into the project file and read back. It
carries no usage caption, and the engine consumes only the numbers its brand table models — a free
extra is a record, not a setting.</p>
<h2 id="a-fractional-number-has-three-fates">A Fractional Number Has Three Fates</h2>
<p>The <strong>Axis parameters (integer)</strong> section is the one place on the branch where a value can be typed,
accepted by the field, and silently discarded.</p>
<p>The cell is the shared numeric widget, which commits on blur or Enter and carries no integer rule.
Typing <code>2.5</code> parses cleanly: the widget clears its own error state, emits <code>2.5</code> to the panel, and
rewrites its text to the parsed form. The panel's integer handler is where the value stops — it
returns without assigning and without raising anything when the number is not whole. Because the
bound value never changed, nothing pushes the stored integer back into the box. <strong>The cell goes on
reading <code>2.5</code> over a table that still holds the old integer</strong>, with no error message, no toast and no
console line. The row corrects itself only when the panel refetches: after an add, after a removal,
or on a fresh mount when the selection leaves this leaf and comes back.</p>
<p>The footer does not behave the same way. <strong>Add / Set</strong> with <strong>Axis (integer)</strong> selected <em>truncates</em> a
fractional value toward zero and writes the truncation, so the same <code>2.5</code> becomes a stored <code>2</code>; and
the same button refuses a fractional <strong>Parameter id</strong> outright, with the message <em>&quot;Enter a
non-negative integer id and a value.&quot;</em> That refusal belongs to the button, not to the box — the
<strong>Parameter id</strong> field is the shared numeric widget with a minimum of 0 and no integer rule, so it
accepts and keeps <code>2.5</code> the way any other cell does, and the one message answers an empty <strong>Value
(raw)</strong> as well. Three controls, one number, three outcomes.</p>
<p><strong>Clearing any cell ends the same way for a different reason.</strong> An emptied field commits <code>null</code>, and
all three handlers return early on <code>null</code>, so the box is blank and the stored value is untouched.
There is no way to unset one cell of a table; the value stands until it is overwritten or its whole
row is removed. The general rule for a box that disagrees with its model is
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>; what is particular here is that the widget did nothing
wrong — the panel accepted the commit and dropped it.</p>
<h2 id="adding-overwriting-and-removing-a-row">Adding, Overwriting and Removing a Row</h2>
<p>The footer is one control set for both adding and overwriting, and the button says so: <strong>Add / Set</strong>.
The write is get-or-create at every level — an unknown id creates its row, an unknown axis name
creates its column — so setting an existing cell and creating a new one are the same request.</p>
<ul>
<li>The <strong>Axis</strong> field appears only while the section is not <em>System</em>, is free text, and is trimmed and
checked for emptiness only. It is not validated against the machine's axes: any name is accepted
and becomes a new column.</li>
<li>A successful add re-reads the whole form. The footer's own fields are <strong>not</strong> cleared, and Enter
does not submit — see <a class="xref" href="../editing-contract.html">Editing Contract</a> for how the
branch's add footers differ from one another.</li>
<li>The two client-side refusals are the id, which must be a non-negative whole number, and, for the
two per-axis sections, a non-empty axis name. Each raises a negative toast and sends nothing.</li>
</ul>
<p><strong>Delete is per row, not per cell.</strong> The trailing button on a row of either per-axis table removes
the id from that dictionary entirely — every axis column of it at once, not the cell it sits beside.
A confirmation dialog comes first, titled <em>Remove parameter</em> and reading <em>Remove <code>&lt;prefixed id&gt;</code> from
the <code>&lt;section&gt;</code> section?</em>, where the section word is the storage kind — <em>system</em>, <em>axis</em>, <em>int-axis</em> —
rather than the heading printed above the table. The removal is not optimistic: the request is
awaited and the whole form is re-read.</p>
<p><strong>A deleted well-known row can come back on its own.</strong> The brand tables' modelled accessors are
get-or-create with a default, so a read through a sibling leaf re-materializes the row it wants. The
clearest case is the maximum spindle speed: delete <code>#3741</code> here, open Interface Parameters, and that
read stores the brand's default back — 60000 on Fanuc, Mazak, Siemens and Heidenhain, 24000 on
Syntec — so the row reappears at that value. The cutter-compensation type, the tool-axis direction
and the peck clearance behave the same way. A free extra id has no accessor and stays deleted.</p>
<h2 id="what-a-freshly-switched-brand-shows">What a Freshly Switched Brand Shows</h2>
<p>The seed each brand's proxy clones decides what the three tables open with. All five declare a linear
X, Y and Z in the axis-type row, a reference position of zero per axis and a rapid rate per axis, so
on every brand the Axis parameters section opens with two rows and the Axis parameters (integer)
section with one. The System section is where they part company.</p>
<table>
<thead>
<tr>
<th>Brand</th>
<th>System parameters</th>
<th>Axis parameters</th>
<th>Axis parameters (integer)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc, Mazak</td>
<td>166 rows — controlled axes, max spindle speed, peck retraction, cutter-comp type, and 162 seeded work-offset addresses at zero</td>
<td>reference position, rapid rate</td>
<td>axis type</td>
</tr>
<tr>
<td>Syntec</td>
<td>166 rows — the same four under <code>Pr</code> numbering, the peck retraction in microns, and the same 162 offsets</td>
<td>reference position, rapid rate</td>
<td>axis type</td>
</tr>
<tr>
<td>Siemens</td>
<td>1 row — max spindle speed</td>
<td>reference position, max axis velocity</td>
<td>axis type</td>
</tr>
<tr>
<td>Heidenhain</td>
<td>2 rows — max spindle speed, tool-axis direction</td>
<td>reference position, rapid rate</td>
<td>axis type</td>
</tr>
</tbody>
</table>
<p>The 162 offset rows are
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IsoCoordinateAddressMap.html#Hi_NcParsers_Dependencys_IsoCoordinateAddressMap_SeedAllDefaults_">SeedAllDefaults</a><small>(API)</small> writing
every G54–G59 and G54.1 P1–P48 triad as zero, so that a managed address always has a value the way a
real control with a fresh battery reads zero rather than nothing. They dominate the System section on
the three brands that carry them, and their captions — <em>G54 X offset</em>, <em>G54.1P12 Z offset</em> — are what
separates them from machine configuration in the same list.</p>
<p>No brand seeds a stroke limit, so the positive and negative stroke-limit rows are absent from the
Axis parameters section until Machine Limits (Stroke) or this leaf creates them.</p>
<h2 id="the-two-empty-layers-and-which-one-is-reachable">The Two Empty Layers, and Which One Is Reachable</h2>
<p>The panel opens through the branch's shared two-layer gate. The first layer is the shared <em>&quot;No NC
runner — load a project first.&quot;</em> from the runner snapshot; the second is <em>&quot;No controller parameter
table on the active runner.&quot;</em> from the leaf's own read — the same line M-Code Declarations and
Interface Parameters show, and honest in all three, because every one of those reads reports presence
from the same parameter-table lookup.</p>
<p>Neither line is normally reached by navigating, because the node itself exists only while the
snapshot reported a parameter table. The second line is also what a failed read leaves on screen: the
presence flag starts false and the failure path only raises a toast. A failed write answers inside a
success envelope — HTTP 200 carrying <code>No ControllerParameterTableBase on the active runner</code> — and
surfaces as one negative toast, three seconds, the panel's localized context followed by that raw
English sentence. The full rule is <a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>Parameters (Native)</strong> Node Row — <code>equipment/controller/machine/native</code>, the last leaf of the
plane, after <strong>Indexing Position Tables</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of the selected node
<ul>
<li><strong>Parameters (Native)</strong> Panel — one column, gutter-spaced
<ul>
<li>Hint Caption — <em>&quot;Raw stored values in native units — no unit conversion is applied here. The
interface-form nodes edit the same backing table.&quot;</em></li>
<li><strong>System parameters</strong> Section Heading</li>
<li>System Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Id</strong>, <strong>Value</strong>, and an unlabelled action column</li>
<li>Data Row — the prefixed id in bold over its usage caption (absent on a free extra), a numeric
cell, and a delete button</li>
</ul>
</li>
<li><strong>Axis parameters</strong> Section Heading</li>
<li>Axis Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Id</strong>, then one column per axis name, then an unlabelled action column</li>
<li>Data Row — the prefixed id in bold over its usage caption, one numeric cell per axis column
(blank where the row has no entry for that axis), and a delete button that removes the whole
id</li>
</ul>
</li>
<li><strong>Axis parameters (integer)</strong> Section Heading</li>
<li>Integer Axis Table — the same shape as the Axis Table, over the integer dictionary</li>
<li>Separator</li>
<li>Add Row — one line, bottom-aligned
<ul>
<li><strong>Section</strong> Select — <em>System</em> / <em>Axis (double)</em> / <em>Axis (integer)</em></li>
<li><strong>Parameter id</strong> Numeric Field — minimum 0</li>
<li><strong>Axis</strong> Text Field — present only while the section is not <em>System</em></li>
<li><strong>Value (raw)</strong> Numeric Field</li>
<li><strong>Add / Set</strong> Button — primary</li>
</ul>
</li>
<li>No-Table Body — replaces everything above: <em>&quot;No controller parameter table on the active
runner.&quot;</em></li>
<li>Shared Empty State — replaces the whole body while the snapshot reports no runner: <em>&quot;No NC
runner — load a project first.&quot;</em></li>
</ul>
</li>
<li>Remove Dialog — <em>Remove parameter</em> over <em>Remove <code>&lt;prefixed id&gt;</code> from the <code>&lt;section&gt;</code> section?</em>,
with a cancel</li>
<li>Toast — negative, three seconds, the panel's context followed by the server's own message</li>
</ul>
</li>
</ul>
<p>The axis columns of the two per-axis tables are computed independently of each other, and each is the
union of the shared snapshot's axis names with every axis key present in that table's own rows. An
axis created here therefore gets its column from the rows even before the shared snapshot is next
refreshed, and an axis the snapshot knows about with no entry in a row shows an empty cell rather
than being omitted.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcNativeParamsPanel.vue</code> — this panel: the three tables
over the three dictionaries, the prefixed id column with its usage caption, the axis-column union,
the three commit handlers with the integer test that drops a fractional value, the
confirm-then-remove flow, and the add footer with its truncation and its two refusals.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the node's id, label and label key,
and the snapshot flag it is pushed behind, last on the machine plane.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the reader that parses the prefix and the three row
families, the three per-cell writers and the three per-id removals, and the snapshot field carrying
a prefix this panel does not use.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the first empty layer and the
axis-column union read.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the cell: commit on blur or Enter, the
parsed value written back into the box, and the absence of any integer rule.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcIndexingPositionsPanel.vue</code> — the sibling that
displays the MD30500 assignment this leaf writes, and points here for it.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the shared fetch helper that turns both a non-2xx status and a
<code>success: false</code> body into one thrown error.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the node label, the raw-units hint, the three section
headings, the three add-footer section options, the two footer refusals and the five error
contexts — one read and four writes, the two per-axis savers sharing a context.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Id</strong>, <strong>Value</strong>, <strong>Axis</strong> and <strong>Add / Set</strong>
strings.</li>
<li><code>wwwroot-src/src/i18n/en/dialog.ts</code> — the remove dialog's title, its message, and the three storage
kind words it interpolates.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the read that orders each dictionary by id and
attaches the brand's usage description to every row, the prefix computed from the resolved table's
type, the three get-or-create writers and the three whole-row removals, the shared dependency
wrapper that answers a missing table inside a success envelope, and the neighbouring readers this
form's rows also feed — the three per-axis reads that take the axis-type row's key set bare, the
tool-change read that falls back to its own configuration's keys when that set is empty, and the
M-code read bound to the stored declaration map rather than the overlaid view.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the three dictionaries this leaf
is, the get-or-create bucket accessors, the role interfaces that let the domain-grouped leaves read
the same rows, the axis-name set taken from the axis-type row, the three usage-description methods,
the rotary-axis helper that writes type, reference position and rapid rate in one call, and the XML
round-trip that persists every row including free extras.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTable.cs</code> — the <code>#</code> vocabulary: the well-known
numbers, the get-or-create defaults behind the modelled accessors, the macro-variable lookup over
the work-offset address ranges, and the seed a fresh Fanuc or Mazak runner opens on.</li>
<li><code>HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTable.cs</code> — the <code>Pr</code> vocabulary, and the peck
clearance stored in microns with the conversion kept in its accessor.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code> — the <code>MD</code> vocabulary: the
indexing assignment, the three equidistant numbers this leaf alone edits and the spacing
resolution that reads them, the tool-change mode, the tool-change M function with the read-time
declaration overlay that never enters the stored map, the fixed-point position, and the seed that
declares one system row.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs</code> — the <code>MP</code> vocabulary and its
two-row seed.</li>
<li><code>HiMech/NcParsers/Dependencys/IsoCoordinateAddressMap.cs</code> — the work-offset address scheme shared by
the Fanuc and Syntec tables, the per-address description that captions those rows, and the seeding
of every triad to zero.</li>
<li><code>HiMech/NcParsers/Dependencys/IMachineAxisConfig.cs</code> — the axis contract, and the axis-type
enumeration a cell of the integer section stores.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolingMcConfig.cs</code> — the tool-change pose, stored outside
the three dictionaries, whose own X / Y / Z keys are what Tool-Change Position falls back to when
the axis-type row is gone.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets and which parameter-table proxy each
carries, the Fanuc proxy inside the Mazak preset among them, the tool-change configuration every
preset seeds, and the chain configuration that is the only other writer of the axis-type row and,
on a rotary chain axis, of that axis' reference position and rapid rate.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane this leaf sits on, and why the table it exposes is
stored on the project rather than on the runner file</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — the flag that grows this node, why it never reports false on
a shipped preset, and why Mazak resolves the Fanuc table</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the commit, rollback, confirm and toast rules this panel
shares with the branch, and the add-footer details it differs on</li>
<li><a class="xref" href="interface-parameters.html">Interface Parameters</a> — the same values in domain vocabulary, including the peck
clearance this leaf shows in the brand's own unit</li>
<li><a class="xref" href="indexing-positions.html">Indexing Position Tables</a> — the leaf that displays the MD30500 assignment and the
equidistant definition that are written here</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,580 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Per-Axis Tables | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Per-Axis Tables | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerAxisTables">
<h1 id="per-axis-tables">Per-Axis Tables</h1>
<p>Four leaves of the Controller branch's machine plane are one table each, keyed on the machine's
axes: <strong>Machine Limits (Stroke)</strong>, <strong>Rapid Feedrates</strong>, <strong>Home / G28 Reference</strong> and <strong>Tool-Change
Position</strong>. They live on the General Setup page at <code>/general-setup</code> under the Control-Tree ids
<code>equipment/controller/machine/limits</code>, <code>equipment/controller/machine/rapid</code>,
<code>equipment/controller/machine/home</code> and <code>equipment/controller/machine/tool-change</code>; none of them is
gated by a snapshot flag, so every brand grows all four. Two components serve them — one panel
behind the first three, discriminating on the id's last segment, and a second behind Tool-Change
Position — and the differences between those two components are what this page is mostly about.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<h2 id="where-the-rows-come-from">Where the Rows Come From</h2>
<p>Every row on all four leaves is one axis of the runner's machine-axis configuration, read as
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IMachineAxisConfig.html#Hi_NcParsers_Dependencys_IMachineAxisConfig_AxisNames">AxisNames</a> through the proxy-resolved dependency
list. On all five brand presets that configuration is the brand parameter table itself:
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a> declares
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IMachineAxisConfig.html">IMachineAxisConfig</a> alongside
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IStrokeLimitConfig.html">IStrokeLimitConfig</a>,
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IRapidFeedrateConfig.html">IRapidFeedrateConfig</a> and
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IHomeMcConfig.html">IHomeMcConfig</a>, so one object supplies both the row set and three of
the four value sets. Which brand carries which table is
<a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>The table's axis set is a single parameter row: the per-axis integer bucket under the brand's
axis-type number, whose keys are the axis names and whose values are the
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.AxisType.html">AxisType</a> — linear, rotary or spindle. Adding an axis therefore means
adding a key to that one row, and exactly one surface on this branch reaches it.</p>
<ul>
<li>Neither panel here has an add-row or delete-row control. Both render a fixed header over one
<code>&lt;tr&gt;</code> per axis and nothing else.</li>
<li>The four per-axis routes behind them write one axis' value and nothing more: the caller names an
axis, and the endpoint writes into the stroke-limit, rapid-rate, home or tool-change store under
that name. None of them touches the axis-type row, so none of them changes which rows the next
read returns.</li>
<li><a class="xref" href="native-parameters.html">Parameters (Native)</a> edits the axis-type row itself, and
is the one place on the branch that widens the axis set: its <strong>Add / Set</strong> footer takes a
free-text axis name, so the section <em>Axis (integer)</em>, the brand's axis-type number, a name and a
value of <code>0</code> linear, <code>1</code> rotary or <code>2</code> spindle adds an axis, which then appears on all four
leaves. Its grid cannot — the per-axis columns are the union of the axis names already present, so
a cell can be written only for an axis that exists — and its delete button removes a whole
parameter row, every axis cell of one number at once, rather than one axis.</li>
</ul>
<p>Axes arrive from the machining chain. Whenever the active runner suit or the chain changes, the
project service re-binds the suit and calls
<a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_ConfigureByMachiningChain_">ConfigureByMachiningChain</a><small>(API)</small>, which walks the
chain's axis codes and their transformers: a rotating transformer writes the axis as rotary — and,
on a brand parameter table, writes its reference position as 0 deg and its rapid rate as 36000
deg/min — any other transformer writes the axis as linear, and every chain axis whose reference
position is still unset is seeded to 0. That is the mechanism behind the description above the
Home / G28 Reference table, <em>&quot;Axes without a value are seeded to 0 when a machine tool attaches.&quot;</em></p>
<p><strong>The stamp adds rows and never removes one.</strong> The generic axis configuration carried by the CSV and
CL runner suits is cleared before the walk; a brand parameter table is not, and keeps every axis it
already held. An axis dropped from the chain therefore keeps its rows on all four leaves, and no
surface removes it individually. The three-axis default table every brand preset seeds is why a
project with no machine tool attached still shows X, Y and Z.</p>
<p>Two <strong>values</strong> are the exception, and only on a rotary chain axis of a brand table: that branch of
the walk is unconditional, so it rewrites the axis' reference position to 0 deg and its rapid rate
to 36000 deg/min whether or not one was stored. The walk re-runs on every suit re-bind, so an edited
Home Position or Rapid Rate on a rotary axis is back at those two numbers once the chain or the
active suit changes. A linear axis' values, and every value on an axis the chain does not carry, are
left untouched.</p>
<p>The caption below the first three tables records the row half of that: <em>&quot;Axis rows follow the Machine
Tool chain; they cannot be added here.&quot;</em> Where the chain itself is edited is
<a class="xref" href="../../machine-tool.html">Machine Tool</a>.</p>
<h2 id="the-value-columns">The Value Columns</h2>
<p>Each leaf renders the axis column, its own value column or columns, and a unit column.</p>
<table>
<thead>
<tr>
<th>Leaf</th>
<th>Value columns</th>
<th>Empty cell means</th>
</tr>
</thead>
<tbody>
<tr>
<td>Machine Limits (Stroke)</td>
<td><strong>+ Limit</strong>, <strong>− Limit</strong></td>
<td>no limit set for that axis and side</td>
</tr>
<tr>
<td>Rapid Feedrates</td>
<td><strong>Rapid Rate</strong></td>
<td>never empty — see below</td>
</tr>
<tr>
<td>Home / G28 Reference</td>
<td><strong>Home Position</strong></td>
<td>no reference position stored</td>
</tr>
<tr>
<td>Tool-Change Position</td>
<td><strong>Stays put</strong>, <strong>Position</strong></td>
<td>see <a href="#stays-put-and-the-sentinel-behind-it">Stays Put</a></td>
</tr>
</tbody>
</table>
<p>The stroke-limit and home readers return an optional value and hand null straight through, so an
unset cell is blank. The rapid reader does not: it returns the stored rate, or — when the axis has
no rate of its own — the base class's fixed default, 20000 mm/min for a linear axis and 36000
deg/min for a rotary one. So a Rapid Rate cell is always a number, and a number in it is not proof
that the axis has a row.</p>
<p>A table cloned fresh from a brand's preset seed decides what the three leaves then open on, and all
four brand defaults agree in shape: a linear X, Y and Z, a reference position of 0 on each, and a
rapid rate per axis — and no stroke limit at all. Machine Limits therefore opens entirely blank on
every brand, while Rapid Feedrates and Home / G28 Reference open populated. Whether a brand switch
clones a fresh table or keeps the project's own is
<a class="xref" href="index.html">Machine and Controller Plane</a>.</p>
<h2 id="the-unit-column">The Unit Column</h2>
<p>The unit column's header is the translated label <em>Unit</em>; its cell text is not translated and does
not come from the server. Each panel computes it in the browser from the row's rotary flag, using
literal strings:</p>
<table>
<thead>
<tr>
<th>Leaf</th>
<th>Linear axis</th>
<th>Rotary axis</th>
</tr>
</thead>
<tbody>
<tr>
<td>Machine Limits (Stroke)</td>
<td><code>mm</code></td>
<td><code>deg</code></td>
</tr>
<tr>
<td>Rapid Feedrates</td>
<td><code>mm/min</code></td>
<td><code>deg/min</code></td>
</tr>
<tr>
<td>Home / G28 Reference</td>
<td><code>mm</code></td>
<td><code>deg</code></td>
</tr>
<tr>
<td>Tool-Change Position</td>
<td><code>mm</code></td>
<td><code>deg</code></td>
</tr>
</tbody>
</table>
<p>The flag itself is server-side, and it is the axis type rather than a separate setting: an axis
stored as rotary <strong>or as spindle</strong> reports rotary, so a spindle-mode axis is labelled in degrees.</p>
<p><strong>The unit is a label, not a conversion.</strong> The base class writes a linear and a rotary rapid rate
into the same per-axis cell and reads them back through the same lookup, differing only in the
default returned when the cell is absent; the stroke-limit and home accessors are equally unit-blind.
Nothing on these three leaves converts, so their numbers are the raw stored numbers, and the same
values appear in <a class="xref" href="native-parameters.html">Parameters (Native)</a> under the brand's own
parameter number:</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>Fanuc, Mazak</th>
<th>Syntec</th>
<th>Siemens</th>
<th>Heidenhain</th>
</tr>
</thead>
<tbody>
<tr>
<td>Axis type — the row set</td>
<td>1006</td>
<td>1006</td>
<td>30300</td>
<td>400</td>
</tr>
<tr>
<td>Reference position (home)</td>
<td>1240</td>
<td>1240</td>
<td>34010</td>
<td>410</td>
</tr>
<tr>
<td>Positive stroke limit</td>
<td>1300</td>
<td>1300</td>
<td>36100</td>
<td>420</td>
</tr>
<tr>
<td>Negative stroke limit</td>
<td>1320</td>
<td>1320</td>
<td>36110</td>
<td>430</td>
</tr>
<tr>
<td>Rapid rate</td>
<td>1420</td>
<td>1420</td>
<td>32000</td>
<td>1010</td>
</tr>
</tbody>
</table>
<p>Mazak shares the Fanuc column because the Mazak preset carries the Fanuc parameter-table proxy;
Siemens spends its rapid-rate role on the max-axis-velocity machine datum.</p>
<p>Tool-Change Position is the exception on both counts: its values live on
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolingMcConfig.html">ToolingMcConfig</a>, a plain runner-owned dependency rather than
a parameter table, so they appear in no native form at all and a brand apply returns them to the
preset defaults — see <a class="xref" href="index.html">Machine and Controller Plane</a>.</p>
<h2 id="stays-put-and-the-sentinel-behind-it">Stays Put, and the Sentinel Behind It</h2>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IToolingMcConfig.html">IToolingMcConfig</a> stores one number per axis, and the contract gives
one number a second meaning: <strong>NaN means the axis stays where it is</strong> during a tool change. The read
splits that back into two fields — a stored NaN becomes the <em>Stays put</em> flag with no position,
anything else becomes the position — and the panel renders the flag as a checkbox and the position
as a numeric field disabled while the box is ticked.</p>
<p>Writing goes the same way round. Ticking the box sends the stay flag and the reader writes NaN.
Unticking it sends a position, and because the panel clears its own cell when the box is ticked, the
position it sends on the way back is 0 — so unticking parks the axis at machine zero rather than
restoring what was there before.</p>
<p>A <strong>third</strong> state exists and is not the sentinel: an axis with no entry in the map at all. Its box is
unticked and its Position cell blank, because the reader reports no position and no stay. At run
time the two are indistinguishable — the tool-change motion overlays only those axes that carry a
number and are not NaN onto the current pose, and leaves every other axis alone. So the machine
stays put either way; the tick mark distinguishes only how that was recorded.</p>
<p>The overlay has a vocabulary of its own, narrower than the table. X, Y and Z always take part; every
other axis takes part only while the machine declares it rotary. A linear axis outside those three
is stored, shown and editable on this leaf, and never moved.</p>
<p>The preset default is exactly this mixture: X and Y carry the sentinel, Z carries 0, and no rotary
axis carries an entry, which is why a rotary axis usually opens unticked and blank.</p>
<p>Above the table sits <strong>Tool-change mechanism time</strong>, a single field in seconds bound to
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IToolingMcConfig.html#Hi_NcParsers_Dependencys_IToolingMcConfig_ToolingTime">ToolingTime</a>. It becomes the duration of the tool
change step the runner emits, and it is the changer mechanism alone — the axis travel to and from
the position is timed separately from the positions in the table. It is 0 on every brand preset, and
it is written into the runner file only when non-zero.</p>
<h2 id="one-reader-two-readings-of-its-presence-flag">One Reader, Two Readings of Its Presence Flag</h2>
<p>All four reads answer the same shape: a <code>present</code> flag plus one row per axis. <code>present</code> reports
whether the backing configuration resolved — stroke limit, rapid feedrate, home or tool change — and
it is computed <strong>independently of the rows</strong>. Each reader builds the row list from the axis set
whether or not that configuration resolved, and fills the value cells from an optional reference, so
a missing configuration yields blank limit and home cells, a 0 rapid rate, and rows all the same.</p>
<p>The two components read the answer differently, and this is the sharpest divergence between them.</p>
<ul>
<li>The <strong>axis-table panel discards <code>present</code> entirely.</strong> Its typed reader parses the flag; the panel
assigns only the rows. Its second empty layer keys on the row count instead: with no axes it shows
<em>&quot;No machine axes yet — these rows are driven by the Machine Tool chain (MechBuilder). Attach a
machine tool to edit per-axis values.&quot;</em>, and with axes it renders the table whatever the flag said.</li>
<li>The <strong>tool-change panel reads <code>present</code></strong> and, when it is false, shows <em>&quot;No tool-change config on
the active runner.&quot;</em></li>
</ul>
<p>What that costs: a runner that has axes but no stroke-limit, rapid-feedrate or home configuration
renders a full, editable table of blank or zero cells, and every commit fails with a toast naming the
missing dependency instead of the panel saying the table is absent. On the five shipped brand presets
the state cannot arise, because the one object that supplies the axis set supplies all three
configurations too. The reachable case is the mirror one, and it reaches the two panels differently:
deleting the axis-type parameter row from Parameters (Native) empties the axis set, and the three
axis-table leaves fall to the no-axes line while <strong>Tool-Change Position keeps its rows</strong>, because its
reader falls back to the tool-change configuration's own axis keys when the axis set is empty. Rows
recovered that way are reported as linear whatever they are.</p>
<p>The tool-change flag itself is never false on a shipped preset: all five carry the tool-change
configuration as a plain entry, so its empty line is reachable only on a hand-built or file-loaded
runner.</p>
<p>Above both layers sits the shared no-runner guard every leaf of the branch carries; it, and the
commit, rollback and toast rules the four leaves share with the rest of the branch, are
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="editing-a-cell">Editing a Cell</h2>
<p>Numeric cells are the shared numeric field, so a value commits on blur or on Enter and never per
keystroke — its full contract is <a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>. The <em>Stays put</em>
checkbox commits on the click. Three details govern how these four leaves commit:</p>
<ul>
<li><strong>The writes are per cell, not per row.</strong> Editing <strong>+ Limit</strong> sends that side alone, and the
endpoint leaves a side it was not sent unchanged. The branch's row-shaped editors do not agree on
this: a tool-offset or work-coordinate edit resends the whole row, while
<a class="xref" href="native-parameters.html">Parameters (Native)</a> writes one axis cell at a time as
these four do.</li>
<li><strong>Clearing a cell writes nothing.</strong> An emptied field parses to null and the handler returns before
the request, so the box is left blank on screen while the stored number stands, and the number
returns when the panel is remounted by selecting another node and coming back. There is
consequently no way to unset a stroke limit or a reference position from these panels; the only
surface that removes one is the native leaf's whole-row delete.</li>
<li><strong>No cell is bounded.</strong> None of the four tables passes a minimum or a maximum to its fields, so a
negative limit or a negative rapid rate is accepted and stored. The mechanism time is the one
bounded field on the four leaves, at zero or above.</li>
</ul>
<h2 id="what-the-values-do-when-a-program-plays">What the Values Do When a Program Plays</h2>
<p>The stroke limits are checked at each played step against the chain's current machine pose while the
session's stroke-limit check is on; a position past a configured limit is reported as a
stroke-limit validation error anchored to that step, and pauses the player when pause-on-failure is
set. The check walks a <strong>fixed axis vocabulary</strong> — X, Y and Z from the machine point, A, B and C
from its orientation — so a limit stored against any other axis name is kept and shown here but never
tested. Rapid-traverse timing has the same shape and the same six names: a G00 move is timed
axis-by-axis at each axis' rate and takes the slowest, falling back to the same 20000 mm/min and
36000 deg/min defaults when no configuration answers.</p>
<p>The reference positions reach further, but only at the start. The initializer that sets the machine
pose at the first block writes <strong>every declared axis</strong>, reading each axis' stored reference and
falling back to 0 for one that has none. The G28 reference return reads the same store through a
narrower window: X, Y and Z for its linear stage, and A, B or C for its rotary stage — and a rotary
letter only while the machine declares that axis rotary and a reference position is stored for it,
an unconfigured one being reported as a validation error instead. So an axis outside the six
canonical names still carries its stored reference into the pose at the first block, while its
stroke limit, its rapid rate and its G28 return are all inert.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>Machine Limits (Stroke)</strong> Node Row — <code>equipment/controller/machine/limits</code></li>
<li><strong>Rapid Feedrates</strong> Node Row — <code>equipment/controller/machine/rapid</code></li>
<li><strong>Home / G28 Reference</strong> Node Row — <code>equipment/controller/machine/home</code></li>
<li><strong>Tool-Change Position</strong> Node Row — <code>equipment/controller/machine/tool-change</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Machine Limits (Stroke)</strong> / <strong>Rapid Feedrates</strong> / <strong>Home / G28 Reference</strong> Panel — one
component, its role taken from the selected id's last segment
<ul>
<li>Description Caption — <em>&quot;Machine travel limits per axis (empty = no limit set). Motions beyond a
limit raise a stroke-limit validation error.&quot;</em>, <em>&quot;G00 rapid traverse rate per axis.&quot;</em> or
<em>&quot;G28 first reference (home) machine coordinate per axis. Axes without a value are seeded to 0
when a machine tool attaches.&quot;</em></li>
<li>Axis Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Axis</strong>, then <strong>+ Limit</strong> and <strong>− Limit</strong>, or <strong>Rapid Rate</strong>, or
<strong>Home Position</strong>, then <strong>Unit</strong></li>
<li>Axis Row, one per axis
<ul>
<li>Axis Name Label — bold, not editable</li>
<li>Value Numeric Field, one per value column</li>
<li>Unit Label — <code>mm</code> / <code>deg</code>, or <code>mm/min</code> / <code>deg/min</code> on Rapid Feedrates</li>
</ul>
</li>
</ul>
</li>
<li>Footer Caption — <em>&quot;Axis rows follow the Machine Tool chain; they cannot be added here.&quot;</em></li>
<li>No-Axes Block — replaces the description, the table and the footer caption when the axis set is
empty: a precision-manufacturing icon beside <em>&quot;No machine axes yet — these rows are driven by
the Machine Tool chain (MechBuilder). Attach a machine tool to edit per-axis values.&quot;</em></li>
</ul>
</li>
<li><strong>Tool-Change Position</strong> Panel
<ul>
<li><strong>Tool-change mechanism time</strong> Numeric Field — suffixed <code>s</code>, minimum 0</li>
<li>Axis Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Axis</strong>, <strong>Stays put</strong>, <strong>Position</strong>, <strong>Unit</strong></li>
<li>Axis Row, one per axis
<ul>
<li>Axis Name Label — bold, not editable</li>
<li><strong>Stays put</strong> CheckBox</li>
<li>Position Numeric Field — disabled while <strong>Stays put</strong> is ticked</li>
<li>Unit Label — <code>mm</code> / <code>deg</code></li>
</ul>
</li>
</ul>
</li>
<li>Footer Caption — <em>&ldquo;Stays put&rdquo; leaves the axis where it is during a tool change.</em></li>
<li>No-Config Block — replaces the field, the table and the caption:
<em>&quot;No tool-change config on the active runner.&quot;</em></li>
</ul>
</li>
<li>Shared Empty State — replaces either panel's whole body while the snapshot reports no runner:
<em>&quot;No NC runner — load a project first.&quot;</em></li>
</ul>
</li>
</ul>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcAxisTablePanel.vue</code> — the panel behind the first
three leaves: the role taken from the node id's last segment, the value columns and description
per role, the unit strings computed from the row's rotary flag, the row-count empty layer that
discards the read's presence flag, and the per-cell commit that returns on a cleared field.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcToolChangePanel.vue</code> — the tool-change panel: the
mechanism-time field, the <em>Stays put</em> checkbox that clears the position and sends 0 on the way
back, the disabled position cell, and the presence-flag empty layer.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line both
panels open with.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the numeric cell: commit on blur or Enter,
and the empty-text-to-null parse the panels reject.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the four node ids, their labels and
label keys, and the two item types that bind them to the two panels.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the four readers and their setters: the row shapes, the
presence flag both panels receive, the per-side stroke-limit payload, and the stay-or-position
tool-change payload.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared runner snapshot the first empty layer
reads.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the four node labels, the three descriptions, the column
headers, the two footer captions and the two empty-state lines quoted above.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Axis</strong> and <strong>Unit</strong> column headers.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcNativeParamsPanel.vue</code> — the neighbouring leaf that
edits the same parameter rows by number: the per-cell axis writes, the grid columns unioned from
the existing axis names, the delete that drops a whole row, and the <strong>Add / Set</strong> footer whose
free-text axis field is what widens the axis-type row.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the chain-driven axis helper every reader
builds its rows from, the four readers with their independently computed presence flags, the four
per-axis writers that name an axis but never declare one, the native per-axis-integer route that
does, the tool-change fallback to the tool-change configuration's own axis keys, the NaN sentinel
write, and the rotary lookup the rapid write re-derives server-side.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the one object behind three of the
four leaves: the axis set as the keys of the axis-type row, the reference-position, rapid-rate and
stroke-limit accessors over the per-axis buckets, the fixed rapid defaults, and the rotary
configuration helper the chain walk calls, which writes the axis type, the reference position and
the rapid rate together with no guard on the two values.</li>
<li><code>HiMech/NcParsers/Dependencys/IMachineAxisConfig.cs</code> — the axis contract and the axis-type enum
whose rotary and spindle members both make a row rotary.</li>
<li><code>HiMech/NcParsers/Dependencys/IStrokeLimitConfig.cs</code> — the limit accessors, and the check itself
with its fixed X/Y/Z and A/B/C vocabulary and its validation report.</li>
<li><code>HiMech/NcParsers/Dependencys/IRapidFeedrateConfig.cs</code> — the linear and rotary rate accessors.</li>
<li><code>HiMech/NcParsers/Dependencys/IHomeMcConfig.cs</code> — the reference-position accessors.</li>
<li><code>HiMech/NcParsers/Dependencys/IToolingMcConfig.cs</code> — the tool-change contract: the NaN sentinel and
the mechanism time.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolingMcConfig.cs</code> — the sole implementation: the per-axis
map, the preset default of X and Y staying and Z at 0, and the serialization that omits a zero
mechanism time.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTable.cs</code>,
<code>HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTable.cs</code>,
<code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code>,
<code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs</code> — the parameter numbers in
the table above and the three-axis default each brand opens on.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code>,
<code>HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTableProxy.cs</code>,
<code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTableProxy.cs</code>,
<code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTableProxy.cs</code> — the placeholders the
brand presets actually carry: each clones its three-axis seed into the project's own dependency
list when that list holds none, and resolves to the project's table when it does.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the chain walk that stamps axis codes, hands a rotary axis to
the unconditional rotary helper on a brand table, and fills a missing reference position; and the
five brand presets that decide which parameter table and which tool-change configuration a brand
carries.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — where the chain walk is triggered from, why the
generic axis configuration is cleared first and a brand table is not, and the per-step stroke-limit
check that reads the limits through the proxy-resolved list.</li>
<li><code>HiMech/MachiningProcs/MachiningSession.cs</code> — the play loop that runs that check once per step and
pauses on failure.</li>
<li><code>HiMech/NcParsers/Semantics/LinearMotionUtil.cs</code> — rapid-traverse timing: per-axis rates, the
slowest axis, and the same six axis names the stroke check uses.</li>
<li><code>HiMech/NcParsers/Initializers/HomeMcInitializer.cs</code> — the reference positions written for every
declared axis at the first block.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/ReferenceReturnSyntax.cs</code> — the G28 return: the three linear names
it resolves a final position for, the three rotary letters it tests against the declared axes, and
the validation error a rotary letter the machine has not declared raises.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/ToolChangeMotionSyntax.cs</code> — the overlay: X, Y and Z plus the
declared rotary axes, of which only the ones carrying a non-NaN position move.</li>
<li><code>HiMech/NcParsers/Semantics/ToolChangeSemantic.cs</code> — the tool-change step that carries the
mechanism time as its duration.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane these four leaves sit on, and which half of the
project each of their values is stored in</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — why all four are ungated, and which table each brand
resolves them through</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the fetch, commit, rollback and empty-layer rules these
panels share with the rest of the branch, and where they are recorded as departing from them</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,533 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Program Reading | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Program Reading | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../index.html">
<img id="logo" class="svg" src="../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ControllerProgramReading">
<h1 id="program-reading">Program Reading</h1>
<p>Program Reading is the pair of Machine / Controller leaves that change how the runner <em>reads</em> a
program rather than how the machine moves: which block-skip layers are active, and where a
subprogram call goes looking for its file. Both live on the General Setup page at <code>/general-setup</code>,
under the Control-Tree ids <code>equipment/controller/machine/block-skip</code> and
<code>equipment/controller/machine/subprograms</code>. The pair is also unevenly branded — every shipped brand
preset carries the subprogram-folder config, and one of the five carries no block-skip config at
all.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older <code>?tree=</code> value resolving is described in
<a class="xref" href="../../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</p>
</div>
<h2 id="the-asymmetry">The Asymmetry</h2>
<p>Both nodes are grown behind a plain presence probe over the runner's proxy-resolved dependency
list: <code>hasBlockSkip</code> is set when the list holds any
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IBlockSkipConfig.html">IBlockSkipConfig</a>, <code>hasSubprogramFolders</code> when it holds a
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.SubProgramFolderConfig.html">SubProgramFolderConfig</a>. Neither probe inspects a value.</p>
<table>
<thead>
<tr>
<th>Brand preset</th>
<th>Block-skip config</th>
<th>Subprogram-folder config</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td>Siemens</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td>Syntec</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td>Mazak</td>
<td>yes</td>
<td>yes</td>
</tr>
<tr>
<td>Heidenhain</td>
<td><strong>no</strong></td>
<td>yes</td>
</tr>
</tbody>
</table>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.GenericBlockSkipConfig.html">GenericBlockSkipConfig</a> is the <strong>only</strong> implementer of the
block-skip interface anywhere in the engine, and the Heidenhain preset's pipeline list is the one of
the five that does not carry it. That single omission is what makes Block Skip / Delete the one node
in the whole Controller branch that exists on four brands and not the fifth; every other gate on
this plane is satisfied either by all five presets or by a Siemens-only table. Which flag each row
answers to is <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>The config is not the only thing missing there. The block-skip switches gate a parsing syntax,
<a class="xref" href="../../../../api/Hi.NcParsers.ParsingSyntaxs.BlockSkipSyntax.html">BlockSkipSyntax</a>, and that syntax appears in the Fanuc, Siemens,
Syntec and Mazak syntax lists and in no other — the Heidenhain list carries no block-delete parser,
so on that preset the nine switches would have nothing to gate.</p>
<p>The subprogram-folder config runs the other way: all five presets carry one, and five call
statements across three brand families read the same two values. The node's own vocabulary is
Fanuc's, because the storage is, but the setting is not Fanuc-only.</p>
<h2 id="block-skip--delete">Block Skip / Delete</h2>
<p>The panel is a caption over nine checkboxes and nothing else. The caption reads <em>&quot;Enabled layers
skip their <code>/n</code>-prefixed blocks (<code>/</code> alone = layer 1).&quot;</em>, the first box is labelled <em>&quot;Layer 1
(bare /)&quot;</em> and the rest <em>&ldquo;Layer 2&rdquo;</em> through <em>&ldquo;Layer 9&rdquo;</em>.</p>
<h3 id="what-a-layer-does-to-a-block">What a layer does to a block</h3>
<p><a class="xref" href="../../../../api/Hi.NcParsers.ParsingSyntaxs.BlockSkipSyntax.html">BlockSkipSyntax</a> matches a leading <code>/</code> optionally followed by a
single digit <code>1</code>–<code>9</code>; a bare slash is layer 1. It then always writes a
<a class="xref" href="../../../../api/Hi.NcParsers.Keywords.BlockSkip.html">BlockSkip</a> section recording the symbol and the layer, and always
consumes the prefix out of the block's unparsed text — so the <code>/</code> itself never survives as residue
for the downstream unrecognized-text check, whatever the switches say.</p>
<p>What the switch decides is the block's <strong>body</strong>:</p>
<ul>
<li><strong>Layer on</strong> — the remaining text is moved into
<a class="xref" href="../../../../api/Hi.NcParsers.Keywords.BlockSkip.html#Hi_NcParsers_Keywords_BlockSkip_Body">Body</a> and the unparsed text is cleared. Every downstream
parsing syntax then sees nothing, so the block emits no NC act, and the run records
<code>BlockSkip--Skipped</code> at message severity for that block.</li>
<li><strong>Layer off, or no config at all</strong> — the body stays null and the rest of the block parses exactly
as an unprefixed one would. A runner with no block-skip config therefore simulates the whole
program, which is the safe reading of an unknown machine setting.</li>
</ul>
<p>Ordering matters for one thing and is fixed in each brand's list: the comment and NC-embedded
script syntaxes run <strong>before</strong> the block-skip syntax, so a comment on a skipped block — and any
script embedded in it — still takes effect while the motion beside it does not.</p>
<h3 id="one-value-seven-spellings-of-the-same-range">One value, seven spellings of the same range</h3>
<p>Layers 1 through 9 are the whole range, and no shared constant carries it. The bound is written out
separately in the parsing syntax's regular expression, in the config's XML reader and again in its
CSV setter, in its per-layer mutator guard, in the read endpoint that enumerates the layers, in the
write endpoint that loops over them, and in the panel's checkbox repeat. A tenth layer is therefore
seven edits, not one, and a partial change would be silent — a switch the panel offered but the
config refused to store would simply never take.</p>
<h3 id="what-a-fully-cleared-set-does-not-survive">What a fully cleared set does not survive</h3>
<p>A freshly applied preset has layer 1 on and the other eight off. The stored form is a
comma-separated list of the enabled layers, and an empty set serializes as an empty element. The
reader treats a blank list and a missing element identically — both mean <em>layer 1 only</em> — so
clearing every box and then reloading the project brings layer 1 back on. Every other combination
round-trips exactly.</p>
<h3 id="the-write">The write</h3>
<p>Each box commits on the click, and the request is the <strong>whole</strong> enabled set rather than the one
layer that changed: the panel sorts its set and sends it, and the endpoint walks 1 through 9 setting
each layer to whether the payload names it. A failed write restores the previous set and raises a
toast; the shared rules behind that shape are
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<p>On the brand that carries no config, the read answers <em>not present</em> with an empty layer list and any
write is answered unsuccessfully with <em>&ldquo;No IBlockSkipConfig on the active runner&rdquo;</em> — but the node is
not built there, so neither answer is reachable from the tree.</p>
<h2 id="subprogram-folders">Subprogram Folders</h2>
<p>Two plain text fields, labelled <em>&quot;Internal folder (M98)&quot;</em> and <em>&quot;External folder (M198)&quot;</em>, hinted
<em>&ldquo;Relative to the host NC file's folder; empty = that folder itself&rdquo;</em> and <em>&ldquo;Fanuc external-storage
calls; empty = fall back to the internal folder&rdquo;</em>. They write
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.SubProgramFolderConfig.html#Hi_NcParsers_Dependencys_Generic_SubProgramFolderConfig_InternalFolder">InternalFolder</a> and
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.SubProgramFolderConfig.html#Hi_NcParsers_Dependencys_Generic_SubProgramFolderConfig_ExternalFolder">ExternalFolder</a>; an empty or
whitespace-only box is stored as null rather than as an empty string.</p>
<h3 id="which-call-statements-read-these-two-values">Which call statements read these two values</h3>
<table>
<thead>
<tr>
<th>Preset</th>
<th>Call statement</th>
<th>Field it reads</th>
<th>File-name chain, first match wins</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc, Mazak, Syntec</td>
<td><code>M98 P_ L_</code></td>
<td>Internal</td>
<td><code>O{P:D4}.NC</code>, <code>O{P}.NC</code>, <code>O{P:D4}</code>, <code>O{P}</code>, <code>{P:D4}.NC</code>, <code>{P}.NC</code></td>
</tr>
<tr>
<td>Fanuc, Mazak, Syntec</td>
<td><code>M198 P_</code></td>
<td>External, falling back to Internal when it is null</td>
<td>the same chain</td>
</tr>
<tr>
<td>Fanuc</td>
<td><code>G65</code> / <code>G66</code> macro call</td>
<td>Internal</td>
<td>the same chain</td>
</tr>
<tr>
<td>Siemens</td>
<td>name call (<code>L9810</code>, a named cycle)</td>
<td>Internal</td>
<td><code>{name}.SPF</code>, <code>{name}.MPF</code>, <code>{name}</code></td>
</tr>
<tr>
<td>Heidenhain</td>
<td><code>CALL PGM name</code></td>
<td>Internal</td>
<td><code>{name}.h</code>, <code>{name}.H</code>, <code>{name}</code></td>
</tr>
</tbody>
</table>
<p>Two consequences follow from the table, and both are invisible from the field labels.</p>
<p><strong>The External folder is inert on two brands.</strong> Only the Fanuc-family inliner,
<a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.SubProgramCallSyntax.html">SubProgramCallSyntax</a>, ever reads it — the Siemens and
Heidenhain call syntaxes look up their callee through the Internal folder alone. The field is
offered on every brand because the storage is brand-neutral, and it is honoured on the three that
have an <code>M198</code>.</p>
<p><strong>The Internal folder is read by more than the M98 label says.</strong> On the Fanuc preset it also anchors
both Custom Macro B lookups — <a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Fanuc.FanucMacroCallSyntax.html">FanucMacroCallSyntax</a> for
the <code>G65</code> one-shot call and <a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Fanuc.FanucModalMacroSyntax.html">FanucModalMacroSyntax</a> for
the <code>G66</code> modal — and on Siemens and Heidenhain it is the <em>only</em> subprogram root there is. Emptying
it to fix an M98 path therefore moves the macro and named-call lookups with it.</p>
<p>Each brand also brings its own file-name chain, and the chains are per-syntax properties rather than
one shared list — the Fanuc chain is
<a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.MacroFileResolver.html#Hi_NcParsers_EvaluationSyntaxs_MacroFileResolver_FilenamePatterns">FilenamePatterns</a>, a static array every
Fanuc-family caller shares, while
<a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Siemens.SiemensSubProgramCallSyntax.html#Hi_NcParsers_EvaluationSyntaxs_Siemens_SiemensSubProgramCallSyntax_FilePatterns">FilePatterns</a> and
<a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Heidenhain.HeidenhainSubProgramCallSyntax.html#Hi_NcParsers_EvaluationSyntaxs_Heidenhain_HeidenhainSubProgramCallSyntax_FilePatterns">FilePatterns</a> are
instance properties of their own call syntaxes and are re-writable from the runner file. Case
matching is delegated to the host file system throughout, which is why the Heidenhain chain carries
both <code>.h</code> and <code>.H</code>.</p>
<h3 id="where-a-relative-folder-is-anchored">Where a relative folder is anchored</h3>
<p>The resolver takes the folder as written. An absolute path is used unchanged. A relative path — and
an empty one, which means <em>the root itself</em> — is combined with the base directory supplied by
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.SystemWired.ProjectFolderDependency.html">ProjectFolderDependency</a>, and the machining session wires
that dependency to the <strong>project's</strong> own root before each play.</p>
<p>So the anchor is the project folder, not the folder the host NC file happens to sit in. A program
played from a subfolder of the project resolves <code>NC</code> against the project root, not against its own
neighbour of that name. The field's hint says otherwise, and so does the API summary on the two
config properties the panel writes; the resolver is what runs.</p>
<p>Two further conditions end the lookup before any pattern is tried: a relative folder with no base
directory resolves to nothing, and so does a folder that does not exist as a directory.</p>
<h3 id="a-missing-file-is-an-error-on-one-family-and-a-warning-on-the-other">A missing file is an error on one family and a warning on the other</h3>
<p>The same empty result is reported at two different severities, and the split follows the call
statement rather than the setting.</p>
<ul>
<li><strong>Fanuc, Mazak, Syntec</strong> — an unresolved <code>M98</code> / <code>M198</code> raises <code>SubProgramCall--FileNotFound</code> at
error severity, quoting the folder that was searched, and the call is consumed.</li>
<li><strong>Siemens, Heidenhain</strong> — an unresolved call raises <code>SiemensCall--Skipped</code> or
<code>HeidenhainCall--Skipped</code> at warning severity and is consumed with no motion effect. That is
deliberate: the common unresolved callee on those controls is an OEM or measuring cycle whose
definition file ships with the machine and never travels with the program, so a hard error would
fire on ordinary, correct programs.</li>
</ul>
<p>A resolved call is inlined: the callee is segmented through the runner's own segmenter and its
blocks are prepended into the program ahead of the host block, each stamped with a record naming the
call it came from. <code>M98</code> with an <code>L</code> count above one inlines the same file that many times in
series, each repetition its own segmentation pass.</p>
<h3 id="the-nesting-ceiling-and-where-there-is-none">The nesting ceiling, and where there is none</h3>
<p>A self- or mutually-recursive callee re-captures its own call statement inside every inlined body,
so a call path needs a rail of its own — no loop watchdog covers it. <strong>That rail is declared twice,
independently, and is missing on the third path.</strong></p>
<table>
<thead>
<tr>
<th>Preset</th>
<th>Ceiling</th>
<th>Where it is declared</th>
<th>Exceeded</th>
</tr>
</thead>
<tbody>
<tr>
<td>Siemens</td>
<td>32 frames</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Siemens.SiemensSubProgramCallSyntax.html#Hi_NcParsers_EvaluationSyntaxs_Siemens_SiemensSubProgramCallSyntax_DefaultMaxCallDepth">DefaultMaxCallDepth</a></td>
<td><code>SiemensCall--DepthLimitExceeded</code>, consumed as a safe skip</td>
</tr>
<tr>
<td>Heidenhain</td>
<td>32 frames</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Heidenhain.HeidenhainSubProgramCallSyntax.html#Hi_NcParsers_EvaluationSyntaxs_Heidenhain_HeidenhainSubProgramCallSyntax_DefaultMaxCallDepth">DefaultMaxCallDepth</a></td>
<td><code>HeidenhainCall--DepthLimitExceeded</code>, consumed as a safe skip</td>
</tr>
<tr>
<td>Fanuc, Mazak, Syntec</td>
<td>none</td>
<td>—</td>
<td>—</td>
</tr>
</tbody>
</table>
<p>The two constants are separate <code>const int</code> declarations on separate classes that happen to hold the
same number, each surfaced as its own writable <code>MaxCallDepth</code> property and each serialized into the
runner file as its own element. Raising one raises nothing else. Both compare against the depth of
the call stack already stamped on the host block, so the count is frames entered, not files listed.</p>
<p>On the Fanuc-family inliner and on both Custom Macro B calls there is no depth comparison at all.
None of them reads the host block's frame count before inlining, so none carries the recursion rail
the Siemens and Heidenhain syntaxes declare. The depth itself is recorded either way — every inlined
block is stamped with a pushed call-stack frame that the matching return statement pops — so what is
missing is the comparison, not the count.</p>
<p>The Heidenhain call syntax carries a second, unrelated ceiling for its section-repeat form:
<a class="xref" href="../../../../api/Hi.NcParsers.EvaluationSyntaxs.Heidenhain.HeidenhainSubProgramCallSyntax.html#Hi_NcParsers_EvaluationSyntaxs_Heidenhain_HeidenhainSubProgramCallSyntax_MaxRepetitions">MaxRepetitions</a> caps
a <code>REP</code> literal at 65534, since the repeat re-scans the file once per repetition.</p>
<h3 id="what-an-empty-internal-folder-does-not-survive">What an empty Internal folder does not survive</h3>
<p>The config's default internal folder is <code>NC</code> — the layout where the main program sits beside an
<code>NC/</code> directory of subprograms — and that default is applied by the property itself, before the XML
reader runs. The writer omits an element for a null folder, and the reader keeps whatever the
property already holds when the element is absent. Clearing the <strong>Internal folder</strong> box therefore
stores a null, writes no element, and reads back as <code>NC</code> the next time the project is opened.</p>
<p>The <strong>External folder</strong> box has no such default and round-trips cleanly: cleared, it stores null,
writes no element, and reads back null — which is the state the field's own hint describes, falling
back to the internal folder.</p>
<h2 id="editing-and-storage">Editing and Storage</h2>
<p>Both panels open with the branch's two empty layers — the shared <em>&quot;No NC runner — load a project
first.&quot;</em> line first, then the panel's own read reporting <em>&quot;No block-skip config on the active
runner.&quot;</em> or <em>&quot;No subprogram-folder config on the active runner.&quot;</em> The first layer is a live guard
against the snapshot emptying under a panel that is already mounted, which is what closing the
project does. The second is unreachable by navigating: the node's gate and the panel's read probe
the same dependency on the same resolved list, so the leaf exists only where the read is about to
succeed.</p>
<p>Their commit shapes differ, and both are recorded in
<a class="xref" href="../editing-contract.html">Editing Contract</a>: the block-skip boxes commit on the click
and roll back on failure, while the two folder fields commit <strong>as a pair</strong> on blur or Enter of
either one, compared against a saved snapshot of both rather than a per-field captured value — so
editing one folder writes both, and a failure restores both.</p>
<p>Both configs are plain runner-owned entries rather than proxies, so their values serialize with the
runner and follow it. Installing a controller file replaces them outright, and a brand apply assigns
a whole fresh preset, so <strong>an applied brand change returns both to that preset's defaults</strong> — layer 1
alone, and the <code>NC</code> internal folder with no external one. Selecting the brand already in force is not
a route to that reset: the apply stays disabled until the selection differs from the runner's own
brand. Which half of a project each value on this plane lands in is
<a class="xref" href="index.html">Machine and Controller Plane</a>.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>Controller</strong> Node Row
<ul>
<li><strong>Machine / Controller</strong> Group Row
<ul>
<li><strong>Block Skip / Delete</strong> Node Row — <code>equipment/controller/machine/block-skip</code>, grown while
the snapshot reports a block-skip config</li>
<li><strong>Subprogram Folders</strong> Node Row — <code>equipment/controller/machine/subprograms</code>, grown while it
reports a subprogram-folder config</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — <strong>Block Skip / Delete</strong> Panel
<ul>
<li>No-Runner Line — <em>&quot;No NC runner — load a project first.&quot;</em>, the branch's shared empty state</li>
<li>No-Config Line — <em>&quot;No block-skip config on the active runner.&quot;</em></li>
<li>Description Caption — <em>&quot;Enabled layers skip their <code>/n</code>-prefixed blocks (<code>/</code> alone = layer 1).&quot;</em></li>
<li>Layer CheckBox Column — nine dense boxes in one column
<ul>
<li><strong>Layer 1 (bare /)</strong> CheckBox</li>
<li><strong>Layer 2</strong> … <strong>Layer 9</strong> CheckBoxes</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — <strong>Subprogram Folders</strong> Panel
<ul>
<li>No-Runner Line — the same shared empty state</li>
<li>No-Config Line — <em>&quot;No subprogram-folder config on the active runner.&quot;</em></li>
<li><strong>Internal folder (M98)</strong> Field — outlined and dense, hinted <em>&ldquo;Relative to the host NC file's
folder; empty = that folder itself&rdquo;</em> — a hint the resolver contradicts, the anchor being the
project root</li>
<li><strong>External folder (M198)</strong> Field — outlined and dense, hinted <em>&ldquo;Fanuc external-storage calls;
empty = fall back to the internal folder&rdquo;</em></li>
</ul>
</li>
</ul>
<p>Every label above is a translated role string rather than a type name, so a locale change rewrites
what is read without touching an id. Both panels mount against one node and take nothing from it:
each serves a single tree id, so neither reads a role off the node's path.</p>
<h2 id="source-code-path">Source Code Path</h2>
<p>See <a class="xref" href="../../../index.html">HiNC App Anatomy</a> for git repository links.</p>
<h3 id="web-application">Web Application</h3>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/SoftNcBlockSkipPanel.vue</code> — the block-skip panel: the two
empty layers, the caption with its two code slots, the nine boxes and their layer-one label, and
the whole-set write with its rollback.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcSubprogramPanel.vue</code> — the folder panel: the two
outlined fields, the blur and Enter bindings on both, the saved-snapshot guard that skips a
redundant write, and the paired rollback.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line both
panels open with.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the two node ids, their labels and
label keys, the snapshot flag each is pushed behind, and the item types that bind them to their
panels.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the two readers and their coercions, the layer-array
writer, the paired folder writer, and the snapshot fields the gates read.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the envelope reader that turns an unsuccessful body into the error
a panel's toast quotes.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot both first empty layers
read, and its once-per-session install.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the two node labels, the block-skip caption and layer
labels, the four folder strings, the two empty-state lines and the error contexts.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the two presence probes on the snapshot, the
block-skip read that enumerates the nine layers and the write that sets each one, the folder read
that substitutes an empty string for a null and the write that substitutes a null for a blank, the
brand switch whose remark names both of these among the runner-owned values it resets, and the
shared helper that answers a missing dependency with an unsuccessful body rather than an error
status.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/IBlockSkipConfig.cs</code> — the block-skip contract: the per-layer query
and mutator, and the stated safe default when the dependency is absent.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/GenericBlockSkipConfig.cs</code> — the only implementer: the
layer-one default, the comma-separated stored form, the range filter repeated in its reader and
its setter, and the blank-equals-absent rule behind the cleared-set round trip.</li>
<li><code>HiMech/NcParsers/ParsingSyntaxs/BlockSkipSyntax.cs</code> — the prefix parser: the layer regular
expression, the always-recorded section, the body move that silences a block, and the message the
skip reports.</li>
<li><code>HiMech/NcParsers/Keywords/BlockSkip.cs</code> — the recorded section: symbol, layer, and the body that
is null exactly when the skip did not take effect.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/SubProgramFolderConfig.cs</code> — the two folders, the <code>NC</code>
default applied by the property itself, the null fallbacks, and the writer that omits an element
for a null.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/SubProgramCallSyntax.cs</code> — the Fanuc-family inliner: the
internal-versus-external choice, the repetition loop, the file-not-found error, and the absence of
any call-depth comparison.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/MacroFileResolver.cs</code> — the shared resolver: the Fanuc file
name chain, the absolute-versus-relative anchoring, and the directory-exists precondition.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Fanuc/FanucMacroCallSyntax.cs</code>,
<code>HiMech/NcParsers/EvaluationSyntaxs/Fanuc/FanucModalMacroSyntax.cs</code> — the other two Fanuc readers
of the internal folder: the <code>G65</code> one-shot macro call, and the <code>G66</code> modal that resolves the same
file again on every motion block it fires on.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensSubProgramCallSyntax.cs</code> — the name-call
inliner: its own file-pattern chain, its own depth constant and property, and the safe-skip that
answers an unresolved OEM cycle.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Heidenhain/HeidenhainSubProgramCallSyntax.cs</code> — the klartext
call inliner: the <code>CALL PGM</code> lookup through the same internal folder, the second depth constant,
and the separate repeat ceiling.</li>
<li><code>HiMech/NcParsers/Keywords/CallStack.cs</code>,
<code>HiMech/NcParsers/EvaluationSyntaxs/SubProgramReturnSyntax.cs</code> — the frame each inliner stamps on
an inlined block, and the <code>M99</code> return that pops it.</li>
<li><code>HiMech/NcParsers/NcDiagnosticProgress.cs</code> — the category and severity behind each diagnostic id
named above.</li>
<li><code>HiMech/NcParsers/Dependencys/SystemWired/ProjectFolderDependency.cs</code> — the base directory a
relative folder is anchored against, and the host-wired provider behind it.</li>
<li><code>HiMech/MachiningProcs/MachiningSession.cs</code> — where that provider is wired to the project root
before a play.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets, and which of them carries each of
these two dependencies.</li>
<li><code>HiMech/NcParsers/Syntaxs/FanucSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SiemensSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SyntecSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/MazakSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/HeidenhainSyntaxUtil.cs</code> — the four lists that carry the block-skip
parser and the one that does not, and which call syntax each preset runs.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Machine and Controller Plane</a> — the plane these two leaves sit on, and the storage split that
decides which of its values a brand apply resets and which it leaves alone</li>
<li><a class="xref" href="../brand-matrix.html">Brand Matrix</a> — the two flags that grow these nodes, what each one probes,
and the brand sets they produce</li>
<li><a class="xref" href="../editing-contract.html">Editing Contract</a> — the commit, rollback and empty-state rules these panels
share with the branch, and the paired-field write recorded as one of its exceptions</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,43 @@
<div id="sidetoggle">
<div>
<div class="sidefilter">
<form class="toc-filter">
<span class="glyphicon glyphicon-filter filter-icon"></span>
<span class="glyphicon glyphicon-remove clear-icon" id="toc_filter_clear"></span>
<input type="text" id="toc_filter_input" placeholder="Filter by title" onkeypress="if(event.keyCode==13) {return false;}">
</form>
</div>
<div class="sidetoc">
<div class="toc" id="toc">
<ul class="nav level1">
<li>
<a href="index.html" name="" title="Overview">Overview</a>
</li>
<li>
<a href="brand-switch.html" name="" title="Controller Brand">Controller Brand</a>
</li>
<li>
<a href="per-axis-tables.html" name="" title="Per-Axis Tables">Per-Axis Tables</a>
</li>
<li>
<a href="interface-parameters.html" name="" title="Interface Parameters">Interface Parameters</a>
</li>
<li>
<a href="m-code-declarations.html" name="" title="M-Code Declarations">M-Code Declarations</a>
</li>
<li>
<a href="program-reading.html" name="" title="Program Reading">Program Reading</a>
</li>
<li>
<a href="indexing-positions.html" name="" title="Indexing Positions">Indexing Positions</a>
</li>
<li>
<a href="native-parameters.html" name="" title="Native Parameters">Native Parameters</a>
</li>
</ul>
</div>
</div>
</div>
</div>
@@ -0,0 +1,2 @@
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"Controller Brand","href":"brand-switch.html","topicHref":"brand-switch.html"},{"name":"Per-Axis Tables","href":"per-axis-tables.html","topicHref":"per-axis-tables.html"},{"name":"Interface Parameters","href":"interface-parameters.html","topicHref":"interface-parameters.html"},{"name":"M-Code Declarations","href":"m-code-declarations.html","topicHref":"m-code-declarations.html"},{"name":"Program Reading","href":"program-reading.html","topicHref":"program-reading.html"},{"name":"Indexing Positions","href":"indexing-positions.html","topicHref":"indexing-positions.html"},{"name":"Native Parameters","href":"native-parameters.html","topicHref":"native-parameters.html"}]}