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,276 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Background / Coolant | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Background / Coolant | 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-BackgroundCoolant">
<h1 id="background--coolant">Background / Coolant</h1>
<p>Background and Coolant are two leaves of the General Setup page's Control Tree
(<code>/general-setup?tree=equipment/background</code>, <code>/general-setup?tree=equipment/coolant</code>), sitting
directly under the General Setup group between Spindle Capability and Fixture. They are two tree
items rather than a page, so the older <code>/equipment/background-coolant</code> path redirects onto the
Background leaf. Both are served by one panel component, which branches on the selected node's role
path.</p>
<p>They edit the thermal condition on the project's authored equipment face,
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html">SetupEquipment</a>, reached as
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SetupEquipment">SetupEquipment</a>:</p>
<ul>
<li><a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_BackgroundTemperature_C">BackgroundTemperature_C</a> (Background
leaf)</li>
<li>the whole <a class="xref" href="../../api/Hi.Physics.CoolantHeatCondition.html">CoolantHeatCondition</a> (Coolant leaf) — file-first, see below</li>
</ul>
<p>Key Model: <a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html">SetupEquipment</a> (+ its
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_CoolantHeatCondition">CoolantHeatCondition</a>).</p>
<div class="NOTE">
<h5>Note</h5>
<p>The WPF desktop app has no surface for either value — no page, no panel, and no handler for the
<code>.CoolantHeatCondition</code> extension. There, both are whatever the project XML carries on the
equipment face, or the class defaults when the XML says nothing.</p>
</div>
<h2 id="layout">Layout</h2>
<ul>
<li>Background leaf — <code>equipment/background</code>, item type <code>ThermalCondition</code>
<ul>
<li>Background Temperature NumberField (°C) —
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_BackgroundTemperature_C">BackgroundTemperature_C</a>.</li>
</ul>
</li>
<li>Coolant leaf — <code>equipment/coolant</code>, the same item type and the same panel (file pick on top,
properties below, as on the workpiece material leaf)
<ul>
<li>&ldquo;Not attached&rdquo; badge — visible until the first coolant-related save (see <em>Lazy
CoolantHeatCondition creation</em> below).</li>
<li><strong>File row</strong> — <code>FilePathInput</code> (<code>.CoolantHeatCondition</code>, resource subfolder
<code>CoolantHeatCondition</code>): a <strong>Select</strong> dropdown button whose menu holds <strong>Browse&hellip;</strong>,
<strong>Browse Resource&hellip;</strong> and — only while a file is set — <strong>Clear</strong>, beside a read-only path
input; it loads a saved condition by named root. A <strong>Save As&hellip;</strong> button follows it, prompting
for a project-relative file to export the current condition to. Tracked in
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_CoolantHeatConditionFile">CoolantHeatConditionFile</a>. The three
standard cooling types ship as ready-made files in <code>HiNc-Resource</code>
(<code>Resource/CoolantHeatCondition/StandardForcedAir.default.CoolantHeatCondition</code>,
<code>StandardOilBasedCoolant.default.CoolantHeatCondition</code> and
<code>StandardWaterSolubleCoolant.default.CoolantHeatCondition</code>), so <strong>Browse Resource&hellip;</strong> starts
populated like the workpiece-material picker — picking a shipped file <em>is</em> the cooling-type
selection.</li>
<li>Name / Note — read-only mirror of the loaded condition.</li>
<li>Property fields (always visible, under Name / Note):
<ul>
<li>Coolant Temperature (°C) — <code>CoolantTemperature_C</code>.</li>
<li>Flood Convection Coefficient (W/(m²·K)) — <code>CoolantConvectionCoefficient_Wdm2K</code>, <code>min: 0</code>.</li>
<li>Mist / Flood Ratio — <code>MistFloodConvectionRatio</code>, <code>min: 0</code>.</li>
<li>Off (Air) Convection Coefficient (W/(m²·K)) — <code>OffConvectionCoefficient_Wdm2K</code>, <code>min: 0</code>.</li>
</ul>
</li>
</ul>
</li>
<li>Both leaves, with no project open: every control is disabled and a &ldquo;no project — open one first&rdquo;
caption is shown. The panel fetches the snapshot itself on mount and gates on its <code>hasEquipment</code>
flag, so the tree host holds no state for either leaf.</li>
</ul>
<h2 id="behavior">Behavior</h2>
<ul>
<li><strong>File-based conditions (WorkpieceMaterial pattern).</strong> Picking a <code>.CoolantHeatCondition</code> file
installs it as the whole condition (<code>XFactory.GenByFile</code>); <strong>Save As&hellip;</strong> exports the current
values and renames the condition after the file (<code>IPreferredFileName</code>). While
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_CoolantHeatConditionFile">CoolantHeatConditionFile</a> is set, the
project save externalizes the condition as
<code>&lt;CoolantHeatCondition&gt;&lt;XmlSource&gt;relPath&lt;/XmlSource&gt;&lt;/CoolantHeatCondition&gt;</code> and regenerates the
side-file (a resource pick is copied into the project on save); with no file the bare inline shape
is kept byte-compatible. Coefficient edits tune <em>this project's</em> copy. The static presets on
<a class="xref" href="../../api/Hi.Physics.CoolantHeatCondition.html">CoolantHeatCondition</a> (<code>StandardPresets</code> / <code>ApplyPreset</code> /
<code>MatchStandardPreset</code>) are the API-side source the shipped files are authored from.</li>
<li><strong>The file row's Clear entry does nothing here.</strong> <code>FilePathInput</code> reports a clear by emitting
<code>update:modelValue(null)</code>, and this panel binds the widget's value one-way and listens only for
<code>picked</code> — so the entry is rendered, is clickable while a file is set, and drops nothing. Clearing
the tracked file is therefore not reachable from this leaf at all; the reference changes only by
loading another file or by a Save As.</li>
<li><strong>The <code>.default</code> marker never survives a user save.</strong> The shipped resource files carry a
<code>.default</code> ownership marker in their names. The Save As&hellip; prompt strips it from its prefill, and
the controller strips it again from the name it stamps on the condition, so a user save cannot
mint a file that masquerades as a shipped default.</li>
<li><strong>The &ldquo;not attached&rdquo; badge is unreachable, and the lazy creation behind it never fires.</strong>
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html">SetupEquipment</a> initializes
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_CoolantHeatCondition">CoolantHeatCondition</a> at its declaration
and nothing assigns null to it — the project-XML read replaces the instance only when the element
is present — so the controller's <code>hasCoolant</code>, which is a null test on that property, is true
whenever a project is open. The badge is therefore rendered only in the no-equipment case the
panel already covers with its own caption, and the null branch each scalar PUT carries is
defensive rather than reached. What a fresh project actually carries is the class defaults, which
are the water-soluble preset's values.</li>
<li><strong>Celsius is the wire format.</strong> <a class="xref" href="../../api/Hi.Physics.CoolantHeatCondition.html">CoolantHeatCondition</a> stores Kelvin internally
but exposes <code>_C</code> accessors that handle the conversion, and the DTO carries Celsius.</li>
<li><strong>Finite values only.</strong> Every field handler drops a null or non-finite entry before any request is
made, so <code>Infinity</code> never reaches these endpoints even though the numeric widget can parse it.
Background and coolant temperatures accept negatives (e.g. -40 °C for cryogenic coolant); the
three convection fields are clamped to <code>min: 0</code>.</li>
<li><strong>The run sees it at once.</strong> The background-temperature write, all four condition scalars and the
file load each end in the project service's ForwardSetupEnvironmentToExecution, which stamps the
background temperature and the condition reference onto the runtime equipment face — including
the lazily created condition, which otherwise would exist only on the authored side.</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/ThermalConditionPanel.vue</code> — the one panel both leaves
share, branching on the node's last role-path segment: Background renders the single temperature
field, Coolant the not-attached badge, the file row, the read-only Name / Note and the four
property fields.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — declares the two
<code>ThermalCondition</code> leaves (<code>equipment/background</code>, <code>equipment/coolant</code>) under the General Setup
group, with no host-level init state.</li>
<li><code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> — registers the <code>ThermalCondition</code> item type
against that panel: no child tree, no geometry-cache chain.</li>
<li><code>wwwroot-src/src/components/widgets/FilePathInput.vue</code> — the file row widget: Browse&hellip; /
Browse Resource&hellip; (opening at the <code>CoolantHeatCondition</code> resource subfolder) / Clear.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the numeric field used by every value on
both leaves, and the source of the <code>min: 0</code> clamp on the three convection fields.</li>
<li><code>wwwroot-src/src/api/backgroundCoolant.ts</code> — typed client for <code>/api/mech/background-coolant/*</code>:
the flat snapshot, the load / save-as pair, and the five scalar PUTs.</li>
<li><code>wwwroot-src/src/router/routes.ts</code> — carries the <code>equipment/background-coolant</code> redirect, which
lands on <code>general-setup?tree=equipment/background</code>.</li>
<li><code>Mech/BackgroundCoolantController.cs</code> — REST surface at <code>/api/mech/background-coolant</code> over the
authored equipment face:
<ul>
<li><code>GET /</code> — flat snapshot <code>{ hasEquipment, backgroundTemperature_C, hasCoolant, coolantTemperature_C, coolantConvectionCoefficient_Wdm2K, mistFloodConvectionRatio, offConvectionCoefficient_Wdm2K, coolantName, coolantNote, coolantHeatConditionFile }</code>.</li>
<li><code>POST /load</code>, <code>POST /save-as</code> — <code>{ rootName, relFile }</code>: install a <code>.CoolantHeatCondition</code> file
/ export the current condition. Both resolve the path under the named root and refuse anything
that escapes it, and both record the result in
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_CoolantHeatConditionFile">CoolantHeatConditionFile</a> as a
project-relative path when it lands under the project. Save-as appends the extension when it is
missing and renames the condition after the file (<code>IPreferredFileName</code>) with the <code>.default</code>
marker stripped.</li>
<li><code>PUT /background-temperature-c</code> — set
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_BackgroundTemperature_C">BackgroundTemperature_C</a>.</li>
<li><code>PUT /coolant-temperature-c</code>, <code>PUT /coolant-convection-coefficient-wdm2k</code>,
<code>PUT /mist-flood-convection-ratio</code>, <code>PUT /off-convection-coefficient-wdm2k</code> — the four condition
scalars, all through one helper that lazy-creates the condition when absent, writes it, and
forwards to the runtime face.</li>
</ul>
</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/Physics/CoolantHeatCondition.cs</code> — the model: Kelvin storage with <code>_C</code> accessors, the
flood / mist-ratio / off convection coefficients, Name and Note with <code>PreferredFileName</code>, and the
<code>StandardPresets</code> / <code>ApplyPreset</code> / <code>MatchStandardPreset</code> statics behind the shipped resource
files.</li>
<li><code>HiMech/Machining/MachiningEquipmentUtils/SetupEquipment.cs</code> — the authored equipment face that
owns the background temperature, the condition and its file reference, and externalizes the
condition to the side-file when that reference is set.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="spindle-capability.html">Spindle Capability</a> — sibling equipment editor, editing the same authored equipment face.</li>
<li><a href="../../manual/setup/coolant.html">Coolant (manual)</a> — the end-user task: picking a cooling type in the application.</li>
<li><a class="xref" href="../../technique/milling-physics/coolant-model.html">Coolant Model</a> — what the values on this panel mean, which one a running program applies, and how the condition is stored.</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,579 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Brand Matrix | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Brand Matrix | 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-ControllerBrandMatrix">
<h1 id="brand-matrix">Brand Matrix</h1>
<p>The Controller branch is the one branch of the equipment tree whose node set is not fixed: it is
grown from a snapshot of the active NC runner, and a node whose backing dependency the runner does
not resolve is never created at all. This page is the index of every node that branch can grow, and
it owns no node of its own — the ids it lists sit under <code>?tree=equipment/controller</code> on
<code>/general-setup</code>, on two plane stems, <code>equipment/controller/machine</code> and
<code>equipment/controller/program-data</code>.</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="what-decides-the-shape">What Decides the Shape</h2>
<p>One request returns a flat snapshot of the active runner: the brand marker, the chain-driven axis
list, and a set of presence flags. Each flag is a type probe over the runner's
proxy-resolved dependency list — <a class="xref" href="../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_GetEffectiveNcDependencyList_">GetEffectiveNcDependencyList</a><small>(API)</small>,
so what is probed is the concrete table a proxy stands in for, never the proxy. Every gate the tree
reads tests a single type; two of the fields no component reads test a pair instead. The branch
builder reads that snapshot once per build and pushes a node for each flag that reads true. A node
that fails its gate is absent, not hidden and not disabled; the branch is regrown from a fresh
snapshot whenever a panel swaps the runner. How a branch is built, rebuilt and selected is the Control-Tree
engine's own subject — see <a class="xref" href="../../platform/control-tree.html">Control Tree</a>.</p>
<p>Five brands ship as presets: Fanuc, Siemens, Heidenhain, Syntec and Mazak. Each is a property on the
engine's runner type returning a fresh instance whose dependency list is written out literally, so
what a brand grows is decided by that list and by nothing else.</p>
<p>The snapshot also carries one non-boolean field that changes a panel rather than the tree.
<code>workCoordinateKind</code> names which storage model backs the work coordinates, by a type switch over the
first <a class="xref" href="../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> in the effective list: <code>fanuc</code> on Fanuc
and Mazak, <code>syntec</code> on Syntec, <code>siemens</code> on Siemens (the frame table precedes the machine-data
table, which is not an ISO coordinate provider), <code>heidenhain</code> on Heidenhain, and <code>none</code> when nothing
resolves. Its <code>generic</code> arm answers for the brand-neutral
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Generic.IsoCoordinateTable.html">IsoCoordinateTable</a>, which no brand preset carries — it is
reachable only from a runner that already holds one, so the arm is defensive rather than dead.</p>
<h2 id="the-fixed-core">The Fixed Core</h2>
<p>Both plane stems are the builder's return value rather than gated entries, so they appear together
whenever a runner resolves, and neither is ever empty: each carries leaves that no flag guards.
Both are <code>Group</code> stems, so each renders an intro line and a clickable child list rather than a field
editor.</p>
<p>The machine plane's ungated leaves are Controller Brand, Machine Limits (Stroke), Rapid Feedrates,
Home / G28 Reference, Tool-Change Position and Controller Parameters. The program-data plane's are
Work Coordinates (G54…) and Tool Offsets.</p>
<p>Nothing in that core is gated, so no dependency decides whether those leaves exist — the builder
pushes them whatever resolves. What the preset lists decide is whether they have anything to show,
and on every brand they do.
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a> declares the machine-config interfaces
itself — home reference, axis set, rapid feedrates, stroke limits, spindle control, M-code
declarations and the tool-change trigger — and every brand's parameter table derives from it, which
covers the limits, rapid, home and parameters leaves at once. The other two read dependencies each
preset carries in its own right: <a class="xref" href="../../../api/Hi.NcParsers.Dependencys.CncBrandDependency.html">CncBrandDependency</a> behind
Controller Brand, and <a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Generic.ToolingMcConfig.html">ToolingMcConfig</a> behind Tool-Change
Position. On the program-data plane, every preset proxies the generic
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTable.html">ToolOffsetTable</a>, and each brand supplies an ISO coordinate
provider of its own.</p>
<p>The axis names the per-axis leaves list are the parameter table's own axis entries, which the
machining chain adds to and re-types when a machine tool is attached — it sets a type per chain axis
and removes nothing, so an attached chain yields the union of the two sets rather than a
replacement. Each brand's default table already seeds a linear X, Y and Z, so those rows are
populated before any chain is wired.</p>
<h2 id="with-no-runner">With No Runner</h2>
<p>The builder returns an empty child list, so both stems and every leaf disappear and only the branch
root survives. Its editor is the runner root panel, which then shows an hourglass over &ldquo;No NC
controller runner on this project.&rdquo; and a hint pointing at the ⋮ menu's Load command; the brand
badge is dropped and the file caption reads &ldquo;No NC runner&rdquo;.</p>
<p>The same empty list is returned when the snapshot request throws, so a failed fetch and a genuine
absence produce an identical tree shape, and the root panel renders the same hourglass block for
both. What separates them is transient and comes from elsewhere: the shared snapshot's own failed
read resets it to the empty value and raises an error toast. That read is a separate request from
the builder's, so the two can disagree — a branch collapsed by a thrown build fetch can sit under a
root panel still showing the brand badge of a snapshot that loaded.</p>
<p>Every leaf panel still opens with its own guard on the same flag, rendering the shared empty body
&ldquo;No NC runner — load a project first.&rdquo; Since a leaf node is built only when the snapshot already
reported a runner, that guard is a live defence against the shared snapshot emptying under a
mounted panel — closing the project does exactly that — rather than a state reached by navigating.</p>
<h2 id="the-matrix">The Matrix</h2>
<p>Keyed on the node id, because one tree label is itself conditional: the generic tool-offsets leaf is
relabelled when the Siemens <code>$TC_DP</code> table resolves, so an index keyed on labels would be
conditional on the very thing it indexes. Rows are in the order the builder pushes them.</p>
<p>Machine-plane rows name the page that documents the node. The program-data leaves have no page of
their own, so those rows point at the Layout section below, which names every one of them in tree
order.</p>
<table>
<thead>
<tr>
<th>Node id</th>
<th>Tree label</th>
<th>Gate</th>
<th>Dependency probed</th>
<th>Brands</th>
<th>Documented by</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>equipment/controller/machine/brand</code></td>
<td>Controller Brand</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td><a class="xref" href="machine/brand-switch.html">Controller Brand</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/limits</code></td>
<td>Machine Limits (Stroke)</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td><a class="xref" href="machine/per-axis-tables.html">Per-Axis Tables</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/rapid</code></td>
<td>Rapid Feedrates</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td><a class="xref" href="machine/per-axis-tables.html">Per-Axis Tables</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/home</code></td>
<td>Home / G28 Reference</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td><a class="xref" href="machine/per-axis-tables.html">Per-Axis Tables</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/tool-change</code></td>
<td>Tool-Change Position</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td><a class="xref" href="machine/per-axis-tables.html">Per-Axis Tables</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/parameters</code></td>
<td>Controller Parameters</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td><a class="xref" href="machine/interface-parameters.html">Interface Parameters</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/m-codes</code></td>
<td>M-Code Declarations</td>
<td><code>hasNativeTable</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a></td>
<td>all five</td>
<td><a class="xref" href="machine/m-code-declarations.html">M-Code Declarations</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/canned-cycle</code></td>
<td>Canned Cycle (Peck)</td>
<td><code>hasCannedCycle</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.ICannedCycleConfig.html">ICannedCycleConfig</a></td>
<td>all five</td>
<td><a class="xref" href="machine/interface-parameters.html">Interface Parameters</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/block-skip</code></td>
<td>Block Skip / Delete</td>
<td><code>hasBlockSkip</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.IBlockSkipConfig.html">IBlockSkipConfig</a></td>
<td>Fanuc, Siemens, Syntec, Mazak</td>
<td><a class="xref" href="machine/program-reading.html">Program Reading</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/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>all five</td>
<td><a class="xref" href="machine/program-reading.html">Program Reading</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/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>Siemens</td>
<td><a class="xref" href="machine/indexing-positions.html">Indexing Position Tables</a></td>
</tr>
<tr>
<td><code>equipment/controller/machine/native</code></td>
<td>Parameters (Native)</td>
<td><code>hasNativeTable</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a></td>
<td>all five</td>
<td><a class="xref" href="machine/native-parameters.html">Native Parameters</a></td>
</tr>
<tr>
<td><code>equipment/controller/program-data/work-coordinates</code></td>
<td>Work Coordinates (G54…)</td>
<td>always</td>
<td>—</td>
<td>all five</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/tool-offsets</code></td>
<td>Tool Offsets, read as Tool Offsets (ISO G43 H) under <code>hasSiemensToolOffsets</code></td>
<td>always; label switched by <code>hasSiemensToolOffsets</code></td>
<td>—</td>
<td>all five</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/siemens-tool-offsets</code></td>
<td>Tool Offsets ($TC_DP)</td>
<td><code>hasSiemensToolOffsets</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a></td>
<td>Siemens</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/tool-names</code></td>
<td>Tool Names</td>
<td><code>hasSiemensToolOffsets</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a></td>
<td>Siemens</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/datum-presets</code></td>
<td>Datum Presets (Q339)</td>
<td><code>hasDatums</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a></td>
<td>Heidenhain</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/datum-shifts</code></td>
<td>Datum Shifts (D)</td>
<td><code>hasDatums</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a></td>
<td>Heidenhain</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/frames</code></td>
<td>Frames (Siemens)</td>
<td><code>hasFrames</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a></td>
<td>Siemens</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/retained-variables</code></td>
<td>Retained Common Variables</td>
<td><code>hasRetainedVariables</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Fanuc.RetainedCommonVariableTable.html">RetainedCommonVariableTable</a></td>
<td>Fanuc, Syntec, Mazak</td>
<td>Layout below</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/r-parameters</code></td>
<td>R Parameters</td>
<td><code>hasRParameters</code></td>
<td><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensRParameterTable.html">SiemensRParameterTable</a></td>
<td>Siemens</td>
<td>Layout below</td>
</tr>
</tbody>
</table>
<p>Three gates never read false on a shipped preset. <code>hasNativeTable</code> is true wherever any brand
parameter table resolves, which every preset provides. <code>hasSubprogramFolders</code> is true because every
one of the five lists carries <a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Generic.SubProgramFolderConfig.html">SubProgramFolderConfig</a>.
<code>hasCannedCycle</code> is true on Fanuc and Mazak through
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a>, on Syntec through
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a>, and on Siemens and Heidenhain through
the <a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Generic.FallbackConfig.html">FallbackConfig</a> those two presets carry. So M-Code
Declarations, Parameters (Native), Canned Cycle (Peck) and Subprogram Folders are gated in code and
unconditional in practice — they can only go missing on a runner composed by hand or loaded from a
file.</p>
<p>Two answers are decided by list <em>order</em>, not only by membership, because the snapshot takes the
first match rather than any match: the parameter table behind <code>hasIndexingTables</code> and the ISO
coordinate provider behind <code>workCoordinateKind</code> are both first-of-type lookups over the effective
list. Each has exactly one candidate in every shipped preset, so order settles nothing there; it
becomes load-bearing only on a runner carrying two.</p>
<h2 id="the-flag-that-does-double-duty">The Flag That Does Double Duty</h2>
<p><code>hasSiemensToolOffsets</code> is the only flag that both adds nodes and rewrites one. It adds Tool Offsets
($TC_DP) and Tool Names, and at the same time switches the ungated tool-offsets leaf from the label
key <code>softNc.node.toolOffsets</code> to <code>softNc.node.toolOffsetsIsoH</code>, so the row reads &ldquo;Tool Offsets (ISO
G43 H)&rdquo; instead of &ldquo;Tool Offsets&rdquo;. The node id is untouched by the relabel. The reason is that the
two ledgers coexist on Siemens — the <code>(T,D)</code>-keyed <code>$TC_DP</code> table beside the single-index ISO G43 H
table — and an unqualified &ldquo;Tool Offsets&rdquo; would blur them into one.</p>
<h2 id="the-one-brand-shaped-hole">The One Brand-Shaped Hole</h2>
<p>Block Skip / Delete is the only node that exists on four brands and not the fifth.
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Generic.GenericBlockSkipConfig.html">GenericBlockSkipConfig</a> is the sole implementer of
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.IBlockSkipConfig.html">IBlockSkipConfig</a> in the engine, and the Heidenhain preset is the one
brand list that does not carry it. Reads of the block-skip endpoint answer absent there, and writes
answer unsuccessful with a message naming the missing dependency.</p>
<h2 id="mazak-resolves-the-fanuc-table">Mazak Resolves the Fanuc Table</h2>
<p>The Mazak preset's dependency list differs from the Fanuc one only in the brand token it carries: it
proxies the same Fanuc parameter table. Both proxies resolve the same concrete per-case table, so
every downstream consequence follows — the same native parameter numbering and prefix, the same
<code>fanuc</code> work-coordinate storage model, and the same rows under a Mazak brand badge. It is also the
one brand pair whose per-case parameter table survives a switch between them, because the sweep that
removes tables the new runner references through no proxy finds this one still referenced.</p>
<h2 id="flags-computed-and-read-by-nothing">Flags Computed and Read By Nothing</h2>
<p>Five snapshot fields are computed by the web service, typed and parsed by the client, and read by no
component. They stay part of what the endpoint returns, so a client other than this one still
receives them.</p>
<ul>
<li><code>hasProject</code> — a null check on the loaded project rather than a dependency probe. It is the only
field the early-return snapshot sets when no runner resolves. The panels gate on the runner flag
instead, and the shared state watches the project store directly.</li>
<li><code>hasCutterComp</code> — would report a cutter-comp startup type on the Fanuc and Syntec tables.</li>
<li><code>hasToolAxisDirection</code> — would report a tool-axis-direction parameter on the Heidenhain table.</li>
<li><code>hasIterationGuards</code> — would report the iteration guards.</li>
<li><code>nativeIdPrefix</code> — <code>#</code> on Fanuc and Mazak, <code>Pr</code> on Syntec, <code>MD</code> on Siemens, <code>MP</code> on Heidenhain.</li>
</ul>
<p>The screen gets the other four elsewhere. The controller-parameters panel shows its cutter-comp,
tool-axis and iteration-guard controls only where its own read returns a non-null value for each, and
the native-parameters panel renders the prefix from its own read, which recomputes the same switch
server-side.</p>
<p>Two of the snapshot's own doc comments disagree with the code that ships, and the code is what the
tree obeys.</p>
<ul>
<li>The subprogram-folder flag is annotated as a Fanuc-family trait. All five presets carry the
folder config: the Siemens entry is the <code>L</code>-call lookup root and the Heidenhain entry the
<code>CALL PGM</code> lookup root, each with its own comment saying so. The node grows on every brand.</li>
<li>The iteration-guard flag is annotated as the Fanuc GOTO / WHILE-DO guards. The probe also matches
the Siemens jump guard, and matches neither
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.Heidenhain.HeidenhainGotoIterationDependency.html">HeidenhainGotoIterationDependency</a> — which the
Heidenhain preset does carry — nor the Fanuc WHILE-DO guard. The flag would therefore misreport
Heidenhain even if something read it.</li>
</ul>
<h2 id="what-this-table-cannot-check">What This Table Cannot Check</h2>
<p>The two halves of every brand column live in different repositories and nothing in either build
joins them.</p>
<ul>
<li><strong>The flags are computed in the web service.</strong> The snapshot builder in
<code>Mech/SoftNcRunnerController.cs</code> holds one probe per flag. Changing a probe's type, or
reordering the first-of-type lookups it depends on, moves a whole row of this table.</li>
<li><strong>What each brand carries is written in the engine.</strong> The five brand presets in
<code>HiMech/NcParsers/SoftNcRunner.cs</code> are literal dependency lists. Adding or removing one entry
changes a brand column here, with no compile error and no failing test to mark it.</li>
</ul>
<p>The brand columns above are read from those preset lists. Re-deriving them means diffing those two
files against each other — the probe list on one side, the five preset lists on the other — and
nothing else reports the drift.</p>
<p>Two narrower falsifiers sit under the same join. The interface list on
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.ControllerParameterTableBase.html">ControllerParameterTableBase</a> is why the machine plane's ungated
leaves have a live table behind them on every brand; if a brand table ever implements
<a class="xref" href="../../../api/Hi.NcParsers.Dependencys.IBlockSkipConfig.html">IBlockSkipConfig</a>, the Heidenhain hole closes silently. And the label
strings in the English locale bundle currently match the hard-coded labels in the branch builder
one for one; the tree renders the label key, so a drift there changes what the tree reads without
changing any id in this table.</p>
<h2 id="layout">Layout</h2>
<ul>
<li>Control Tree — the <strong>General Setup</strong> root
<ul>
<li><strong>Controller</strong> Node Row — the branch root; its editor carries the Object-Management ⋮ button,
the brand badge and the runner-file caption, or the hourglass no-runner block
<ul>
<li><strong>Machine / Controller</strong> Group Stem — intro line plus a clickable list of its children
<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 — gated</li>
<li><strong>Canned Cycle (Peck)</strong> Node Row — gated</li>
<li><strong>Block Skip / Delete</strong> Node Row — gated; absent on Heidenhain</li>
<li><strong>Subprogram Folders</strong> Node Row — gated</li>
<li><strong>Indexing Position Tables</strong> Node Row — gated; Siemens only</li>
<li><strong>Parameters (Native)</strong> Node Row — gated</li>
</ul>
</li>
<li><strong>Program Data</strong> Group Stem — intro line plus a clickable list of its children
<ul>
<li><strong>Work Coordinates (G54…)</strong> Node Row</li>
<li><strong>Tool Offsets</strong> Node Row — reads <strong>Tool Offsets (ISO G43 H)</strong> where the Siemens <code>$TC_DP</code>
table resolves</li>
<li><strong>Tool Offsets ($TC_DP)</strong> Node Row — gated; Siemens only</li>
<li><strong>Tool Names</strong> Node Row — gated; Siemens only</li>
<li><strong>Datum Presets (Q339)</strong> Node Row — gated; Heidenhain only</li>
<li><strong>Datum Shifts (D)</strong> Node Row — gated; Heidenhain only</li>
<li><strong>Frames (Siemens)</strong> Node Row — gated; Siemens only</li>
<li><strong>Retained Common Variables</strong> Node Row — gated; absent on Siemens and Heidenhain</li>
<li><strong>R Parameters</strong> Node Row — gated; Siemens only</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The rows marked <em>gated</em> are the ones this page's table keys; the rest are the fixed core. Every row
is a plain label with no icon and no checkbox, since the tick column belongs to the mission branch
alone.</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: it mints every
node id in the table above, holds each leaf's gate, carries the tool-offsets relabel, and returns
an empty child list both when no runner resolves and when the snapshot request throws.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — declares the
<code>equipment/controller</code> root that prefixes every id in the table, and rebuilds the branch when a
panel reports a structural change scoped to it.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the snapshot type, its empty value and its parser; the
five fields with no reader are declared, defaulted and parsed here.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the leaf panels gate on,
the project watch that empties it, and the brand switch and Object-Management install that re-read
it.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcRunnerRootPanel.vue</code> — the branch root's editor and
the no-runner block the branch collapses to.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner body every
leaf panel falls back to.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcWorkCoordinatesPanel.vue</code> — the one consumer of the
snapshot's work-coordinate kind.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcControllerParamsPanel.vue</code> — gates the cutter-comp,
tool-axis and iteration-guard controls on its own read rather than on the snapshot flags.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcNativeParamsPanel.vue</code> — renders the native id
prefix from its own read.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the node label strings the tree renders, the two group
intros, and the empty-state bodies.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — builds the snapshot: one probe per flag over the
proxy-resolved dependency list, the early-return form used when no runner resolves, and the brand
switch that swaps the whole runner and sweeps the orphaned per-case tables.</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, and the
proxy resolution every flag probe reads through.</li>
<li><code>HiMech/NcParsers/ControllerPresetWriter.cs</code> — the brand token list and the factory that turns one
token into a preset runner, for the shipped preset files.</li>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the machine-config interfaces
every brand table inherits, which is why the machine plane's ungated leaves have data on all five
brands.</li>
<li><code>HiMech/NcParsers/Dependencys/CncBrandDependency.cs</code> — the brand marker the snapshot reports, and
the five brand tokens.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/GenericBlockSkipConfig.cs</code> — the sole implementer of the
block-skip interface, and therefore the whole reason one node is missing on one brand.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/FallbackConfig.cs</code> — the peck-retraction provider that makes
the canned-cycle gate true on the two brands whose own table has none.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/SubProgramFolderConfig.cs</code> — the folder config every preset
carries, against the snapshot comment that calls it a Fanuc-family trait.</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
the extra interfaces that decide the canned-cycle, indexing and work-coordinate answers.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the per-case resolution the
Fanuc and Mazak presets share, which is why those two survive each other's brand switch.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Controller Branch</a> — the branch this table indexes, its two planes and the runner behind them</li>
<li><a class="xref" href="editing-contract.html">Editing Contract</a> — the rules every leaf panel in this table obeys when it reads, commits and rolls back</li>
<li><a class="xref" href="machine/index.html">Machine and Controller Plane</a> — the machine plane, whose leaves make up most of this table</li>
<li><a class="xref" href="program-data/index.html">Program Data Plane</a> — the program-data plane, whose per-case leaves are the rest of this table's rows</li>
<li><a class="xref" href="machine/brand-switch.html">Controller Brand</a> — the control that changes which rows of this table exist, and what it resets on the way</li>
<li><a class="xref" href="machine/per-axis-tables.html">Per-Axis Tables</a> — the four ungated per-axis leaves: stroke limits, rapid rates, home reference and the tool-change position</li>
<li><a class="xref" href="machine/interface-parameters.html">Interface Parameters</a> — the interface parameter form and the canned-cycle leaf, two of the rows above</li>
<li><a class="xref" href="machine/m-code-declarations.html">M-Code Declarations</a> — the M-code declaration leaf, gated by the native-table flag</li>
<li><a class="xref" href="machine/program-reading.html">Program Reading</a> — the block-skip and subprogram leaves, and the one gate that switches a node off for exactly one brand</li>
<li><a class="xref" href="machine/indexing-positions.html">Indexing Position Tables</a> — the one row that appears on a single brand only</li>
<li><a class="xref" href="machine/native-parameters.html">Native Parameters</a> — the native parameter form behind the id prefix this page reports as unread</li>
<li><a class="xref" href="../hidden-controllers.html">Hidden Controller Branches</a> — the other kind of withheld node: the two equipment branches a device preference hides, which a link can reveal where none of these can</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,496 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Editing Contract | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Editing Contract | 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-ControllerEditingContract">
<h1 id="editing-contract">Editing Contract</h1>
<p>Every leaf of the Controller branch edits a different table, and almost all of them edit it the same
way. This page is that shared way: what a leaf panel fetches, when an edit commits, what a failed
write does, what a panel shows while its table is absent, and what it never notices. It owns no
Control-Tree node of its own — it holds over every panel of the Controller branch on
<code>/general-setup</code>: the root at <code>equipment/controller</code>, and every leaf beneath
<code>equipment/controller/machine</code> and <code>equipment/controller/program-data</code>. Each leaf page can then
state only what is its own. The last section is the other half of the contract: every panel that
departs from it.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that these ids are stable across versions. See
<a class="xref" href="../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a> for the migration hop that keeps an older
link resolving.</p>
</div>
<h2 id="the-shape-of-a-leaf-panel">The Shape of a Leaf Panel</h2>
<p>Each leaf panel takes one prop, the tree node, and reads the shared runner snapshot through a
module-singleton composable whose installer it calls synchronously while its script runs rather than
on mount. The installer is idempotent, so whichever panel mounts first pays for it and the rest get
the cache. Only four panels read the node prop at all: two use its id to tell apart the sibling
leaves they serve, and two use it as the scope of the structural change they report.</p>
<p>Beyond that shared snapshot, a panel owns its own table. It imports its reader and its setters
directly from the branch's typed API module and fetches once, in <code>onMounted</code>. There is no store, no
request queue and no batch: one panel, one read, one table. The two panels that fetch nothing — the
branch root and Controller Brand — are exactly the two that have no table of their own and render
off the snapshot alone. How the branch itself is built, rebuilt and selected belongs to the
Control-Tree engine and is described in <a class="xref" href="../../platform/control-tree.html">Control Tree</a>.</p>
<p>Only one panel is mounted at a time. The editor row's remount key is composed from the node id, the
node key and the mission stamp; controller nodes carry an empty key and no mission record, so the
key reduces to the node id alone. Moving the selection therefore remounts and refetches; re-clicking
the row already selected does neither. No controller item type registers a large content view, so
the General Setup content column keeps its own empty hint for every selection in this branch.</p>
<h2 id="when-an-edit-commits">When an Edit Commits</h2>
<p>The commit rule is per control kind, and every panel that uses a given kind uses it the same way.</p>
<p><strong>Numeric fields</strong> are the shared numeric widget, and it commits on blur or on Enter and on nothing
else — never per keystroke. Its full contract, including what a bound violation does to the box, is
<a class="xref" href="../../widget/numeric-input.html">Numeric Input</a>. Three consequences matter here. Enter commits without
moving focus, so leaving the field afterwards commits the same value a second time. The widget
carries no equality guard, so a field that is focused and left unchanged still emits. And the two
halves of the rule are not wired alike: blur is the input component's own event and is raised only
while the field is editable, whereas Enter is a plain listener on the underlying element and still
fires on a read-only one — which is why a read-only cell can still commit.</p>
<p><strong>Checkboxes, toggles and selects</strong> commit immediately, on the click or the pick. That covers the
block-skip layer boxes, the tool-change <em>Stays put</em> box, the M-code tool-change box and its Spindle
and Coolant selects, the tool-word trigger toggle, the cutter-compensation and tool-axis selects,
and the tool-offset <em>Set ideal offset dependent on tool house</em> toggle.</p>
<p><strong>Plain text fields</strong> have no shared widget and are wired twice over. The M-code <em>Not-simulated
note</em> commits on the field's own change event, which carries the string and fires on Enter or on
leaving a changed field. The two subprogram folder inputs bind blur and Enter explicitly, and commit
as a pair.</p>
<p><strong>Row actions</strong> — the datum reset-to-zero button, the work-coordinate P0 and M0 buttons, a delete
button, an add button — commit on the click, and are not optimistic: they write first and touch the
local rows only after the request resolves.</p>
<p>Two panels do not commit per control at all; they stage a draft behind an explicit button, and both
are named below.</p>
<h2 id="the-optimistic-write">The Optimistic Write</h2>
<p>A per-control commit follows one fixed shape. The handler captures the current value, assigns the
new one so the screen updates at once, awaits the setter, and on failure restores the captured value
and raises a toast. Nothing is queued and nothing is retried.</p>
<p>Two details of that shape are easy to misread from the screen:</p>
<ul>
<li><strong>The write is row-scoped even though the handler is per cell.</strong> Editing one cell of a work
coordinate, a datum row, a frame or a tool-offset row sends the whole row or the whole XYZ triad
as it stands after the edit. An M-code edit likewise sends all four declaration fields, which is
why the wire format is a whole-declaration replace rather than a patch.</li>
<li><strong>The rollback restores what the handler captured, which is usually narrower than what it sent.</strong>
A work-coordinate, datum, frame or tool-offset handler captures the one cell it edited and puts
back only that, though the request carried the whole row. Three handlers capture the whole set
they send and restore it intact — the M-code declaration row, the tool-change <em>Stays put</em> pair,
and the two subprogram folders. Either way, a failure the server had partly applied leaves the two
sides disagreeing; the panel's recovery is the toast and a later remount, not a re-read.</li>
</ul>
<p>Adds and removes are outside this shape, and the local rows never move before the request resolves.
Two panels then append the returned row themselves — the generic tool-offset table and the Siemens
<code>$TC_DP</code> table, the two whose add hands back a freshly minted key. The other five re-read their whole
table instead: M-Code Declarations, Tool Names, R Parameters, Retained Common Variables and
Parameters (Native). A delete usually filters the row out in place; Parameters (Native) re-reads
there too. Three deletions are guarded by a confirmation dialog first: a tool-offset row, a Siemens
<code>$TC_DP</code> row, and a native parameter. M-code declarations, tool names, R parameters and retained
common variables delete on the click with no confirmation.</p>
<h2 id="when-a-write-fails">When a Write Fails</h2>
<p>The branch's REST surface answers a missing dependency inside a success envelope rather than with an
error status: the dependency lookup returns HTTP 200 carrying <code>success: false</code> and a message naming
the type it could not find, such as <code>No IStrokeLimitConfig on the active runner</code>, and an exception
raised inside the same lookup is reported the same way. The shared fetch helper turns both a non-2xx
status and a <code>success: false</code> body into a thrown error, so a panel cannot tell the two apart; the
first arrives with the status prefixed to the message, the second as the server's own sentence.</p>
<p>What the user sees is one negative toast, three seconds, composed as the panel's localized context
followed by the raw server message — the locale bundle stores each error key as the bare context and
the code appends the separator and the message. A failed write leaves no inline error state, no
retry affordance and no dirty marker: once the toast expires, nothing on screen records that it
happened. (The <em>unsaved</em> badge and the staged-brand banner on the two draft panels mark a pending
edit, not a failed one.) A console error accompanies the toast.</p>
<p>The server message is not localized. Only a coded error payload can be re-rendered in the app
locale, and the envelope this branch answers with carries no code, so its English sentence is
appended verbatim under any language. The two writes that replace the whole runner answer with real
status codes instead of the envelope: installing one through Object Management, and switching brand.
Of the codes they raise, only <em>not found</em> with no project loaded carries the coded payload — the
conflict raised rather than swapping the parser under a playing NC program, and the refusals of an
unknown key or an unknown brand, are all bare English sentences. One further status code sits on the
datum routes, a defensive 400 for a table segment that is neither <code>preset</code> nor <code>shift</code>; the panel
sends only those two, so nothing reachable from the branch produces it.</p>
<h2 id="the-two-empty-layers">The Two Empty Layers</h2>
<p>A leaf panel opens with two guards, in this order.</p>
<p><strong>Layer one is the shared snapshot.</strong> The shared empty state renders the single line <em>&quot;No NC runner
— load a project first.&quot;</em> whenever the snapshot reports no runner. Every leaf panel carries it; the
branch root is the one panel that does not, because it renders a richer block of its own.</p>
<p>Reaching that line by navigating is not possible: a leaf node is created only when the snapshot
already reported a runner, and a deep link naming an id the built tree lacks is refused. The guard
is live rather than navigational — it answers the snapshot emptying under a panel that is already
mounted, which is what closing the project does.</p>
<p><strong>Layer two is the panel's own read.</strong> Where the runner resolves but its table does not, the panel
renders one line naming the absent table — <em>&quot;No block-skip config on the active runner.&quot;</em>, <em>&quot;No
Siemens frame table on the active runner.&quot;</em>, <em>&quot;No tool-offset table on the active runner.&quot;</em>, and so
on, one string per table. Three panels key that line on one table: Controller Parameters, M-Code
Declarations and Parameters (Native) all gate on the brand's controller parameter table and all
render <em>&quot;No controller parameter table on the active runner.&quot;</em> Controller Parameters is the loosest
fit — its two iteration-guard fields resolve through dependencies of their own, and only its
presence flag is the parameter table.</p>
<p>Controller Brand, which reads no table, has no second layer at all; every other leaf has one. Only
after both guards does the editor body render. One panel keys its second layer on something other
than its read's presence flag, and it is named below.</p>
<h2 id="what-a-mounted-panel-does-not-see">What a Mounted Panel Does Not See</h2>
<p>The shared snapshot is fetched once per load of the application. The installer's flag lives at module
scope, so a later call does nothing and only a browser reload starts over, and the snapshot is
re-read on exactly three events afterwards: the project store's has-a-project flag turning <em>true</em>, an
Object-Management install of a runner, and a brand switch. The flag turning false re-reads nothing —
it clears the snapshot in place. No panel re-reads it either; the composable exposes a reload
function that nothing in the branch calls.</p>
<p>Three consequences follow, and they are the contract's sharpest edges.</p>
<ul>
<li><strong>A project-to-project load does not refresh it.</strong> The has-a-project flag is derived from the
project path being non-empty, and loading another project assigns the new path directly, so the
flag never leaves true and the watch never fires. The page itself is destroyed and rebuilt, because
the layout keys its keep-alive on a project epoch — but the rebuilt panels call the same
now-idempotent installer, which does nothing. Meanwhile the branch builder makes its own,
independent request for the same snapshot, so the tree shape can be built from the new project
while every leaf gates on the old one.</li>
<li><strong>Nothing pushes changes at a mounted panel.</strong> No panel holds a watcher, a poll or a status-hub
subscription over its table. A write made by another browser tab, by an NC run, or by the server's
own sweep after a brand switch is invisible until the panel is remounted by moving the selection
away and back.</li>
<li><strong>A panel's own edits do not update the snapshot or the branch.</strong> Deleting the last row of a table
leaves both the cached snapshot and the tree branch as they were until one of the three refresh
events happens.</li>
</ul>
<p>Because the shared snapshot is a reactive reference, a panel does track it live once it changes —
but with one panel mounted at a time, the change that matters in practice is a project close.</p>
<h2 id="the-table-primitive">The Table Primitive</h2>
<p>Where a leaf renders a table it is Quasar's markup table — dense, flat, bordered — with a
hand-written header and body and a repeat over the rows. No panel in the branch uses the data-table
component, and the four data tables left in the application are the legacy controller tabs this
branch supersedes. The practical difference is that these tables have no built-in sort, no pagination
and no column menu: what the header says is what the column is.</p>
<p>Key columns are rendered as plain text and cannot be edited: the tool and edge numbers of a <code>$TC_DP</code>
row, a tool name, an <code>R</code> number, a <code>#</code> variable number, a datum row index, a native parameter id, and
the axis names of every per-axis table, which follow the machining chain rather than the panel. The
one renameable key is the generic tool-offset row's tool number, and it is guarded against duplicates
on both sides of the wire.</p>
<p>A table that can grow carries an add control, and the branch spells it three ways. Five carry a true
footer below the table — the new row's fields, then a primary button: <strong>Add / Set</strong> on Tool Names,
R Parameters, Retained Common Variables and Parameters (Native), <strong>Declare</strong> on M-Code Declarations.
Two carry a fieldless button in the toolbar above the table instead, because the server mints the
key: <strong>Add</strong> on Tool Offsets and <strong>Add Tool</strong> on the Siemens <code>$TC_DP</code> table. The last is <strong>Add
Position</strong> on Indexing Position Tables, which appends an empty row to the local draft and sends
nothing. Two tables carry a <strong>Show all</strong> toggle that hides an all-zero extended tail, on Work
Coordinates and on Frames.</p>
<h2 id="nothing-guards-a-switch-away">Nothing Guards a Switch Away</h2>
<p>No panel in the branch registers the Control-Tree host's before-switch gate, and none exposes
anything to the host at all. Selecting another node therefore always succeeds immediately. For the
per-control panels that is exactly right — every edit is already on the server. For the two draft
panels it means an unsaved draft and a staged brand are discarded silently when the selection moves,
with no prompt and no warning.</p>
<h2 id="where-the-wave-is-not-uniform">Where the Wave Is Not Uniform</h2>
<p>Everything above is the rule. What follows is every place the branch departs from it, panel by
panel. A contract page without this section invites the next editor to tidy it into a uniformity that
was never true.</p>
<h3 id="machine-limits--rapid-feedrates--home-reference--the-presence-flag-is-discarded">Machine Limits / Rapid Feedrates / Home Reference — the presence flag is discarded</h3>
<p>The axis-table panel serves three leaves and is the one panel whose second empty layer is not its
read's presence flag: it keys on the row count instead. The rows and the flag are independent on the
server, which builds one row per chain axis whether or not the backing config resolves and reports
presence separately. So a runner with axes but no stroke-limit, rapid-feedrate or home config still
renders a full, editable table, and every commit fails with a toast naming the missing dependency.
What fills the cells differs by leaf: the stroke-limit and home values are nullable and come through
blank, while a rapid rate is a plain number the server defaults to <code>0</code>, so that one leaf shows a
table of zeros that never signals the absence at all.</p>
<h3 id="controller-brand--staged-confirmed-and-destructive-by-design">Controller Brand — staged, confirmed, and destructive by design</h3>
<p>Controller Brand is one of two panels with no table read of its own, the only panel with no local
error handler, and one of the two whose write replaces the whole runner — the branch root's
Object-Management install is the other. The select stages a pending brand rather than applying it;
<strong>Revert</strong> drops the staging, an orange banner spells out what the switch destroys, and <strong>Apply
brand</strong> opens a confirmation dialog before the request. Its failures surface through the shared
composable's global notification rather than the panel's own, so the console line names the shared
state and not the panel.</p>
<h3 id="controller-root--its-own-empty-block-its-own-toast">Controller root — its own empty block, its own toast</h3>
<p>The branch root panel does not use the shared empty state. It renders an hourglass over its own
no-runner title and hint. Its error path also differs: it takes an already-composed string from the
Object-Management button, omits the console line, and uses a longer toast than every other panel.</p>
<h3 id="indexing-position-tables--draft-validate-save-whole">Indexing Position Tables — draft, validate, save whole</h3>
<p>The one draft table. Its numeric cells bind straight into a local draft row with no handler, an
<em>unsaved</em> badge appears while the draft differs from what was saved, and nothing reaches the server
until the per-table <strong>Save Table</strong> button, which stays disabled while the draft fails validation. The
validation mirrors the endpoint's own rules — a per-table maximum, strictly ascending values, and a
<code>0 ≤ position &lt; 360</code> range when a modular rotary axis reads the table — and reports the first
violation under the table. A failed save deliberately does <strong>not</strong> roll the draft back: the draft
stays dirty so the edit is not lost, which is the opposite of every other panel.</p>
<p>Its axis-assignment table above the drafts is display-only. The assignment is not editable on this
panel and the endpoint exposes no writer for it; the value is edited as a per-axis integer parameter
in Parameters (Native), which is what the panel's own hint says.</p>
<h3 id="subprogram-folders--two-fields-one-write">Subprogram Folders — two fields, one write</h3>
<p>The two folder inputs are plain text fields committed together on blur or Enter of either one, and
the guard against a redundant write is a saved snapshot of both values rather than a per-field
captured value. Editing one folder writes both.</p>
<h3 id="m-code-declarations--a-third-text-commit-wiring-and-an-add-that-submits-on-enter">M-Code Declarations — a third text-commit wiring, and an add that submits on Enter</h3>
<p>The note cell is the only text cell in the branch that commits through its field's native change
event rather than an explicit blur binding or the numeric widget. The timing matches; the wiring is a
third variant. This is also the only add-row footer whose input submits on Enter.</p>
<h3 id="r-parameters-and-retained-common-variables--the-two-panels-that-commit-a-null">R Parameters and Retained Common Variables — the two panels that commit a null</h3>
<p>Every other numeric handler returns early on a null and treats a cleared cell as no edit. These two
send the null, because an empty value is a real state in both tables — the vacant entry, which a
program reading it reports as an error rather than silently taking as zero.</p>
<h3 id="tool-offsets--a-read-only-column-that-still-writes-and-a-two-call-rollback">Tool Offsets — a read-only column that still writes, and a two-call rollback</h3>
<p>With the tool-house dependence on, the two ideal columns are made read-only rather than hidden. A
read-only field is still focusable and still takes keystrokes, and the widget's Enter binding is a
plain listener on the underlying element rather than the component event blur travels on — so
pressing Enter in one of those cells re-sends the whole row unchanged, while focusing it and leaving
does not, because a non-editable field raises no blur. The tool number column, by contrast, is
swapped for plain text and is genuinely inert. The tool-change position field takes a third route —
it is disabled rather than read-only while its axis stays put, and a disabled field takes no
keystrokes either.</p>
<p>Turning the dependence on also makes two server calls inside one try: the toggle write, then the
refresh from the tool house. If the second fails after the first succeeded — the refresh answers
unsuccessfully when the project carries no tool house — the catch reverts the local toggle while the
server has already committed it.</p>
<h3 id="work-coordinates--the-one-panel-that-writes-outside-the-branch">Work Coordinates — the one panel that writes outside the branch</h3>
<p>Clicking a row picks which coordinate the General Setup canvas marks, and that write goes to the
equipment-display surface rather than to this branch's own. It is also the branch's one call through
the plain-JSON helper instead of the envelope helper, so only a transport failure throws there. The
row click is bound on the whole row with no click-stopping on the cells or the P0 and M0 buttons, so
acting anywhere in a row marks it; a same-id short circuit is what keeps cell editing on the marked
row from writing again on every pass through.</p>
<h3 id="two-truth-sources-for-this-is-siemens">Two truth sources for &ldquo;this is Siemens&rdquo;</h3>
<p>The tree relabels the generic Tool Offsets leaf from the snapshot's <code>$TC_DP</code> flag, while the tool
offsets panel shows its ISO-G43-H caveat, and the controller parameters panel picks its macro-guard
labels, from the brand <em>string</em> instead. The brand marker is a free-form string that can be edited
independently of the dependency list, so the label and the caveat can disagree.</p>
<h3 id="the-error-handler-does-not-agree-with-itself">The error handler does not agree with itself</h3>
<p>Every panel that has a local handler composes the same toast through one signature carrying two
incompatible conventions. Nine pass a localization key and translate inside the handler; eight pass
an already-translated string. Both first parameters are typed as a plain string, so nothing separates
the two conventions at compile time. Nothing user-visible changes; the console line does — the
key-style panels log the raw key path, the string-style panels log the rendered sentence.</p>
<h3 id="the-add-row-footers-disagree-on-two-details">The add-row footers disagree on two details</h3>
<p>Whether Enter submits: only M-Code Declarations binds it. Whether the fields clear after a successful
add: M-Code Declarations clears its code, Tool Names clears both fields, and R Parameters, Retained
Common Variables and Parameters (Native) leave what was typed in place.</p>
<h3 id="a-source-comment-the-code-contradicts">A source comment the code contradicts</h3>
<p>The shared composable's header names the Execution page as the owner of the Controller root. The root
is built by the equipment-scoped host, which only the General Setup page creates, so the code is what
ships. The shared empty state's comment reads like a second case and is not one: the number in it
counts the byte-identical copies its extraction removed, not the call sites it has today, which are
one more.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li>Editor Row — the panel of whichever controller leaf is selected, one at a time
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em>; shown first, from the shared
snapshot</li>
<li>Absent-Table Body — one line naming the table this leaf edits, from the panel's own read</li>
<li>Editor Body
<ul>
<li>Description Caption — the leaf's own one-line explanation, above the table</li>
<li>Toolbar — where the add button carries no fields: <strong>Add</strong> on Tool Offsets, <strong>Add Tool</strong> on
the Siemens <code>$TC_DP</code> table, beside that panel's own toggles</li>
<li>Table — dense, flat, bordered, no sort and no pagination
<ul>
<li>Header Row — the leaf's own columns, over the shared labels <strong>Axis</strong>, <strong>Id</strong>, <strong>Value</strong>,
<strong>Unit</strong> and <strong>Actions</strong></li>
<li>Data Row — a bold plain-text key cell, then one editable cell per column, then the row's
action buttons</li>
</ul>
</li>
<li>Add Row Footer — the new row's fields, then <strong>Add / Set</strong> or <strong>Declare</strong></li>
<li><strong>Show all</strong> Toggle — Work Coordinates and Frames only, revealing the all-zero extended tail</li>
<li>Draft Bar — <strong>Add Position</strong>, <strong>Revert</strong> and <strong>Save Table</strong> with an <em>unsaved</em> badge on
Indexing Position Tables; <strong>Revert</strong> with <strong>Apply brand</strong> on Controller Brand</li>
</ul>
</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/composables/useSoftNcRunner.ts</code> — the shared snapshot every panel gates on: the
module-scope installed flag, the project-presence watch, the three events that re-read it, the
reload no panel calls, and the global notification the brand panel's failures take.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the branch's typed REST wrappers: the snapshot parser, one
reader and one setter family per table, and the whole-row payloads the per-cell handlers send.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the two failure shapes a panel cannot tell apart, and the coded
error rendering that a message without a code falls through.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the numeric field: commit on blur or
Enter, no per-keystroke emit, no equality guard, and the Enter listener that keeps firing where a
read-only field's blur is suppressed.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line, and the
comment that counts the copies its extraction removed rather than its call sites.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: the node ids the
two multi-leaf panels discriminate on, and the empty node key that reduces the remount key to the
id.</li>
<li><code>wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue</code> — the editor row: the remount key,
the one-panel-at-a-time mounting, and the events it wires to the host.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — the before-switch gate no panel in
this branch registers, and the rebuild that a reported structural change triggers.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcAxisTablePanel.vue</code> — the three per-axis leaves, and
the second empty layer keyed on the row count instead of the presence flag.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcBrandPanel.vue</code> — the staged select, the revert, the
confirmation and the structural change it reports.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcRunnerRootPanel.vue</code> — the panel with its own empty
block and its own toast shape.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcIndexingPositionsPanel.vue</code> — the draft table: the
unsaved badge, the client-side mirror of the endpoint's rules, the save that keeps a failed draft,
and the display-only assignment column.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcSubprogramPanel.vue</code> — the two text fields committed
as a pair against a saved snapshot.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcMCodesPanel.vue</code> — the change-event text cell, the
whole-declaration write, and the only add input that submits on Enter.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcToolOffsetsPanel.vue</code> — the read-only ideal columns
that still commit, the renameable key with its duplicate guard, and the two-call dependence toggle.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcWorkCoordinatesPanel.vue</code> — the row click that writes
to the display surface, and the row-scoped triad write.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcRParametersPanel.vue</code> and
<code>wwwroot-src/src/components/controlTree/SoftNcRetainedVariablesPanel.vue</code> — the two panels that
commit a null to vacate an entry.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcSiemensToolOffsetsPanel.vue</code> and
<code>wwwroot-src/src/components/controlTree/SoftNcNativeParamsPanel.vue</code> — the other two confirmed
deletions, and the two add flows that part company: one appends the minted row, the other re-reads
the whole table.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcToolNamesPanel.vue</code>,
<code>wwwroot-src/src/components/controlTree/SoftNcToolChangePanel.vue</code>,
<code>wwwroot-src/src/components/controlTree/SoftNcDatumTablePanel.vue</code> and
<code>wwwroot-src/src/components/controlTree/SoftNcControllerParamsPanel.vue</code> — the conforming shape:
one read on mount, per-control commit, capture-assign-await-restore, one toast. The tool-change
<em>Stays put</em> box is the one handler among them that captures and restores a pair of fields.</li>
<li><code>wwwroot-src/src/api/equipmentSetup.ts</code> — the canvas marker id the work-coordinate row click
writes, outside this branch's own surface and through the plain-JSON helper.</li>
<li><code>wwwroot-src/src/stores/project.ts</code> — the has-a-project flag the shared snapshot watches, and the
direct path assignment that keeps it true across a project-to-project load.</li>
<li><code>wwwroot-src/src/layouts/MainLayout.vue</code> — the project epoch that rebuilds the page without
re-running the idempotent installer.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — every empty line, column header, hint and error context this
branch renders, and the convention that an error key stores the bare context.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared column and action labels the tables reuse.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the dependency lookup that answers a missing
table inside a success envelope, the reads that report presence independently of their rows, the
rapid rate defaulted to zero where its config is absent, the two runner-replacing writes that
answer real status codes instead, and the datum routes' defensive rejection of an unknown table
segment.</li>
<li><code>Common/ApiError.cs</code> — the coded payloads, and why only the no-project answer can be re-rendered in
the app locale.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Controller Branch</a> — the branch these rules hold across, its two planes and the runner behind them</li>
<li><a class="xref" href="machine/index.html">Machine and Controller Plane</a> — the plane whose seven leaf pages this contract lets stay short</li>
<li><a class="xref" href="program-data/index.html">Program Data Plane</a> — the other plane these rules hold across, whose six leaf pages inherit them the same way</li>
<li><a class="xref" href="brand-matrix.html">Brand Matrix</a> — which leaf exists on which brand, and the snapshot flags the first empty layer reads</li>
<li><a class="xref" href="../../widget/numeric-input.html">Numeric Input</a> — the numeric field's own contract: what parses, what the bounds do, and why Enter commits twice</li>
<li><a class="xref" href="machine/brand-switch.html">Controller Brand</a> — the staged, confirmed, whole-runner write that departs furthest from these rules</li>
<li><a class="xref" href="machine/per-axis-tables.html">Per-Axis Tables</a> — the three leaves whose empty layer keys on the row count, and the tool-change position beside them</li>
<li><a class="xref" href="machine/interface-parameters.html">Interface Parameters</a> — the interface parameter form and the canned-cycle field, both plain per-control commits</li>
<li><a class="xref" href="machine/m-code-declarations.html">M-Code Declarations</a> — the whole-declaration write and the change-event text cell</li>
<li><a class="xref" href="machine/program-reading.html">Program Reading</a> — the block-skip boxes and the paired folder inputs</li>
<li><a class="xref" href="machine/indexing-positions.html">Indexing Position Tables</a> — the draft-then-save table, the one panel that keeps a failed edit</li>
<li><a class="xref" href="machine/native-parameters.html">Native Parameters</a> — the confirmed deletions and the add-or-set footer that keeps what was typed</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,409 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Controller Branch | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Controller Branch | 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-Controller">
<h1 id="controller-branch">Controller Branch</h1>
<p>The Controller branch is the SoftNcRunner-native settings face: the controller the project actually
parses NC with, edited one tree node at a time. It lives on the General Setup page at
<code>/general-setup</code> under the Control-Tree id <code>equipment/controller</code>, and grows two plane stems beneath
it — <code>equipment/controller/machine</code> and <code>equipment/controller/program-data</code>. The superseded
HardNcEnv surface still ships as a route of its own at <code>/controller</code>, and edits a different model.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>Anatomy makes no claim that these ids are stable across versions. See
<a class="xref" href="../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a> for the migration hop that keeps an older link
resolving.</p>
</div>
<h2 id="key-models">Key Models</h2>
<ul>
<li><a class="xref" href="../../../api/Hi.NcParsers.NcRunnerSuit.html">NcRunnerSuit</a> — the switchable suit the branch edits: one runner plus the
per-workpiece dependency list the runner's proxy placeholders resolve against. A suit constructs
on the Fanuc preset, and a project file that carried no runner element keeps that.</li>
<li><a class="xref" href="../../../api/Hi.NcParsers.SoftNcRunner.html">SoftNcRunner</a> — the NC pipeline itself, and the object the branch root's
Object-Management menu loads, pastes and saves.</li>
<li><a class="xref" href="../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_PipelineNcDependencyList">PipelineNcDependencyList</a> — what a runner file carries.</li>
<li><a class="xref" href="../../../api/Hi.NcParsers.NcRunnerSuit.html#Hi_NcParsers_NcRunnerSuit_PerCaseNcDependencyList">PerCaseNcDependencyList</a> — what stays on the project.</li>
<li><a class="xref" href="../../../api/Hi.NcParsers.Dependencys.CncBrandDependency.html">CncBrandDependency</a> — the brand marker the root panel's badge
reads. Its <a class="xref" href="../../../api/Hi.NcParsers.Dependencys.CncBrandDependency.html#Hi_NcParsers_Dependencys_CncBrandDependency_Brand">Brand</a> is a plain string with five
declared tokens, so the marker can be edited independently of the dependency list it sits in.</li>
<li><a class="xref" href="../../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SoftNcRunner">SoftNcRunner</a> — the facade setter every install and
every brand switch assigns through. It re-binds the proxies to the suit, stamps the machining
chain's axis codes onto the axis config, and resets the per-session runner state.</li>
</ul>
<p>Every value the branch reads is resolved through
<a class="xref" href="../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_GetEffectiveNcDependencyList_">GetEffectiveNcDependencyList</a><small>(API)</small> rather than the
raw pipeline list: the proxies deliberately implement no machine-config interface, so a consumer
that read the raw list would see a placeholder instead of the table. The tree itself speaks domain
vocabulary only — brand, machine limits, work coordinates. The dependency pipeline, the proxy
indirection and the brand preset lists have no node.</p>
<h2 id="the-two-planes">The Two Planes</h2>
<p>The branch root's children are two <code>Group</code> stems, and neither is ever empty: each carries at least
one leaf that every runner grows. A stem's editor panel is the shared group panel — an intro line
over a clickable list of its children.</p>
<p><strong>Machine / Controller</strong> (<code>equipment/controller/machine</code>) introduces itself as <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>
<p><strong>Program Data</strong> (<code>equipment/controller/program-data</code>) introduces itself as <em>&quot;Data that travels with
the workpiece / project — work-coordinate offsets (G54…), tool offsets and the brand-specific
per-case tables. Kept on the project even when the runner file is swapped.&quot;</em></p>
<h3 id="what-a-runner-file-actually-carries">What a runner file actually carries</h3>
<p>The runner's pipeline list holds two kinds of entry, and the difference decides what an installed
runner file changes.</p>
<p>Plain instances are the runner's own and are replaced outright by an install: the brand marker, the
tool-change position, the block-skip, subprogram-folder and iteration-guard configs a given preset
carries, and — on Siemens and Heidenhain — the generic fallback config those two presets add
because their parameter tables declare no number for the peck clearance. That last entry is why
<strong>Canned Cycle (Peck)</strong> is runner-owned on those two brands, while on the other three the same
clearance is a row of the brand parameter table and stays with the project.</p>
<p>The brand parameter table is reached through a proxy instead. The proxy carries a machine-config
<strong>seed</strong>, and that seed — not the live table — is what the runner file serializes.
<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 yet; a project
that already carries one keeps it. So the per-axis limits, rapid rates, home references, M-code
declarations and native parameters a panel edits are stored on the <strong>project</strong>, and installing a
same-brand runner file leaves them where they are while it does replace the plain instances above.
The Fanuc-family and Syntec parameter tables are per-case because they mix machine configuration
with per-case work-coordinate offsets, which the two planes could not otherwise split. The Siemens
machine-data and Heidenhain tables hold no work coordinates and are per-case for the other half of
the reason: a project's own table wins over the seed, so installing a same-brand runner file
re-binds to the machine data already edited instead of resetting it to the preset's.</p>
<h3 id="what-survives-a-swap">What survives a swap</h3>
<p>The per-case list belongs to the project and survives an install or a brand switch, minus one step:
after the new runner is assigned, per-case tables its proxies resolve nothing for are removed, so a
project switched from Fanuc to Siemens does not keep a retained-common-variable table nothing reads.
The generic tool-offset table survives every switch, because every brand preset proxies it.
Switching back does not restore what the sweep removed. Only the brand parameter table returns from
a seed, deep-cloned out of the new preset's proxy; the per-case tables beside it carry no seed at
all, so their proxies create bare instances — a project switched back to Fanuc gets an empty
retained-common-variable table, not the values it held before.</p>
<h2 id="the-root-panel">The Root Panel</h2>
<p>The <strong>Controller</strong> node's editor is the branch root panel. Its header row carries three things:</p>
<ul>
<li>The <strong>Object Management</strong> menu (<code>⋮</code>), whose entries are Load, Save As, Copy, Paste and XML Mode.
Load and Save As browse the server file system through the shared file-explorer dialog, filtered to
<code>*.Controller</code>, <code>*.SoftNcRunner</code> and <code>*.xml</code>; Save As proposes the name <code>NcRunner.Controller</code>.
Paste checks the pasted object against the expected type <code>Hi.NcParsers.SoftNcRunner, HiMech</code>.
Load, Paste and an XML apply all install the swapped object onto the project and then regrow the
branch. The button is disabled while no runner key is indexed, which is the state with no project
open; with a project but no runner a blank placeholder is indexed so the menu stays reachable as a
Load target.</li>
<li>A <strong>brand badge</strong>, shown only while a runner resolves. It reads the brand marker's string, or
<strong>Unknown brand</strong> when the marker is empty.</li>
<li>A <strong>runner-file caption</strong>. It reads the side-file path recorded on the suit, or <strong>Embedded in
project</strong> when the runner is inlined in the project file, or <strong>No NC runner</strong> when none resolves.
The web service reads that recorded path and never writes it, so a Save As from this menu writes a
file without changing what the caption names — and after an Object-Management install or a brand
switch the caption still names the file the project was loaded with rather than the runner now in
place.</li>
</ul>
<p>Below the separator the panel shows one of two bodies. With a runner it shows the presets hint —
<em>&quot;Machine / controller presets (brand, limits, rapid, home, tool change, parameters) travel with the
runner and can be saved / loaded as a <code>.SoftNcRunner</code> asset via the ⋮ menu. Program data (work
coordinates, tool offsets…) stays with this project's workpiece. Edit each group via this item's
child tree-nodes.&quot;</em> Both hint blocks name <code>.SoftNcRunner</code>, while the file picker offers <code>.Controller</code>
first.</p>
<p>An orange line follows when the snapshot reports no axes: <em>&quot;No machine axes yet — per-axis rows
(limits / rapid / home) are driven by the Machine Tool chain. Attach a machine tool to populate
them.&quot;</em> The axis set is read from the controller parameter table's axis-type rows, which the facade
setter stamps from the <a class="xref" href="../machine-tool.html">Machine Tool</a> chain — adding the chain's axes and
keeping the table's persisted ones. Every brand preset's default table already declares X, Y and Z,
so on a preset-built runner the warning does not appear; it is a live guard for a runner whose table
declares no axis, or which resolves no parameter table at all.</p>
<h2 id="when-no-runner-resolves">When No Runner Resolves</h2>
<p>With no project open the host skips the branch builder entirely, so the Controller node stands alone
with no children, and the root panel shows its own no-runner block: an hourglass, <em>&quot;No NC controller
runner on this project.&quot;</em>, and the hint <em>&quot;Load a project, or use the ⋮ menu → Load to attach a
<code>.SoftNcRunner</code> file.&quot;</em> This block is the panel's own, not the shared empty state the leaf panels use.</p>
<p>With a project open the builder runs and returns no children when the snapshot reports no runner —
and returns exactly the same empty result when the snapshot request throws. Nothing in the branch
shape distinguishes a failed fetch from an absent runner, and neither does the root panel: a failed
load resets the shared snapshot to the empty one, so the panel renders the same no-runner block. The
only signal is the error toast that failed load raises. The builder's own fetch failure raises
nothing, so a request that fails for the builder alone leaves a childless branch under a panel still
showing the brand badge.</p>
<p>In practice a loaded project resolves a runner, since the suit's runner property constructs on the
Fanuc preset and a project file with no runner element keeps that default. The empty branch is
therefore what a reader meets before a project is open rather than a state a loaded project sits in.</p>
<h2 id="how-the-branch-regrows">How the Branch Regrows</h2>
<p>Two panels in the whole branch report a structural change, and both scope it to the branch root:</p>
<ul>
<li>The <strong>root panel</strong>, after an Object-Management Load, Paste or XML apply. It installs the indexed
object onto the project, refreshes the shared snapshot and the runner key, and then emits — whether
or not the install itself succeeded, so the branch always regrows against a fresh snapshot.</li>
<li><strong>Controller Brand</strong>, after a brand switch, and only when the switch reported success.</li>
</ul>
<p>The host resolves the named scope, re-runs the branch builder over it, and adds the node to the
expansion list so the fresh children are visible. Because a brand switch replaces the whole runner,
this is what makes the brand-driven leaves appear and disappear.</p>
<p>A brand switch that fails <em>after</em> the server has already assigned the preset leaves the two halves
out of step: the shared snapshot is reloaded either way, so the badge and the brand select read the
new brand, while the branch was not regrown and still lists the old brand's leaves.</p>
<p>The tree shape and the panels' own gate come from two independent requests for the same snapshot.
The builder calls the endpoint directly; the panels read a module-singleton cache filled once per
SPA session and refreshed on a project-presence change, an Object-Management install and a brand
switch. No panel refreshes it, so an edit that empties a table changes neither the cache nor the
branch until one of those happens.</p>
<p>Both structural writes are refused while an NC program is playing: installing a runner and switching
brand each answer a conflict rather than swapping the parser under a running session.</p>
<h2 id="the-superseded-route">The Superseded Route</h2>
<p>The legacy controller page still ships at <code>/controller/:tab?</code> and is reached from the app menu bar's
<strong>Page → Legacy-Controller</strong> entry. It edits <a class="xref" href="../../../api/Hi.Numerical.HardNcEnv.html">HardNcEnv</a> through its own REST
surface, which is a different model from the runner this branch edits — the two are not two views of
one object, and an edit on one is invisible to the other. Its anatomy is
<a class="xref" href="../../legacy-controller.html">Legacy Controller Page</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 — <code>equipment/controller</code>, built after <strong>Workpiece</strong> and ahead of the two
optional runner-suit leaves
<ul>
<li><strong>Machine / Controller</strong> Group Row — <code>equipment/controller/machine</code>
<ul>
<li><strong>Controller Brand</strong>, <strong>Machine Limits (Stroke)</strong>, <strong>Rapid Feedrates</strong>,
<strong>Home / G28 Reference</strong>, <strong>Tool-Change Position</strong>, <strong>Controller Parameters</strong> — grown for
every runner</li>
<li><strong>M-Code Declarations</strong>, <strong>Canned Cycle (Peck)</strong>, <strong>Block Skip / Delete</strong>,
<strong>Subprogram Folders</strong>, <strong>Indexing Position Tables</strong>, <strong>Parameters (Native)</strong> — each grown
only where the snapshot reports its backing entry. Across the shipped presets only Block
Skip / Delete and Indexing Position Tables ever go missing; the other four are gated in code
and unconditional in practice</li>
</ul>
</li>
<li><strong>Program Data</strong> Group Row — <code>equipment/controller/program-data</code>
<ul>
<li><strong>Work Coordinates (G54…)</strong> and <strong>Tool Offsets</strong> — grown for every runner. The second reads
<strong>Tool Offsets (ISO G43 H)</strong> while the Siemens <code>$TC_DP</code> table resolves, so the two offset
ledgers do not read as one; its node id is unchanged by that relabel</li>
<li><strong>Tool Offsets ($TC_DP)</strong>, <strong>Tool Names</strong>, <strong>Datum Presets (Q339)</strong>, <strong>Datum Shifts (D)</strong>,
<strong>Frames (Siemens)</strong>, <strong>Retained Common Variables</strong>, <strong>R Parameters</strong> — each grown only where
the snapshot reports its backing table</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Controller</strong> Root Panel
<ul>
<li>Object Management Menu Button (<code>⋮</code>) — Load, Save As, Copy, Paste, XML Mode</li>
<li>Brand Badge — the brand marker, or <strong>Unknown brand</strong></li>
<li>Runner File Caption — the recorded side file, <strong>Embedded in project</strong>, or <strong>No NC runner</strong></li>
<li>No-Runner Block — hourglass icon, <strong>No NC controller runner on this project.</strong>, and the
attach-a-file hint; shown instead of the two items below</li>
<li>Presets Hint</li>
<li>No-Axes Warning — shown only while the snapshot carries no axis</li>
</ul>
</li>
<li><strong>Machine / Controller</strong> and <strong>Program Data</strong> Group Panels — the stem's intro line over a
clickable list of its children, each row selecting that node</li>
</ul>
</li>
</ul>
<p>Which brand grows which leaf is <a class="xref" href="brand-matrix.html">Controller Brand Matrix</a>; how a
leaf panel commits an edit is <a class="xref" href="editing-contract.html">Controller Editing Contract</a>.</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/SoftNcRunnerRootPanel.vue</code> — the branch root's editor: the
Object-Management button and its install-then-regrow handler, the brand badge, the runner-file
caption, the no-runner block, the presets hint and the no-axes warning.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the wave's item-type registry and the
branch builder: the two plane stems, the leaves every runner grows, the snapshot flag each further
leaf is grown behind, and the Siemens relabel of the Tool Offsets leaf.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the module-singleton snapshot and runner key,
the idempotent install of the project-presence watch, and the install and brand-switch calls the
two emitting panels use.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the snapshot shape the tree and the panels parse, and the
typed wrappers over the controller's REST surface.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared &ldquo;no runner&rdquo; line the
leaf panels render; the root panel renders its own block instead.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcBrandPanel.vue</code> — the other panel that regrows the
branch: the staged brand select, its confirmation, and the scope it emits.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — builds the <code>equipment/controller</code>
root inside the equipment group, and turns a reported structure change into a branch rebuild.</li>
<li><code>wwwroot-src/src/components/controlTree/GroupInfoPanel.vue</code> — the editor both plane stems use.</li>
<li><code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> — the base item-type registry the wave's own
registry is spread into, and the mapping of the <code>Group</code> item type to that shared editor.</li>
<li><code>wwwroot-src/src/components/widgets/ObjectManagementMenuButton.vue</code> — the <code>⋮</code> menu: the server file
browser and its extension filter, the expected type it sends with a paste, and the events the root
panel listens to.</li>
<li><code>wwwroot-src/src/pages/GeneralSetupPage.vue</code> — the route that creates the <code>equipment</code>-scoped tree
host this branch is built in.</li>
<li><code>wwwroot-src/src/router/routes.ts</code> — the <code>/general-setup</code> route and the still-shipping
<code>/controller/:tab?</code> route beside it.</li>
<li><code>wwwroot-src/src/components/AppMenuBar.vue</code> — the Page menu entry that reaches the legacy route.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — every label, group intro, hint and empty string this branch
renders.</li>
<li><code>wwwroot-src/src/i18n/en/tree.ts</code> — the <strong>Controller</strong> root label.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the branch's REST surface: the snapshot the tree shape is built
from, the index-and-install pair behind Object Management, the brand switch with its carry and
sweep, and the per-group readers and writers the leaves use.</li>
<li><code>Widget/ObjectManagementController.cs</code> — the server half of the <code>⋮</code> menu: the paste that rejects
an object the expected-type string does not admit.</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 nested serialization of both, and the proxy wiring that materialises a per-case
table into a project holding none of that type yet.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the pipeline and the five brand presets whose entries decide
which leaves a brand grows, plus the chain configuration that stamps axis codes onto the axis
config.</li>
<li><code>HiMech/NcParsers/Dependencys/CncBrandDependency.cs</code> — the brand marker behind the badge and the
five declared tokens.</li>
<li><code>HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs</code> — the base whose interface list is
why the per-axis leaves exist on every brand, and the axis-type rows the axis set is read from.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the get-or-create proxy pattern
the parameter tables use: a serialized seed on the runner, the live table on the project.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the facade setter: the hoist of pre-proxy baked
tables, the proxy re-binding, the chain-driven axis stamp and the session reset.</li>
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — where the suit hangs off the project and the load path
that leaves a project with the default preset when its file names no runner.</li>
</ul>
<h2 id="pages">Pages</h2>
<p>Ordered as the branch is read: the two references that hold across every node in it, then the two
plane folders whose leaves have editors of their own.</p>
<ul>
<li><a class="xref" href="brand-matrix.html">Controller Brand Matrix</a> — Which leaf exists on which brand,
the snapshot flag behind each one, and what a brand switch keeps, resets or destroys</li>
<li><a class="xref" href="editing-contract.html">Controller Editing Contract</a> — The rules every leaf panel
in the branch follows — when an edit commits, what a failed write does, how a table is rendered —
and the panels that depart from them</li>
<li><a class="xref" href="machine/index.html">Machine / Controller Plane</a> — The plane that carries the machine
and controller settings: the brand switch, the per-axis tables, the parameter forms, the M-code
declarations, program reading and the indexing tables</li>
<li><a class="xref" href="program-data/index.html">Program Data Plane</a> — The plane that carries the per-case
tables: the work coordinates, the two tool-offset ledgers, the Heidenhain datum rows, the Siemens
frames and the two variable tables</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="brand-matrix.html">Brand Matrix</a> — which leaf exists on which brand, and the snapshot flag
behind each one</li>
<li><a class="xref" href="editing-contract.html">Editing Contract</a> — the commit, rollback and error rules every leaf panel in
this branch follows</li>
<li><a class="xref" href="machine/index.html">Machine and Controller Plane</a> — the plane whose leaves carry the machine and controller
settings, one page per editor</li>
<li><a class="xref" href="program-data/index.html">Program Data Plane</a> — the plane whose leaves carry the per-case tables, and the
ownership rules that keep them on the project</li>
<li><a class="xref" href="../index.html">General Setup Page</a> — the page that hosts this branch, and the equipment items beside it</li>
<li><a class="xref" href="../../platform/control-tree.html">Control Tree</a> — the engine that builds, rebuilds and selects this branch</li>
<li><a class="xref" href="../../legacy-controller.html">Legacy Controller Page</a> — the superseded HardNcEnv route this branch replaces, still shipped
at <code>/controller</code></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,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"}]}
@@ -0,0 +1,464 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Datum Tables | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Datum 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-ControllerDatumTables">
<h1 id="datum-tables">Datum Tables</h1>
<p>Datum Tables is the pair of Heidenhain per-case ledgers the Controller branch grows together: the
preset rows a <code>CYCL DEF 247</code> cycle selects, and the datum shift rows a <code>CYCL DEF 7</code> cycle adds on top
of the selected preset. Both live on the General Setup page at <code>/general-setup</code> under the
Control-Tree ids <code>equipment/controller/program-data/datum-presets</code> and
<code>equipment/controller/program-data/datum-shifts</code> — reached as
<code>?tree=equipment/controller/program-data/datum-presets</code> and
<code>?tree=equipment/controller/program-data/datum-shifts</code> — and the tree labels them
<strong>Datum Presets (Q339)</strong> and <strong>Datum Shifts (D)</strong>. One snapshot flag grows the pair, one component
serves both nodes, and one of the two tables is also the object the Work Coordinates leaf edits.</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="two-nodes-one-component">Two Nodes, One Component</h2>
<p>The branch builder pushes both nodes inside a single test on the snapshot's datum flag, so the two
rows appear and disappear as a pair and never one without the other. That flag probes
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a> over the runner's proxy-resolved dependency
list, and only the Heidenhain preset carries it — which brand grows which leaf is
<a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>Both nodes carry the same item type, so the editor row mounts the same component for either
selection. The component takes its role from the last segment of the node id it is given: an id
ending in <code>datum-shifts</code> makes it the shift editor, and <strong>anything else</strong> makes it the preset editor.
The test is a suffix match with a preset default rather than an equality check over two known ids, so
the preset role is what an unrecognised id falls to.</p>
<p>Four things follow from that role, and nothing else in the panel differs between the two nodes.</p>
<table>
<thead>
<tr>
<th>Decided by the role</th>
<th>Datum Presets (Q339)</th>
<th>Datum Shifts (D)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Which array of the one read is rendered</td>
<td>the preset rows</td>
<td>the shift rows</td>
</tr>
<tr>
<td>The index column's header</td>
<td><code>Q339</code></td>
<td><code>D</code></td>
</tr>
<tr>
<td>The description caption above the table</td>
<td><em>&quot;Datum presets (CYCL DEF 247, Q339 = row). Rows 1–6 double as G54–G59.&quot;</em></td>
<td><em>&quot;Datum shifts (CYCL DEF 7).&quot;</em></td>
</tr>
<tr>
<td>The route segment its writes address</td>
<td><code>preset</code></td>
<td><code>shift</code></td>
</tr>
</tbody>
</table>
<p><strong>One request serves both nodes.</strong> The read returns a presence flag together with <em>both</em> arrays, and
the panel keeps one and discards the other. Selecting the sibling node changes the node id, which
changes the editor row's remount key, so the component remounts and re-fetches the same payload for
the other half. Reading the pair therefore costs two identical requests, and neither node can show a
value the other's request had already returned.</p>
<p>The <code>preset</code> and <code>shift</code> segments are the only two the panel ever sends; the endpoint's rejection of
a third is one of the departures recorded in
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="what-each-table-holds">What Each Table Holds</h2>
<p>Both tables are the same shape — an integer row number against an X / Y / Z translation — and both
are seeded with rows 1 through 20 at zero when the table is constructed. What separates them is which
klartext cycle reads which, and how the value is applied.</p>
<ul>
<li><strong>Datum Presets (Q339)</strong> is the preset store. <code>CYCL DEF 247 DATUM SETTING</code> with <code>Q339=N</code> selects
row <em>N</em>, and its translation becomes the block's active coordinate offset. The same parser claims
the DIN/ISO spelling <code>G247 Q339=+N</code> and stamps the identical cycle record, so both dialects reach
one store.</li>
<li><strong>Datum Shifts (D)</strong> is the shift store. <code>CYCL DEF 7</code> with a <code>#N</code> row index reads row <em>N</em>, and the
translation composes <strong>on top of</strong> the active preset rather than replacing it — the two land in
separate entries of the block's transform chain. <code>CYCL DEF 7</code> written with direct X / Y / Z values
instead of a <code>#</code> index reads no table row at all.</li>
</ul>
<p>The two stores sit on one dependency and are two dictionaries of it, which is why one flag grows two
nodes and one request answers for both.</p>
<p><strong>Neither store is a parameter table.</strong> <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a> does not
derive from the controller parameter base every brand's machine settings hang off, and the Heidenhain
preset carries it as an entry of its own beside the machine-parameter entry. So no datum row has a
native parameter number, and none of these values appears in
<a class="xref" href="../machine/native-parameters.html">Native Parameters</a> — unlike the Fanuc and Syntec work
coordinates, which are parameter addresses and do.</p>
<h3 id="where-the-table-is-stored">Where the table is stored</h3>
<p>The Heidenhain preset does not carry a datum table. It carries
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTableProxy.html">HeidenhainDatumTableProxy</a>, a get-or-create placeholder that installs
a bare new table into the project's per-case list when that list holds none of the type, and resolves
to the project's own table when it does. The proxy holds no seed and serializes as an empty element,
so a controller file saved from the branch root carries none of these values, and installing a
Heidenhain runner file over a Heidenhain project leaves the rows already edited exactly where they
are. The plane's storage model in full is <a class="xref" href="index.html">Program Data Plane</a>.</p>
<h2 id="the-columns-and-the-axis-set">The Columns and the Axis Set</h2>
<p>Each leaf renders one dense markup table: the row-number column, three value columns, and an
unlabelled action column.</p>
<ul>
<li>The row-number cell is bold plain text and cannot be edited. Its header is the role's literal —
<code>Q339</code> or <code>D</code>.</li>
<li>The three value headers read <code>X (mm)</code>, <code>Y (mm)</code> and <code>Z (mm)</code>. Those three and the index literal
beside them are hard-coded rather than translated, so they read the same under any locale, while
the description caption, both empty bodies and the action button's tooltip are translated.</li>
<li>The action column has no header text and holds one button per row.</li>
</ul>
<p><strong>The axis set is fixed at three and does not follow the machine.</strong> A datum row is a three-component
translation on both sides of the wire, so the columns are X, Y and Z whatever the machining chain
declares — a fourth or rotary axis has no column here and no stored datum component. That is the
opposite of the branch's <a class="xref" href="../machine/per-axis-tables.html">Per-Axis Tables</a>, whose rows are the
chain's own axis names. The <code>mm</code> in the header is a label: nothing on the path from the stored
translation to the cell converts anything.</p>
<p>Rows are whatever the read returned, in row-number order. A table built by the proxy opens with
twenty rows of zeros in each node; a table deserialized from a project file carries the rows that
file holds.</p>
<h2 id="editing-a-row">Editing a Row</h2>
<p>Every value cell is the shared numeric field, so it commits on blur or on Enter and never per
keystroke; its parsing, its bounds behaviour and the second commit that follows Enter are
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>. No cell here passes a minimum or a maximum, so a negative
offset is accepted and stored.</p>
<p><strong>The write is the whole row.</strong> A commit assigns the new number into the local row and then sends
that row's three components as they stand to the row's number; a failure restores the one component
the handler captured and raises the branch's standard toast, though the request carried all three.
That shape, the toast it ends in and what a failed write leaves behind are
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<p>A cleared cell parses to null, and <code>Infinity</code> or <code>NaN</code> parse to a non-finite number; the handler
returns before the request in every one of those cases. Nothing is written and nothing is restored,
so the cell is left blank — or showing the literal <code>Infinity</code> — while the stored number stands
unchanged, until the panel is remounted by selecting another node and coming back.</p>
<p>The action button is labelled with the single character <code>0</code> and carries the tooltip <em>&ldquo;Reset to
zero&rdquo;</em>. It writes zeros into all three components of that row through a route of its own rather than
through the row setter, and it is not a delete: the row stays, at zero. It is also the one write on
these two leaves that is not optimistic — the request resolves first, and only then are the three
local cells set to zero. No confirmation guards it.</p>
<p>Neither leaf has an add control, a delete control, a draft or a save button, and the component
declares no events at all, so no edit made here rebuilds the branch or marks anything dirty.</p>
<h2 id="the-preset-table-is-also-the-work-coordinate-table">The Preset Table Is Also the Work-Coordinate Table</h2>
<p>This is the pairing with <a class="xref" href="work-coordinates.html">Work Coordinates</a>, and it holds for
exactly one of the two tables.</p>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a> implements
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a>, and the work-coordinate endpoint reads and
writes through the <em>first</em> implementer of that interface in the runner's proxy-resolved list. On the
Heidenhain preset the datum proxy is the second entry of the dependency list, immediately after the
brand marker, and no other entry that preset carries implements the interface — the Heidenhain
machine-parameter table does not. So on Heidenhain the object behind Work Coordinates is this datum
table, and the snapshot's coordinate-kind field reports the Heidenhain arm for that same reason.</p>
<p>The alias is narrow, and one-directional in coverage.</p>
<ul>
<li><strong>Datum Presets (Q339) aliases.</strong> The ISO face maps <code>G54</code> through <code>G59</code> onto preset rows 1 through
6, in order, on both the read and the write, and enumerates no id outside that series — one for
each of those six rows the table holds, which is all six unless a stored project table omits one of
them. Editing the <code>Q339</code> row 3 cells changes what Work Coordinates shows for <code>G56</code>, and an edit
made on <code>G56</code> changes row 3 — one instance, one cell, two faces. The row-zeroing button reaches the
same cells.</li>
<li><strong>Datum Shifts (D) does not alias.</strong> Nothing on the ISO face touches the shift store: the id
enumeration reads the preset dictionary only, and both the offset getter and the offset setter
resolve to preset rows. The shift table has exactly one editing surface, the leaf on this page.</li>
<li><strong>Preset rows 7 through 20 have no work-coordinate face either.</strong> The ISO face reserves six rows
for the <code>G54</code> series and enumerates no id past <code>G59</code>, so fourteen preset rows and all twenty shift
rows are reachable from this page and from nowhere else on the branch.</li>
</ul>
<p>The two faces are never on screen together — the editor row mounts one panel at a time — and each
panel reads once on mount, so moving the selection between the two nodes is itself the refresh. What
a mounted panel therefore does not see is
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h3 id="what-a-brand-switch-does-to-the-pair">What a brand switch does to the pair</h3>
<p>The brand switch's carry option reads the outgoing runner's coordinate provider and writes the values
into the incoming one, for the ids the incoming provider enumerates. On this table that means at most
six values, landing in preset rows 1 through 6; a switch away from Heidenhain reads those same six
rows out. <strong>No shift row is ever carried, and neither are preset rows 7 through 20.</strong></p>
<p>A switch away from Heidenhain then sweeps the datum table itself, because no other brand preset
proxies it — both stores go, and switching back materializes a fresh table of zeros rather than
restoring them. The staged select, its confirmation and the rest of that operation are
<a class="xref" href="../machine/brand-switch.html">Brand Switch</a>.</p>
<h2 id="what-the-rows-feed">What the Rows Feed</h2>
<p>At run time both stores are read and neither is written. No syntax in the parse pipeline writes
either dictionary — every writer is one of the four surfaces named below — so playing a program
cannot change a datum row.</p>
<p><strong>The preset path.</strong> A <code>CYCL DEF 247</code> block resolves its <code>Q339</code> number — a literal, or the number a
Q-expression evaluated to — looks the row up, and writes a synthetic coordinate id naming that row
alongside the translation. The shared ISO coordinate syntax then keeps the offset alive on following
blocks by re-resolving that synthetic id against the same table, which is how a mid-program edit to a
row takes effect on the blocks after the declaration. A <code>CYCL DEF 247</code> whose <code>Q339</code> is not a number
resolves nothing, leaves the active datum unchanged, and reports the validation warning <em>&quot;CYCL DEF
247 without a literal Q339 preset number; the active datum is kept unchanged.&quot;</em></p>
<p><strong>A successful preset selection clears the active shift.</strong> That is the one path in the cycle handler
that does not carry the previous block's shift forward.</p>
<p><strong>The shift path.</strong> A <code>CYCL DEF 7</code> block carrying a <code>#</code> index reads that shift row and composes it as
a second, separate transform entry, so preset and shift add rather than replace. On blocks with no
cycle the shift is carried modally, and a numbered shift re-resolves from the table on every block,
exactly as the preset does. A <code>CYCL DEF 7</code> written with direct values carries them forward verbatim
instead and needs no datum table at all; cancelling is a zero shift. A <code>CYCL DEF 7</code> carrying neither
form reports <em>&quot;CYCL DEF 7 carries neither a #-table index nor direct X/Y/Z values; the active datum
shift is kept unchanged.&quot;</em></p>
<p><strong>A <code>G54</code> with axis words is not a table read.</strong> On the Heidenhain syntax list the datum-shift parser
claims a <code>G54</code> that is followed by at least one axis value and routes it into the direct-shift flow,
which reads no row; a bare <code>G54</code> is left alone and reaches the ISO path, where it resolves preset row</p>
<ol>
<li>So <code>G54</code> means a table lookup or a literal shift depending on what follows it in the block.</li>
</ol>
<p>A row number that resolves to nothing yields a zero translation rather than an error, on both paths.
And nothing is active at the first block: the Heidenhain preset's initializer seeds no coordinate
section at all, unlike the Fanuc family's <code>G54</code> and the Siemens cancel frame, so a program that
issues no datum cycle and no <code>G54</code> machines with no offset applied.</p>
<h2 id="where-non-zero-rows-come-from">Where Non-Zero Rows Come From</h2>
<p>Four surfaces put a value into these tables, and only the first is on this page.</p>
<ul>
<li><strong>The two leaves themselves</strong>, per cell or per row.</li>
<li><strong>Work Coordinates</strong>, for preset rows 1 through 6 only, through the alias above.</li>
<li><strong>A brand switch onto Heidenhain with its carry option on</strong>, through that same ISO face and into
the table the incoming preset resolves: at most six offsets, into preset rows 1 through 6 —
<a class="xref" href="../machine/brand-switch.html">Brand Switch</a>.</li>
<li><strong>A project-load migration.</strong> Opening a project file that carries the superseded controller
environment element and no per-case list populates the materialized per-case tables from that
element, and the datum half runs only when the legacy brand element reads Heidenhain. It copies
preset entries and shift entries by row number, overwriting a row it names and leaving every other
row as seeded. The legacy work-coordinate table is replayed first, through the same ISO face that
aliases onto preset rows 1 through 6, so where the two overlap the explicit datum copy is what
stands.</li>
</ul>
<p>The superseded <code>/controller</code> route also carries datum preset and datum shift surfaces, over the
separate environment model rather than over the runner — an edit made there does not reach the table
on this page. That route's own anatomy is <a class="xref" href="../../../legacy-controller.html">Legacy Controller Page</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>Program Data</strong> Group Row — <code>equipment/controller/program-data</code>
<ul>
<li><strong>Datum Presets (Q339)</strong> Node Row — <code>equipment/controller/program-data/datum-presets</code></li>
<li><strong>Datum Shifts (D)</strong> Node Row — <code>equipment/controller/program-data/datum-shifts</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected, one at a time
<ul>
<li><strong>Datum Presets (Q339)</strong> / <strong>Datum Shifts (D)</strong> Panel — one component, its role taken from the
selected id's last segment
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em>; the shared empty state, shown
instead of everything below while the snapshot reports no runner</li>
<li>Absent-Table Body — <em>&quot;No Heidenhain datum table on the active runner.&quot;</em>; shown instead of
everything below while the panel's own read reports no table</li>
<li>Description Caption — grey, one line: <em>&quot;Datum presets (CYCL DEF 247, Q339 = row). Rows 1–6
double as G54–G59.&quot;</em> or <em>&quot;Datum shifts (CYCL DEF 7).&quot;</em></li>
<li>Datum Table — dense, flat, bordered; no sort, no pagination, no column menu
<ul>
<li>Header Row — <code>Q339</code> or <code>D</code>, then <code>X (mm)</code>, <code>Y (mm)</code> and <code>Z (mm)</code>, then an unlabelled action
column</li>
<li>Datum Row, one per row the read returned, in row-number order
<ul>
<li>Row Number Label — bold plain text, never editable</li>
<li>Value Numeric Field, one per axis column — no minimum, no maximum, no unit suffix</li>
<li><strong>0</strong> Button — flat, dense, centred, tooltip <em>&ldquo;Reset to zero&rdquo;</em></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Toast — negative, three seconds, the panel's context followed by the server's own message</li>
</ul>
</li>
</ul>
<p>The panel carries no heading, no toolbar, no add row, no delete button, no toggle, no save button and
no dialog.</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/SoftNcDatumTablePanel.vue</code> — the one component behind both
leaves: the role taken from the node id's suffix with its preset default, the four things that
role decides, the literal column headers, the whole-row cell commit with its non-finite guard, and
the non-optimistic row reset.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: the two node ids
and label keys pushed together inside one flag test, and the single item type both carry.</li>
<li><code>wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue</code> — the editor row: the remount key
that makes a move between the two nodes a re-fetch, and the events this panel declares none of.</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/widgets/NumericInput.vue</code> — the value cell: commit on blur or Enter,
the empty-text-to-null parse, and the infinity and NaN literals the panel's handler then rejects.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the read that returns both tables and its row shape, the
two role values, the per-row setter and the reset call, and the snapshot's datum flag.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the first empty layer reads.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the envelope helper that turns a success-flagged failure body into
a thrown error.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcWorkCoordinatesPanel.vue</code> — the second face on the
preset rows: the same six cells addressed by coordinate id, and the caption that names the mapping.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the two node labels, the two descriptions, the absent-table
body and the three error contexts.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the reset button's tooltip.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the datum flag's probe in the snapshot
builder, the read that returns both dictionaries ordered by row number, the per-row writer and the
zeroing route behind one shared table-segment guard, the work-coordinate routes that reach the same
object through the ISO interface, and the brand switch's capture-swap-carry-sweep.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs</code> — the two dictionaries, their
twenty seeded rows, the accessors both cycles read through, and the ISO face: the <code>G54</code>–<code>G59</code> map
onto preset rows 1–6, the synthetic preset ids it also resolves, and the id enumeration that stops
at six.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTableProxy.cs</code> — the seedless get-or-create
placeholder the preset carries, and the bare table it installs into a project holding none.</li>
<li><code>HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs</code> — the offset-provider contract whose first
implementer the work-coordinate face resolves.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs</code> — the other Heidenhain
per-case table: it derives from the controller parameter base, which declares no coordinate
contract, which is why the datum table is the preset's only implementer of one.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the Heidenhain preset's dependency list and the position of the
datum proxy in it, the proxy resolution every read goes through, and the legacy per-case populate
that fills both dictionaries by row number after replaying the legacy coordinate table.</li>
<li><code>HiMech/NcParsers/ParsingSyntaxs/Heidenhain/CyclDefSyntaxs/HeidenhainDatumSettingSyntax.cs</code> — the
<code>CYCL DEF 247</code> parser and the DIN/ISO <code>G247</code> spelling it claims for the same cycle record.</li>
<li><code>HiMech/NcParsers/ParsingSyntaxs/Heidenhain/CyclDefSyntaxs/HeidenhainDatumShiftSyntax.cs</code> — the
<code>CYCL DEF 7</code> parser: the <code>#</code> row index, the direct-value form, and the axis-word test that decides
whether a <code>G54</code> becomes a direct shift or stays on the ISO path.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainCoordinateOffsetSyntax.cs</code> — the resolver: the
preset lookup and its synthetic id, the additive shift in its own transform entry, the modal carry
that re-resolves a numbered shift from the table, the shift reset a successful preset selection
performs, and the two validation warnings.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/IsoCoordinateOffsetSyntax.cs</code> — the <code>G54</code>-series path and the modal
lookback that re-queries the table for the carried id.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/CoordinateOffsetUtil.cs</code> — the offset resolution and the translation
composed onto the block's transform chain.</li>
<li><code>HiMech/NcParsers/Syntaxs/HeidenhainSyntaxUtil.cs</code> — the Heidenhain syntax list carrying both the
ISO coordinate syntax and the datum-cycle syntax.</li>
<li><code>HiMech/NcParsers/Initializers/StaticInitializer.cs</code> — the Heidenhain initializer that seeds no
coordinate section, against the Fanuc and Siemens ones that do.</li>
<li><code>HiGeom/Geom/Vec3d.cs</code> — the three-component translation a datum row stores, and the zero it
defaults to.</li>
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — the load path that wires the proxies and then runs the
legacy per-case populate on a project carrying no per-case list.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Program Data Plane</a> — the plane these two leaves sit on, where their table is
stored, and the per-case tables beside them a brand switch keeps or sweeps</li>
<li><a class="xref" href="work-coordinates.html">Work Coordinates</a> — the second face on the preset rows: the same six cells
addressed as <code>G54</code>–<code>G59</code>, on the one brand where two nodes edit one object</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,508 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Frames | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Frames | 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-ControllerFrames">
<h1 id="frames">Frames</h1>
<p>Frames is the Controller branch's Sinumerik settable-frame ledger: one row per <code>$P_UIFR</code> entry,
each carrying the X / Y / Z translation HiNC stores for that frame. It lives on the General Setup
page at <code>/general-setup</code> under the Control-Tree id <code>equipment/controller/program-data/frames</code>,
reached as <code>?tree=equipment/controller/program-data/frames</code>, and it is grown only while the active
runner resolves a Siemens frame table — among the shipped brand presets, Siemens alone. That same
table is the offset provider behind the ungated <strong>Work Coordinates (G54…)</strong> leaf on this brand, so
the two nodes edit one object.</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="what-a-frame-is-here">What a Frame Is Here</h2>
<p>On a Sinumerik, <code>$P_UIFR</code> is an array of frames and each entry carries a translation, a rotation, a
scale and a mirror. HiNC consumes the translation and nothing else, so
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a> stores one <a class="xref" href="../../../../api/Hi.Geom.Vec3d.html">Vec3d</a> per frame
id in <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensFrameTable_Frames">Frames</a>, with a second,
axis-letter-keyed dictionary beside it for translation components on axes other than X, Y and Z.</p>
<p>The table is a dependency in its own right rather than a section of the machine data, because
<code>$P_UIFR</code> is not machine data on the control either:
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html">SiemensMachineDataTable</a> holds the <code>MD</code>-numbered parameters
— axis type, reference position, stroke limits — and carries no frame. That split is also why the
frame table is the brand's ISO offset provider while the machine-data table is not.</p>
<p>The array index is not the row key. Rows are keyed by the G-code that selects the frame, and the
bridge that serves <code>$P_UIFR</code> accesses maps one onto the other:</p>
<table>
<thead>
<tr>
<th><code>$P_UIFR</code> index</th>
<th>Frame id</th>
<th>Row on this leaf</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code></td>
<td><code>G500</code></td>
<td>none — the cancel frame is never stored</td>
</tr>
<tr>
<td><code>1</code>–<code>4</code></td>
<td><code>G54</code>–<code>G57</code></td>
<td>the four base rows</td>
</tr>
<tr>
<td><code>5</code>–<code>99</code></td>
<td><code>G505</code>–<code>G599</code></td>
<td>the extended tail</td>
</tr>
</tbody>
</table>
<p>An index outside <code>0</code>–<code>99</code> maps to no id at all, and the bridge drops the access rather than
inventing a frame for it.</p>
<h3 id="settable-and-computed">Settable, and computed</h3>
<p>Four parts of the Sinumerik frame chain show up in this branch's behaviour, and only one of them is
a row here.</p>
<ul>
<li><strong>The settable frames</strong> are the table. Ninety-nine ids are allocated by the constructor —
<code>G54</code>–<code>G57</code> plus every id in
<a class="xref" href="../../../../api/Hi.NcParsers.Keywords.Siemens.SiemensKeywords.html#Hi_NcParsers_Keywords_Siemens_SiemensKeywords_ExtendedCoordinateSeries">ExtendedCoordinateSeries</a>, which is
<code>G505</code> through <code>G599</code> — each seeded to zero, matching a control where every <code>$P_UIFR[n]</code> is
allocated with an initial value of zero. Every one is editable on this leaf.</li>
<li><strong><code>G500</code></strong> is computed, not stored. The accessor answers a zero offset for it and drops a write,
and the constructor deliberately keeps it out of the dictionary, so it is a row on neither this
leaf nor Work Coordinates. The description above the table says as much: <em>&quot;Settable frames
($P_UIFR, translation only); G500 cancels and stores nothing.&quot;</em></li>
<li><strong>The programmable frame</strong> — <code>TRANS</code> / <code>ATRANS</code> / <code>ROT</code> / <code>AROT</code> and their solid-angle forms,
together with the <code>CYCLE800</code> tilt that shares its slot — is computed per block from the program
and has no table, no node and no stored value anywhere in this branch.</li>
<li><strong>A frame's rotation and fine-offset components</strong> reach neither this table nor the simulation. Only
the <code>TR</code> component of a <code>$P_UIFR</code> access is bridged into the frame entry; a write to any other
component is recorded on the block and reported as recognised-but-not-simulated by the Siemens
system-variable catch-all.</li>
</ul>
<h2 id="how-the-chain-composes">How the Chain Composes</h2>
<p>A frame word in a program selects one id. The Siemens coordinate syntax resolves that id's offset
through <a class="xref" href="../../../../api/Hi.NcParsers.SoftNcRunner.html#Hi_NcParsers_SoftNcRunner_GetEffectiveNcDependencyList_">GetEffectiveNcDependencyList</a><small>(API)</small>, taking
the first provider that answers, and writes the result twice: as the block's coordinate-offset
section, and as one named entry in the block's program-to-machine transform chain. Entries in that
chain are multiplied in the order they were written, so slot order is the whole of the composition
rule.</p>
<p>Two placements in the Siemens syntax list decide what a settable frame ends up doing.</p>
<ul>
<li><strong>The programmable-frame entry is written first, deliberately.</strong> The tilt and programmable-frame
syntaxes run ahead of the coordinate-offset syntaxes so their shared entry lands ahead of the
settable frame's. That is what keeps a <code>ROT</code> turning the program coordinates <em>inside</em> the frame
rather than turning the frame's own offset with them. Tool-height compensation is written between
the two, and the kinematic pivot transform is written last so every frame-space operation is
accumulated before the final inverse kinematics.</li>
<li><strong>Both coordinate syntaxes are registered, and they share one entry.</strong> The Siemens preset carries
the ISO coordinate syntax and the Siemens one side by side; each writes the chain entry under the
same name, and a repeated name is replaced in place rather than appended. So the <code>G54</code>–<code>G59</code>
vocabulary the ISO syntax recognises and the <code>G500</code> and <code>G505</code>–<code>G599</code> vocabulary the Siemens one
adds resolve into a single slot. The Siemens syntax leaves the coordinate flag in place instead of
consuming it, and falls back to the block's existing coordinate section and then to the previous
block's, which is what carries a frame selection modally.</li>
</ul>
<p>Three codes bypass the composed transform for one block rather than changing it: <code>G53</code>, <code>G153</code> and
<code>SUPA</code> all reduce to the same one-shot machine-coordinate move, and the values on this leaf take no
part in such a block.</p>
<p>The word vocabulary is also wider than the table. The ISO syntax's own series adds <code>G58</code>, <code>G59</code> and
<code>G59.1</code>–<code>G59.9</code>, none of which the frame table allocates; such a word resolves to no offset from any
provider and composes a zero translation.</p>
<h2 id="the-rows-and-the-columns">The Rows and the Columns</h2>
<p>The panel is a four-column table and nothing else — no toolbar, no add footer, no action column, no
dialog. The <strong>Frame</strong> header is translated; <code>X (mm)</code>, <code>Y (mm)</code> and <code>Z (mm)</code> are literals in the
template and stay English under any locale. The id cell is bold plain text and cannot be edited.</p>
<p><strong>The row set is fixed.</strong> The branch exposes exactly two frame routes, a whole-table read and a
per-id write; no third route adds, deletes or resets a row. The write itself is an unguarded
assignment by key rather than an update of an existing row, so an id the table has never held would
be seated as a new one — but no panel has a way to send such an id. A row exists because the table
allocated it, and the ninety-nine allocated ids are the same ninety-nine whatever a project has done
to their values.</p>
<p><strong>Nothing in the path sorts.</strong> The reader projects the dictionary as it enumerates it and applies no
ordering, unlike the datum, retained-variable, R-parameter and tool-offset readers beside it, and the
panel adds none of its own. What that yields depends on how the table was built. A table the proxy
has just created lists <code>G54</code>–<code>G57</code> first and then the extended tail, in construction order. A table
read back from a project file lists the ids in the order the file names them — and the file is
written sorted by id as text, not as a number. Plain string order puts <code>G505</code> ahead of <code>G54</code>, because
the third character decides that pair, and <code>G54</code> ahead of <code>G540</code>, because the shorter of two strings
that share a prefix comes first. So a saved and reopened table does not push the four base frames to
either end: it interleaves them into the extended tail, as <code>G505</code>–<code>G539</code>, <code>G54</code>, <code>G540</code>–<code>G549</code>,
<code>G55</code>, <code>G550</code>–<code>G559</code>, <code>G56</code>, <code>G560</code>–<code>G569</code>, <code>G57</code>, then <code>G570</code>–<code>G599</code>. Work Coordinates enumerates
the same dictionary, so both leaves reorder together.</p>
<h3 id="show-all">Show all</h3>
<p>A <strong>Show all</strong> toggle sits at the right of the header strip. With it off, a row is listed only when
its id matches <code>G54</code> through <code>G57</code>, or when at least one of its three values is non-zero; with it on,
every allocated id is listed. The test is the panel's own regular expression, evaluated in the
browser over the rows the read returned — nothing about it reaches the server, and the toggle is a
plain local flag that returns to off whenever the panel is remounted.</p>
<p>Two details of that rule are worth reading against the sibling leaf. The toggle here is
unconditional, so it renders even on a table with no extended rows at all. And the always-visible
test here is narrower than the work-coordinate one, which admits <code>G54</code> through <code>G59</code> and any of them
followed by a dot and one further digit. On the seeded table the two tests keep the same four rows
visible, because the frame table allocates none of the ids where they differ.</p>
<h2 id="two-nodes-one-table">Two Nodes, One Table</h2>
<div class="WARNING">
<h5>Warning</h5>
<p>On Siemens this leaf and <strong>Work Coordinates (G54…)</strong> are two faces of one instance, not two tables
kept in step. The work-coordinate leaf resolves the first
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> on the active runner; on this brand that is
the frame table, because it is the only dependency the Siemens preset carries that implements that
interface — the machine-data table included. A <code>G54</code> edit made on either node is the same
assignment into the same cell.</p>
</div>
<p>This is the reciprocal half of the warning <a class="xref" href="work-coordinates.html">Work Coordinates</a>
carries. What the two leaves share, exactly:</p>
<ul>
<li><strong>The row set.</strong> The work-coordinate reader enumerates the provider's own ids, which for this
provider is the frame dictionary's key set. The two leaves therefore list the same ids in the same
order, and gain or lose none independently.</li>
<li><strong>The X, Y and Z cells.</strong> Both leaves write the whole triad for one id, and both land in the same
dictionary entry. Neither mutates the vector already sitting there: each write installs a fresh
<a class="xref" href="../../../../api/Hi.Geom.Vec3d.html">Vec3d</a> over it, so a reference taken before the write still reads the old values. A
value typed on one leaf is what the other shows at its next mount.</li>
<li><strong>The absence of <code>G500</code>.</strong> Neither leaf can show it, for the same reason: it is not in the
dictionary the ids are enumerated from.</li>
</ul>
<p>What is not shared, and what a reader must not infer from the shared face:</p>
<ul>
<li><strong>The per-axis translation components.</strong> A settable frame's translation on an axis letter other
than X, Y or Z lives in the table's second dictionary, written and read only by the <code>$P_UIFR</code>
bridge. Neither leaf returns it, and neither leaf disturbs it: both write a fresh
<a class="xref" href="../../../../api/Hi.Geom.Vec3d.html">Vec3d</a> into the frame entry and leave the axis dictionary untouched, so a C-axis
frame component set by a program survives every edit made from either panel.</li>
<li><strong>The row actions.</strong> The <strong>P0</strong> and <strong>M0</strong> buttons and the canvas marker belong to Work
Coordinates. This leaf has no action column at all — but P0 and M0 write through the same provider,
so pressing one there changes what this leaf shows.</li>
<li><strong>The presence gate.</strong> This leaf gates on the concrete frame table; the other gates on the ISO
interface. On Siemens the two answers are one object and the leaves appear together; the empty
lines they would show differ all the same — <em>&quot;No Siemens frame table on the active runner.&quot;</em> here,
<em>&quot;No work-coordinate table on the active runner.&quot;</em> there.</li>
<li><strong>The write path.</strong> The work-coordinate route writes through the provider's coordinate accessor,
which drops a <code>G500</code> write; the frame route assigns into the dictionary by key and would store one.
Neither is reachable from a panel, because both panels only ever send an id their own read
returned.</li>
<li><strong>The caption.</strong> Work Coordinates picks its caption from the snapshot's coordinate-kind field and
on this brand shows <em>&quot;Stored as Siemens settable frames ($P_UIFR; G500 cancels and is always
zero).&quot;</em> — which is that leaf naming this one. This leaf's caption is fixed.</li>
</ul>
<p>Nothing reconciles the two views while both are in scope, and nothing needs to: the editor row mounts
one panel at a time and each fetches once on mount, so moving the selection between the two nodes is
itself the refresh. What a panel that stays mounted therefore never sees is described in
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="editing-a-frame">Editing a Frame</h2>
<p>Every value cell is the shared numeric field, so it commits on blur or on Enter and never per
keystroke; its parsing, its bounds behaviour and the second commit that follows Enter are
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>. No cell here passes a minimum or a maximum, and no cell
carries a unit suffix — the millimetre is stated in the column header instead.</p>
<p><strong>The write is the whole row.</strong> A commit assigns the new number into the local row and sends that
row's <code>x</code>, <code>y</code> and <code>z</code> as they now stand under the row's id. The optimistic shape, the single-cell
rollback and the toast a failure raises are the branch's own and are described in the
<a class="xref" href="../editing-contract.html">Editing Contract</a>; what is this panel's is the guard in
front of them. The numeric field can emit an empty value as null and the literals <code>Infinity</code>,
<code>-Infinity</code> and <code>NaN</code> as non-finite numbers, and the handler returns before the request on all four.
So a cleared cell stores nothing and clears nothing: the box shows blank while the stored number
stands, and the number returns when the panel is remounted by selecting another node and coming back.</p>
<p>No surface here renames a frame: the id is the write's route key and no route accepts a replacement
for it. The panel reports no structural change either, so no edit on this leaf rebuilds the branch.</p>
<h2 id="what-survives-and-what-a-brand-change-destroys">What Survives, and What a Brand Change Destroys</h2>
<p>The table is per-case: <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTableProxy.html">SiemensFrameTableProxy</a> stands in the
runner's pipeline list and the real table lives on the project's
<a class="xref" href="../../../../api/Hi.NcParsers.NcRunnerSuit.html#Hi_NcParsers_NcRunnerSuit_PerCaseNcDependencyList">PerCaseNcDependencyList</a>, serialized inside the project file. That
proxy carries <strong>no seed</strong> — it installs a freshly constructed table when the project holds none and
takes the project's own table thereafter — so the runner file records only the placeholder, and a
<code>.Controller</code> written from this project carries no frame values at all. The ownership rules the
proxy pattern follows across the branch are <a class="xref" href="index.html">Program Data</a>.</p>
<p>Two consequences follow for the values on this leaf.</p>
<ul>
<li><strong>A same-brand runner install keeps them.</strong> The incoming preset's proxy re-binds to the table the
project already holds rather than replacing it, and the sweep that follows keeps every per-case
table the new runner resolves through a proxy.</li>
<li><strong>A switch to any other brand destroys them.</strong> No other preset proxies a frame table, so the sweep
removes it, and switching back installs a fresh all-zero table rather than the one that was there.
The brand switch's optional carry salvages a corner of it: the offsets are read from the outgoing
provider before the swap and written into the incoming one afterwards, but only for ids the
incoming provider already enumerates. No other shipped provider allocates a <code>G5xx</code> id — the
Fanuc-family and Syntec tables enumerate <code>G54</code>–<code>G59</code> and <code>G54.1P1</code>–<code>G54.1P48</code>, the Heidenhain datum
table <code>G54</code>–<code>G59</code> — so the carry keeps <code>G54</code> through <code>G57</code> and the whole extended tail is lost.
What a switch keeps, resets and destroys elsewhere is <a class="xref" href="../machine/brand-switch.html">Brand Switch</a>;
which leaf exists on which brand is <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</li>
</ul>
<h2 id="what-a-run-writes-back">What a Run Writes Back</h2>
<p>A played program does not only read this table; it writes into it, through a bridge with two
halves.</p>
<ul>
<li><strong><code>$P_UIFR[n,axis,TR]=…</code> writes into it.</strong> A literal numeric assignment is routed into the table:
an X, Y or Z component replaces that component of the frame entry, and any other axis letter lands
in the per-axis dictionary. A non-literal right-hand side is left for the expression evaluator
earlier in the same block, and a write to index <code>0</code> is consumed and ignored, mirroring the cancel
frame's own rule. With no frame table on the runner the bridge is a no-op and the assignment stays
visible as unconsumed residue.</li>
<li><strong><code>$P_UIFR</code> reads come back out of it.</strong> The same mapping serves the read side, answering zero for
an allocated frame's unset axis component and for every component of <code>G500</code>, and null — a
fall-through to the next lookup in the chain — for an id the table does not hold.</li>
</ul>
<p>The table is deliberately <strong>not</strong> session-resettable. Settable frames are setting data on a real
control: a <code>$P_UIFR</code> write survives reset and power-off, so a replayed session must see what the
previous run left. The runner clears every session-resettable dependency on the fresh-session edge
and this table is not among them, which means a value a run wrote is what this panel shows
afterwards, and what the project file keeps.</p>
<p>Which frame a program starts on is not stored in the table either. The Siemens preset's static
initializer seeds the first block's coordinate id as <code>G500</code>, so an untagged Siemens program begins
with no frame active and a zero offset until a frame word appears — unlike the Fanuc-family and
Syntec presets, which begin on <code>G54</code>.</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>Program Data</strong> Group Row — <code>equipment/controller/program-data</code>
<ul>
<li><strong>Frames (Siemens)</strong> Node Row — <code>equipment/controller/program-data/frames</code>; grown while the
snapshot reports a Siemens frame table</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Frames (Siemens)</strong> Panel
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em>; the shared empty state, shown
instead of everything below while the snapshot reports no runner</li>
<li>Absent-Table Body — <em>&quot;No Siemens frame table on the active runner.&quot;</em>; shown instead of
everything below while the panel's own read reports no table</li>
<li>Header Strip
<ul>
<li>Description Caption — <em>&quot;Settable frames ($P_UIFR, translation only); G500 cancels and stores
nothing.&quot;</em>, left-aligned and grey</li>
<li><strong>Show all</strong> Toggle — right-aligned, dense; always rendered</li>
</ul>
</li>
<li>Frame Table — dense, flat, bordered; no sort, no pagination, no column menu
<ul>
<li>Header Row — <strong>Frame</strong>, <strong>X (mm)</strong>, <strong>Y (mm)</strong>, <strong>Z (mm)</strong>. Only <strong>Frame</strong> is translated</li>
<li>Frame Row, one per visible id — not clickable, and carrying no tooltip
<ul>
<li>Frame Id Label — bold plain text, never editable</li>
<li>Value Numeric Field, one per axis column — no minimum, no maximum, no unit suffix</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Toast — negative, three seconds, the panel's context followed by the server's own message</li>
</ul>
</li>
</ul>
<p>The panel carries no heading, no add or delete control, no reset button, no save button and no
unsaved marker.</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/SoftNcFramesPanel.vue</code> — the panel: the two empty layers,
the fixed description and the unconditional <strong>Show all</strong> toggle, the four-column table with its two
literal header spellings, the always-visible regular expression, and the whole-triad cell commit
with its null and non-finite guard.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcWorkCoordinatesPanel.vue</code> — the other node on this
table: the same ids and values through the ISO reader, plus the wider always-visible test, the
conditional toggle, the P0 and M0 actions and the canvas marker this leaf has none of.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: this leaf's node
id and label key, the snapshot flag it is pushed behind, and its position among the program-data
children.</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/widgets/NumericInput.vue</code> — the value cell: commit on blur or Enter,
the empty-text-to-null parse, and the infinity and NaN literals the panel's handler then rejects.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the frames reader and its whole-triad setter, the shared
offset-row shape and parser both leaves reuse, and the presence flag this leaf is gated on.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the envelope helper that turns a <code>success: false</code> body into a
thrown error indistinguishable from a transport failure.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the first empty layer reads.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the node label, the description, the <strong>Frame</strong> column header,
the absent-table line and the two error contexts.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Show all</strong> label.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the presence probe behind the node's gate, the
unsorted frames reader with its zero fill, the per-id writer that assigns into the dictionary
directly rather than through the coordinate accessor, the work-coordinate routes that share the
same object on this brand, and the brand switch's capture-swap-carry-sweep with its accepted-id
filter.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTable.cs</code> — the table: the ninety-nine ids its
constructor seeds, the cancel frame it keeps out of them, the coordinate accessors the
work-coordinate leaf uses, the per-axis accessors the <code>$P_UIFR</code> bridge uses, the axis dictionary
neither panel exposes, the deliberate absence of session-reset, and the id-sorted serialization
that decides the row order after a reload.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTableProxy.cs</code> — the seedless get-or-create
placeholder: what the runner file records, and the fresh table it installs into a project holding
none.</li>
<li><code>HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs</code> — the offset-provider contract this table
implements, which is what makes it the work-coordinate leaf's provider on this brand.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs</code> — the <code>MD</code> table beside it,
carrying no frames and implementing no offset-provider contract.</li>
<li><code>HiMech/NcParsers/Keywords/Siemens/SiemensKeywords.cs</code> — the cancel frame and the extended series
the constructor seeds from.</li>
<li><code>HiMech/NcParsers/Keywords/Generic/IsoKeywords.cs</code> — the <code>G54</code> series the ISO syntax recognises,
wider than this table allocates.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the Siemens preset that carries the frame-table proxy as its
first per-case entry, behind only the brand marker; the proxy resolution every read goes through;
and the session-reset sweep this table is deliberately outside of.</li>
<li><code>HiMech/NcParsers/ISessionResettable.cs</code> — the marker the table does not carry, and the difference
between session state and setting data it draws.</li>
<li><code>HiMech/NcParsers/NcRunnerSuit.cs</code> — the per-case list the table hangs off and the proxy wiring
that installs it.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/CoordinateOffsetUtil.cs</code> — the resolver that walks every provider
and takes the first non-null answer, the coordinate section it writes, and the translation it
composes.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Siemens/SiemensCoordinateOffsetSyntax.cs</code> — the frame-word path: the
vocabulary it detects, the flag it leaves in place, the modal lookback, and the zero fallback when
nothing answers.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/IsoCoordinateOffsetSyntax.cs</code> — the sibling registered beside it on
this preset, writing the same chain entry from the <code>G54</code>–<code>G59.9</code> vocabulary.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Siemens/SiemensProgrammableFrameSyntax.cs</code> — the computed
programmable frame, and the entry it shares with the tilt cycle ahead of the settable frame's.</li>
<li><code>HiMech/NcParsers/Syntaxs/SiemensSyntaxUtil.cs</code> — the slot order that decides the composition:
tilt and programmable frame, tool height, the two coordinate syntaxes, then the pivot transform.</li>
<li><code>HiMech/NcParsers/Syntaxs/TransformationUtil.cs</code> — the transform chain: in-order multiplication,
replacement of a repeated entry in place, and the pivot entry that must stay last.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/MachineCoordSelectSyntax.cs</code> — the one-shot codes that bypass the
composed transform, widened on this preset to <code>G153</code> and <code>SUPA</code>.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensUifrWritingSyntax.cs</code> — the write half of the
<code>$P_UIFR</code> bridge: the literal-only rule, the ignored cancel-frame index, and the no-op when no
frame table resolves.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensUifrVariableLookup.cs</code> — the read half.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensVariableKey.cs</code> — the index-to-id map behind
both halves, and the <code>TR</code>-only key pattern.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensSystemVariableSyntax.cs</code> — where a non-<code>TR</code>
frame component goes instead: recorded on the block, reported unsupported, never reaching the
frame table.</li>
<li><code>HiMech/NcParsers/Initializers/StaticInitializer.cs</code> — the Siemens preset's first-block coordinate
id, and the two brands that differ from it.</li>
<li><code>HiMech/NcParsers/Dependencys/IsoCoordinateAddressMap.cs</code> — the Fanuc-family id set the brand
switch's carry intersects this table against.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs</code> — the third id set the same carry
can meet.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Program Data Plane</a> — the plane this leaf sits on, and the ownership rules that
decide what a runner install keeps and what a brand switch sweeps</li>
<li><a class="xref" href="work-coordinates.html">Work Coordinates</a> — the other node on this table, where the same ids are
edited as G54 offsets, with the row actions and the canvas marker this leaf has none of</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,537 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Program Data Plane | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Program Data 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-ControllerProgramData">
<h1 id="program-data-plane">Program Data Plane</h1>
<p>Program Data is the second of the Controller branch's two plane stems: the coordinate, offset and
variable tables that belong to the workpiece rather than to the control, 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/program-data</code>, and each of its leaves takes that id plus one segment —
<code>work-coordinates</code>, <code>tool-offsets</code>, <code>siemens-tool-offsets</code>, <code>tool-names</code>, <code>datum-presets</code>,
<code>datum-shifts</code>, <code>frames</code>, <code>retained-variables</code>, <code>r-parameters</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 two 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;Data that travels with the workpiece / project — work-coordinate
offsets (G54…), tool offsets and the brand-specific per-case tables. Kept on the project even when
the runner file is swapped.&quot;</em></p>
<p>Every leaf below follows the branch's shared editing rules — one read on mount, per-control commit,
capture-assign-await-restore on failure, a dense markup table with no sort and no pagination, and the
two empty layers. Those rules, and the places this plane departs from them, are
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="where-the-data-actually-lives">Where the Data Actually Lives</h2>
<p>The stem's introduction names a storage location, and here — unlike the machine stem's own
introduction — it holds for every leaf. What it does not say is that the location is not a file.</p>
<h3 id="one-list-inlined-in-the-project">One list, inlined in the project</h3>
<p><a class="xref" href="../../../../api/Hi.NcParsers.NcRunnerSuit.html">NcRunnerSuit</a> carries the runner beside 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 can be written to a side file; the per-case half never is — the suit writes it as a
nested element with no file reference of its own, so it lands inside whatever file the owning project
writes. Every table this plane edits is an entry of that one list.</p>
<p>No panel and no endpoint reads or writes a table by reaching into that list by name. The one place
that addresses the list directly is the sweep that follows a runner swap, and it removes whole
entries rather than editing any value in one. A brand preset's pipeline list carries an
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.INcDependencyProxy.html">INcDependencyProxy</a> in place of each of these tables, and every read
and write resolves 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. That is also what the snapshot flags probe, so the tree
grows a leaf for the concrete table and never for the proxy —
<a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<h3 id="the-proxies-on-this-plane-carry-nothing">The proxies on this plane carry nothing</h3>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTableProxy.html">ToolOffsetTableProxy</a> and its siblings — the Siemens <code>$TC_DP</code>,
frame and R-parameter proxies, the Heidenhain datum proxy and the Fanuc-family retained common
variable proxy — are get-or-create placeholders with no seed. Each serializes an empty element, and
on wiring installs a bare new instance into the per-case list when the project holds none of that
type; an existing table is left untouched.</p>
<p>Two consequences follow. A controller file written from the branch root's Object-Management menu
carries none of the values edited on this plane: the seedless proxies write an empty element, and the
one proxy on this plane that does carry a seed — the brand parameter-table proxy standing behind the
work coordinates on the Fanuc family and Syntec — serializes the fixed seed it was constructed with
rather than the table the panels have been editing. And a table this plane loses is not recoverable
from a preset: a fresh instance is what the proxy makes.</p>
<h3 id="which-store-a-work-coordinate-lands-in">Which store a work coordinate lands in</h3>
<p>Work Coordinates is the one leaf whose backing object changes with the brand. The panel reads and
writes the first <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> in the effective list, and the
caption above its table names which one answered.</p>
<table>
<thead>
<tr>
<th>Brand</th>
<th>Backing object</th>
<th>Ids the table lists</th>
<th>Caption above the table</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc, Mazak</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a></td>
<td>G54–G59 and G54.1P1–G54.1P48</td>
<td><em>&quot;Stored in the Fanuc parameter table (#5221+ / #7001+).&quot;</em></td>
</tr>
<tr>
<td>Syntec</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a></td>
<td>G54–G59 and G54.1P1–G54.1P48</td>
<td><em>&quot;Stored in the Syntec parameter table (Pr5221+ / Pr7001+).&quot;</em></td>
</tr>
<tr>
<td>Siemens</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a></td>
<td>G54–G57 and G505–G599</td>
<td><em>&quot;Stored as Siemens settable frames ($P_UIFR; G500 cancels and is always zero).&quot;</em></td>
</tr>
<tr>
<td>Heidenhain</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a></td>
<td>G54–G59</td>
<td><em>&quot;G54–G59 map onto Heidenhain datum preset rows 1–6.&quot;</em></td>
</tr>
</tbody>
</table>
<p>An id appears only where the store actually holds it. A Fanuc-family or Syntec table yields an id
whose parameter address is present, and every one of those default tables seeds all of them at zero;
the Siemens frame table seeds G54–G57 and the extended series, and has no G58 or G59 row at all; the
Heidenhain table yields one id per preset row 1–6 that exists. G500 is never a row — the frame table
refuses to store it and does not seed it.</p>
<p>That caption is the panel's only statement of where a value went, and it is selected from the
snapshot's work-coordinate kind rather than from the brand marker.</p>
<h3 id="the-one-setting-that-is-not-in-the-list">The one setting that is not in the list</h3>
<p>The Tool Offsets leaf's <em>Set ideal offset dependent on tool house</em> toggle is not a dependency. It
writes <a class="xref" href="../../../../api/Hi.MachiningProcs.MachiningActRunnerConfig.html#Hi_MachiningProcs_MachiningActRunnerConfig_IsIdealOffsetDependentOnToolHouse">IsIdealOffsetDependentOnToolHouse</a>, an
element the project serializes beside the runner suit. It therefore stays with the project like
everything else on this plane, but by a different route — and, being outside the per-case list, it is
untouched by the sweep that follows a runner swap.</p>
<h2 id="how-this-differs-from-the-machine-plane">How This Differs From the Machine Plane</h2>
<p>The machine plane's ownership is split. Several of its leaves are plain instances the runner owns
outright — the tool-change position, the block-skip layers, the subprogram folders, and on Siemens
and Heidenhain the peck clearance — and an install replaces them with the incoming runner's; the
rest are rows of the brand parameter table and are stored on the project. One leaf holds both
halves: Controller Parameters, where the macro loop guards are runner-owned fields sitting beside
table rows. Which side of that line a machine-plane field falls on is
<a class="xref" href="../machine/index.html">Machine and Controller Plane</a>.</p>
<p>This plane has no such split. Nothing on it is a plain runner-owned instance: every leaf resolves a
per-case table through a proxy, so every value it edits is stored on the project. That is what makes
the two introductions' phrasing load-bearing rather than decorative — and it is why installing a
controller file of the brand already in force changes nothing here at all: each of that brand's
proxies takes the table the project already holds rather than installing a fresh one over it. The
same install does return the machine plane's runner-owned settings to the incoming file's values.</p>
<p>The one crossing runs the other way. On Fanuc, Mazak and Syntec the work coordinates are rows of the
same brand parameter table whose other rows the machine plane's per-axis leaves and native parameter
form edit. A single object straddles both stems there, which is exactly why that table is per-case at
all: the two planes could not otherwise be separated.</p>
<h2 id="the-planes-leaves">The Plane's Leaves</h2>
<p>Ordered as the branch builder pushes them: the two ungated leaves first, then each gated leaf or
gated pair 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>Backing table</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>work-coordinates</code></td>
<td>Work Coordinates (G54…)</td>
<td>ungated</td>
<td>the brand's ISO coordinate provider, per the table above</td>
</tr>
<tr>
<td><code>tool-offsets</code></td>
<td>Tool Offsets, read as Tool Offsets (ISO G43 H) where the Siemens table resolves</td>
<td>ungated; label switched by <code>hasSiemensToolOffsets</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTable.html">ToolOffsetTable</a></td>
</tr>
<tr>
<td><code>siemens-tool-offsets</code></td>
<td>Tool Offsets ($TC_DP)</td>
<td><code>hasSiemensToolOffsets</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a>, its cutting-edge map</td>
</tr>
<tr>
<td><code>tool-names</code></td>
<td>Tool Names</td>
<td><code>hasSiemensToolOffsets</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a>, its name map</td>
</tr>
<tr>
<td><code>datum-presets</code></td>
<td>Datum Presets (Q339)</td>
<td><code>hasDatums</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a>, its preset rows</td>
</tr>
<tr>
<td><code>datum-shifts</code></td>
<td>Datum Shifts (D)</td>
<td><code>hasDatums</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a>, its shift rows</td>
</tr>
<tr>
<td><code>frames</code></td>
<td>Frames (Siemens)</td>
<td><code>hasFrames</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a></td>
</tr>
<tr>
<td><code>retained-variables</code></td>
<td>Retained Common Variables</td>
<td><code>hasRetainedVariables</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.RetainedCommonVariableTable.html">RetainedCommonVariableTable</a></td>
</tr>
<tr>
<td><code>r-parameters</code></td>
<td>R Parameters</td>
<td><code>hasRParameters</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensRParameterTable.html">SiemensRParameterTable</a></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><strong>Two leaves stand on every brand: Work Coordinates (G54…) and Tool Offsets.</strong> They are the builder's
unconditional entries, and every brand preset satisfies them — each carries an ISO coordinate
provider of its own, and all five proxy the generic tool-offset table. The remaining leaves are
pushed inside a flag test, and each of those flags is satisfied by one brand family only.</p>
<h3 id="fewer-tables-than-leaves">Fewer tables than leaves</h3>
<p>Two flags grow two leaves each, because two leaves edit two halves of one object. Tool Offsets
($TC_DP) and Tool Names are the cutting-edge map and the name map of a single Siemens tool-offset
table, which is why there is no separate tool-name flag and why the two nodes appear and disappear
together. Datum Presets (Q339) and Datum Shifts (D) are the preset rows and the shift rows of a
single Heidenhain datum table, served by one component that reads its role from the node id's last
segment.</p>
<p>The work coordinates then fold two more leaves together, on the two brands where the ISO provider is
a table this plane already lists. On Siemens, Work Coordinates and Frames (Siemens) are two views of
one frame dictionary — the same ids, the same X/Y/Z translations, edited through two panels. On
Heidenhain, the G54–G59 rows of Work Coordinates <em>are</em> preset rows 1–6 of Datum Presets (Q339), so
editing one changes what the other shows. Datum Shifts has no such alias, and the Siemens frame
table's per-axis translation components beyond X/Y/Z are reachable from neither panel.</p>
<h3 id="the-two-tool-offset-ledgers-disagree-on-the-sign-of-wear">The two tool-offset ledgers disagree on the sign of wear</h3>
<p>Where both are present the difference matters more than the labels suggest. On the generic table an
effective value is the ideal minus the wear; on the Siemens <code>$TC_DP</code> table it is the geometry plus
the wear. That is why the Siemens panel spells the addition out in a footnote below its table, and
says in its own description that a shortened tool is entered as negative wear, while the generic
panel states no sign rule at all. The relabel of the ungated leaf to <strong>Tool Offsets (ISO G43 H)</strong>
exists so that the two are not read as one ledger.</p>
<h2 id="what-a-brand-change-does-here">What a Brand Change Does Here</h2>
<p>A brand switch replaces the whole runner with the target brand's preset and then removes every
per-case table the new runner references through no proxy. This plane is where that sweep lands: the
machine plane's project-owned parameter table is one entry, and every other entry the sweep can reach
is a table edited here. The staged select, its confirmation and the rest of the control are
<a class="xref" href="../machine/brand-switch.html">Controller Brand</a>; what follows is the outcome for this plane
alone.</p>
<ul>
<li><strong>Tool Offsets survives every switch.</strong> All five presets proxy the generic tool-offset table, so
the sweep's keep-set always contains it. Its rows, its tool numbering and the tool-house dependence
toggle beside it come through a brand change unchanged; only the leaf's label can change.</li>
<li><strong>Every other table on the plane is brand-bound and is removed</strong> the moment the target brand
proxies none of it — the Siemens <code>$TC_DP</code> table with its tool-name map, the Siemens frame table,
the Siemens R-parameter table, the Heidenhain datum table, and the Fanuc-family or Syntec parameter
table that holds the work coordinates. Switching back does not restore what was removed; the proxy
makes a fresh instance instead, empty for the tool-offset, tool-name, R-parameter and
retained-variable tables, and zero-filled for the frame and datum tables that seed a row set in
their constructor.</li>
<li><strong>The Fanuc and Mazak presets proxy the same parameter table</strong>, so a switch between those two
brands sweeps nothing here: the work coordinates and every other row of that table stay where they
are.</li>
<li><strong>Retained Common Variables spans a family rather than a brand.</strong> Fanuc, Syntec and Mazak all proxy
it, so it survives any switch among those three and is removed on a switch to Siemens or
Heidenhain.</li>
<li><strong>Work coordinates are the one set of values a switch can carry across.</strong> The brand panel's <em>Carry
work-coordinate XYZ (G54…) into the new brand's table</em> checkbox is on by default; the switch reads
every id the outgoing providers exposed and, after the swap, writes each back only to a provider
on the incoming runner that lists it — never to a provider that would merely accept it. The
Fanuc-family and Syntec presets expose the same set — G54–G59 and all forty-eight G54.1 P offsets
on the brand table, every address seeded, plus the extended G59.1–G59.9 on the brand-neutral
table behind it — so a switch between Syntec and either of Fanuc and Mazak carries every one of
them. A switch into Siemens carries only G54–G57, the frame table listing no G58, no G59, no
G59.x and no G54.1 P id at all; a switch into Heidenhain carries G54–G59 and drops the
forty-eight G54.1 P offsets and the nine G59.x. No other table's values are carried, and the carry
is skipped for a table the swap re-bound rather than replaced — which is the Fanuc-to-Mazak case.</li>
</ul>
<p>The banner shown before the apply says as much in one line: <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>An Object-Management Load, Paste or XML apply runs the same install and the same sweep, so a
controller file of another brand takes this plane through the identical outcome without the carry
option. Both writes are refused while an NC program is playing.</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 — the plane beside this one</li>
<li><strong>Program Data</strong> Group Row — <code>equipment/controller/program-data</code>
<ul>
<li><strong>Work Coordinates (G54…)</strong> Node Row</li>
<li><strong>Tool Offsets</strong> Node Row — reads <strong>Tool Offsets (ISO G43 H)</strong> where the Siemens <code>$TC_DP</code>
table resolves</li>
<li><strong>Tool Offsets ($TC_DP)</strong> Node Row — grown while the snapshot reports a Siemens tool-offset
table</li>
<li><strong>Tool Names</strong> Node Row — grown on the same flag</li>
<li><strong>Datum Presets (Q339)</strong> Node Row — grown while it reports a Heidenhain datum table</li>
<li><strong>Datum Shifts (D)</strong> Node Row — grown on the same flag</li>
<li><strong>Frames (Siemens)</strong> Node Row — grown while it reports a Siemens frame table</li>
<li><strong>Retained Common Variables</strong> Node Row — grown while it reports a retained common variable
table</li>
<li><strong>R Parameters</strong> Node Row — grown while it reports a Siemens R-parameter table</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Program Data</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 program-data
stem's id, label key and intro key, the two leaves it pushes unconditionally, the snapshot flag
each further leaf is pushed behind in the order the table above lists them, and the tool-offsets
relabel.</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, the
brand switch with its carry option, and the Object-Management install that regrows the branch.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcWorkCoordinatesPanel.vue</code> — the one panel that reads
the snapshot's work-coordinate kind, the caption it selects from it, and the extended-row filter.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcToolOffsetsPanel.vue</code> — the generic ledger: the
tool-house dependence toggle, the refresh it enables, and the Siemens caveat gated on the brand
string rather than on the table flag.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcSiemensToolOffsetsPanel.vue</code> and
<code>wwwroot-src/src/components/controlTree/SoftNcToolNamesPanel.vue</code> — the two leaves over one Siemens
table: the cutting-edge rows and the name map.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcDatumTablePanel.vue</code> — the one component behind both
Heidenhain leaves, choosing its role from the node id's last segment.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcFramesPanel.vue</code> — the settable-frame table and its
own always-visible set, narrower than the work-coordinate panel's.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcRetainedVariablesPanel.vue</code> and
<code>wwwroot-src/src/components/controlTree/SoftNcRParametersPanel.vue</code> — the two sparse variable
tables, and the vacant entry a cleared cell writes.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue</code> — the shared no-runner line every
leaf on this plane falls back to.</li>
<li><code>wwwroot-src/src/api/equipmentSetup.ts</code> — the canvas marker id a work-coordinate row click writes,
outside this branch's own REST surface.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the stem's introduction line, every leaf label in the table
above, the work-coordinate storage captions and the brand-switch warning.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared column and action labels these tables reuse.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the plane's REST surface: the presence flags, the per-table
readers and writers, the brand switch with its work-coordinate carry, and the sweep of per-case
tables the new runner references through no proxy.</li>
<li><code>Mech/EquipmentSetupDisplayController.cs</code> — the marked-coordinate id a work-coordinate row click
stores, on the user configuration rather than on the project.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/NcRunnerSuit.cs</code> — the suit: the per-case list this plane's tables live in, the
serialization that inlines it with no file reference, and the proxy wiring that materialises a
table into a project holding none of that type.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets as literal dependency lists, showing
which proxies each brand carries, and the proxy resolution every read on this plane goes through.</li>
<li><code>HiMech/NcParsers/Dependencys/INcDependencyProxy.cs</code> — the maker-and-taker contract, and why a
proxy's resolved data is never written into the runner.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTableProxy.cs</code> — the seedless get-or-create shape
the Siemens, Heidenhain and Fanuc-family per-case proxies on this plane all repeat.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the contrasting seeded proxy,
and its own account of why the Fanuc-family table mixes machine configuration with the per-case
work-coordinate offsets.</li>
<li><code>HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs</code> and
<code>HiMech/NcParsers/Dependencys/IsoCoordinateAddressMap.cs</code> — the interface the work-coordinate panel
edits through, and the address arithmetic that decides which ids a Fanuc-family or Syntec table
lists.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTable.cs</code> — the generic ledger every preset
proxies, and the subtraction that makes an effective value ideal minus wear.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensToolOffsetTable.cs</code> — the cutting-edge map and the
name map behind two leaves, and the addition that makes an effective value geometry plus wear.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTable.cs</code> — the settable frames: the ids the
constructor seeds, the ISO coordinate implementation the work-coordinate leaf shares, and the
per-axis translation components neither panel exposes.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensRParameterTable.cs</code> and
<code>HiMech/NcParsers/Dependencys/Fanuc/RetainedCommonVariableTable.cs</code> — the two persistent variable
tables, their accepted id ranges, and the vacant entry a valueless element preserves across a save.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs</code> — the preset and shift rows behind
two leaves, and the mapping that aliases G54–G59 onto preset rows 1–6.</li>
<li><code>HiMech/MachiningProcs/MachiningActRunnerConfig.cs</code> — the tool-house dependence flag, the one
setting on this plane stored outside the per-case list.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the facade setter every install and brand switch
assigns through, and the proxy re-binding that materialises the incoming brand's per-case tables.</li>
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — where the suit and the runner configuration hang off
the project, and the load path for a project file carrying the per-case list.</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="work-coordinates.html">Work Coordinates</a> — The one uniform face over four brand
storage models, its P0 and M0 row actions, and the row click that marks a coordinate on the canvas</li>
<li><a class="xref" href="tool-offsets.html">Tool Offsets</a> — The generic ISO ledger: ideal against wear, the
renameable tool number, and what tool-house dependence takes over</li>
<li><a class="xref" href="siemens-tool-offsets.html">Siemens Tool Offsets</a> — The <code>$TC_DP</code> cutting-edge table
and the tool-name map beside it, two leaves over one object</li>
<li><a class="xref" href="datum-tables.html">Datum Tables</a> — The Heidenhain preset and shift rows, one
component serving both, and the preset rows the work coordinates alias</li>
<li><a class="xref" href="frames.html">Frames</a> — The Siemens settable frames, the ids they seed, and what
the work-coordinate leaf shows of the same table</li>
<li><a class="xref" href="persistent-variables.html">Persistent Variables</a> — The retained common variables
and the R parameters: sparse tables, accepted ranges, and the difference between vacant and deleted</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 machine plane
beside it</li>
<li><a class="xref" href="../machine/index.html">Machine and Controller Plane</a> — the other plane, whose ownership splits between the runner and
the project where this one does not</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="work-coordinates.html">Work Coordinates</a> — the ungated leaf whose backing object changes with the
brand, and the only values a brand switch can carry</li>
<li><a class="xref" href="tool-offsets.html">Tool Offsets</a> — the other ungated leaf, and the one table on this plane that
survives every brand switch</li>
<li><a class="xref" href="siemens-tool-offsets.html">Siemens Tool Offsets</a> — the second tool-offset ledger, whose presence
relabels the first</li>
<li><a class="xref" href="datum-tables.html">Datum Tables</a> — the two Heidenhain leaves, and the preset rows the work
coordinates share</li>
<li><a class="xref" href="frames.html">Frames</a> — the Siemens frame table, read by two leaves of this plane at once</li>
<li><a class="xref" href="persistent-variables.html">Persistent Variables</a> — the two variable tables a program's arithmetic
reads, kept on the project rather than reset with the session</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,565 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Persistent Variables | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Persistent Variables | 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-ControllerPersistentVariables">
<h1 id="persistent-variables">Persistent Variables</h1>
<p>Persistent Variables is the pair of program-variable ledgers the Controller branch grows for one
brand or the other, and no shipped preset carries both: the Fanuc-style retained common variables
and the Sinumerik R parameters. Both live on the General Setup page at <code>/general-setup</code> under the
Control-Tree ids <code>equipment/controller/program-data/retained-variables</code> and
<code>equipment/controller/program-data/r-parameters</code> — reached as
<code>?tree=equipment/controller/program-data/retained-variables</code> and
<code>?tree=equipment/controller/program-data/r-parameters</code> — and the tree labels them
<strong>Retained Common Variables</strong> and <strong>R Parameters</strong>. They share a page because no preset shows both:
each node is grown by a flag that one set of brand presets satisfies and the other set does not.</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="two-nodes-that-exclude-each-other">Two Nodes That Exclude Each Other</h2>
<p>The branch builder pushes each of the two nodes inside a test of its own, one snapshot flag each, and
each flag is a single type probe over the runner's proxy-resolved dependency list —
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.RetainedCommonVariableTable.html">RetainedCommonVariableTable</a> behind one and
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensRParameterTable.html">SiemensRParameterTable</a> behind the other. Which brand
satisfies which gate is <a class="xref" href="../brand-matrix.html">Brand Matrix</a>; the short answer is that
the Fanuc, Syntec and Mazak presets carry a retained-common-variable proxy, the Siemens preset carries
an R-parameter proxy, and the Heidenhain preset carries neither.</p>
<p><strong>The exclusion is a property of the five preset lists, not a rule in the builder.</strong> Nothing in the
tree code forbids the two rows from standing together; no shipped preset simply carries both entries,
and the brand select can produce nothing but a preset. A runner composed by hand or loaded from a
controller file whose pipeline list named both tables would grow both rows, each with its own working
panel — and the Object-Management menu on the branch root, described in
<a class="xref" href="../index.html">Controller Branch</a>, installs such a file without inspecting what its
pipeline names, so that route is open from inside the branch.</p>
<p>The two panels are separate components rather than one component discriminating on the node id, so
neither reads the node it was given. What they share is a shell: the same three-state body, the same
dense table, the same footer, and the same commit, rollback and toast rules the whole branch follows
— <a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="what-each-ledger-is">What Each Ledger Is</h2>
<h3 id="retained-common-variables">Retained Common Variables</h3>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.RetainedCommonVariableTable.html">RetainedCommonVariableTable</a> models the Fanuc-style ISO
controller's common variables in the retained range <code>#500</code>–<code>#999</code> — the range a real control keeps in
non-volatile memory across a power cycle, which is why the model stores it with the project rather
than with the run. It is the range, not the whole macro-variable surface: the table answers a <code>#nnn</code>
lookup only for 500 through 999 and returns nothing for any other key, so an out-of-range reference
falls through to the next lookup in the evaluator's chain.</p>
<p>The neighbouring ranges are deliberately elsewhere, and none of them has a node on this branch.
<code>#1</code>–<code>#33</code> are call-frame locals bounded by a macro call rather than by a power cycle. <code>#100</code>–<code>#499</code>
are the non-retained commons, carried block to block in the per-block dataflow and cleared on the
block that ends the program. <code>#1000</code> and above are read-only or computed from runtime state rather
than stored here: some resolve against other dependencies — the brand parameter table behind the
work-coordinate addresses, the tool offset table behind <code>#2001</code>–<code>#2200</code> — and some come from the
block's own record, which is where the position variables are read and where a system-control write
is recorded rather than emulated.</p>
<p>The type name and the tree label both say <em>common variable</em> rather than <em>macro variable</em>, because the
model reserves &ldquo;macro&rdquo; for Custom Macro B's call frame. The hint above the table is the one string in
the leaf that says <strong>Retained macro variables</strong> instead.</p>
<h3 id="r-parameters">R Parameters</h3>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensRParameterTable.html">SiemensRParameterTable</a> models the Sinumerik arithmetic
variable surface <code>R0</code>–<code>R999</code> — the <code>R63=100.5</code> and <code>C=R61</code> of a Siemens program. On the control these
sit in retentive memory and survive both program end and a power cycle, and the model keeps them the
same way: stored with the project, not reset with the session.</p>
<p>The table answers an <code>Rn</code> key in the canonical uppercase form the Siemens expression parser produces,
and a lowercase <code>rn</code> from a raw capture resolves as well. Every other key returns nothing.</p>
<h2 id="addressing-and-ranges">Addressing and Ranges</h2>
<table>
<thead>
<tr>
<th></th>
<th>Retained Common Variables</th>
<th>R Parameters</th>
</tr>
</thead>
<tbody>
<tr>
<td>Node id</td>
<td><code>equipment/controller/program-data/retained-variables</code></td>
<td><code>equipment/controller/program-data/r-parameters</code></td>
</tr>
<tr>
<td>Snapshot flag</td>
<td><code>hasRetainedVariables</code></td>
<td><code>hasRParameters</code></td>
</tr>
<tr>
<td>Backing table</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.RetainedCommonVariableTable.html">RetainedCommonVariableTable</a></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensRParameterTable.html">SiemensRParameterTable</a></td>
</tr>
<tr>
<td>Brands</td>
<td>Fanuc, Syntec, Mazak</td>
<td>Siemens</td>
</tr>
<tr>
<td>Key column header</td>
<td><strong>Variable</strong></td>
<td><strong>Parameter</strong></td>
</tr>
<tr>
<td>Key cell reads</td>
<td><code>#</code> then the number</td>
<td><code>R</code> then the number</td>
</tr>
<tr>
<td>Accepted ids</td>
<td>500–999</td>
<td>0–999</td>
</tr>
<tr>
<td>Bounds on the add field</td>
<td>minimum 500, maximum 999</td>
<td>minimum 0, maximum 999</td>
</tr>
<tr>
<td>Absent-table body</td>
<td><em>&quot;No retained common variable table on the active runner.&quot;</em></td>
<td><em>&quot;No R-parameter table on the active runner.&quot;</em></td>
</tr>
</tbody>
</table>
<p>Each range is a pair of constants declared on the table type, and four separate places enforce it.
The panel refuses an add whose id is not a whole number inside the range and says so in a toast of its
own. The numeric field carrying that id rejects a <em>finite</em> value outside its bounds before the panel
ever sees it — the infinity and NaN literals the field also parses skip the bounds test entirely and
are stopped by the panel's whole-number check instead. The write endpoint compares the id against the
same two constants and answers unsuccessfully with the range spelled out in the message. And behind
all three, the table's own writer ignores an out-of-range id — the guard that holds for a caller
reaching the table directly rather than through the endpoint, which is how a run writes.</p>
<p>The endpoint's guard is not reachable from a project this app wrote. The rows a panel writes to came
from the table, the add path is already filtered, and the serializer drops an out-of-range key on the
way out, so no file the app saves carries one. Loading applies no such filter: a project file edited
by hand can carry an id outside the range, the read lists it and the panel shows it, and editing that
row's value is the one way a reader meets the endpoint's range message.</p>
<p><strong>No range is narrowed by machine data.</strong> The R range in particular is the fixed pair 0–999 on the
table type; the endpoint reads no value from the Siemens machine-data table before accepting an id,
so the parameter count a real control would allocate does not constrain what this leaf accepts.</p>
<p><strong>Neither table is dense.</strong> A row exists for an id that has been written, and for no other, so a
thirty-row table and a four-row table are equally normal and the numbers in the key column need not be
contiguous. A project that has never written one shows an empty table body under the header, because
the proxy that materialises the table installs a bare instance carrying nothing —
<a class="xref" href="index.html">Program Data Plane</a> covers the seedless proxies this plane is
built from. The read returns the rows in ascending id order and the panel never re-sorts: an edit
leaves a row where it is, a delete removes it in place, and only the re-read that follows an add
re-orders anything.</p>
<h2 id="what-persists-and-what-clears">What Persists, and What Clears</h2>
<p>Both tables are per-case entries of the project's own dependency list, reached through a get-or-create
placeholder in the runner's pipeline list. The runner file records only an empty placeholder element,
so the values are written into the project file rather than into a controller asset.</p>
<p>Four things in the runtime could plausibly discard what these tables hold. Three of them leave both
tables alone; the fourth removes a table outright rather than clearing it.</p>
<ul>
<li><strong>A new session does not clear them.</strong> The session-init edge that rebuilds the syntax pipeline
sweeps every dependency and syntax declaring session-scoped state — iteration counters, index
allocators — and calls each one's reset. Neither table declares any, so both are skipped.</li>
<li><strong>A power reset does not clear them.</strong> The power-reset command sweeps the proxy-resolved dependency
list for dependencies declaring a volatile subset and then resets the session state, dropping the
per-block dataflow. None of the dependencies a brand preset carries declares a volatile subset, so
what a power reset actually discards is the dataflow — which is where the non-retained commons live
— while every value in these two tables stands.</li>
<li><strong>Program end does not clear them.</strong> The syntax that models a control's <code>M02</code> / <code>M30</code> reset empties
the block's volatile dictionary and cancels an active modal macro. It writes into the block's own
record and reaches no dependency at all, so neither table is in its path.</li>
<li><strong>A brand switch removes the whole table rather than clearing it.</strong> The sweep that follows a runner
swap keeps exactly the per-case tables the new runner resolves through a proxy, so a switch to a
brand that proxies neither drops the table outright, and switching back materialises a fresh empty
one rather than the values that were there. Retained Common Variables is the wider of the two: all
three of Fanuc, Syntec and Mazak proxy it, so it survives any switch among those three and is
removed on a switch to Siemens or Heidenhain. R Parameters survives only a Siemens-to-Siemens
re-flash. What a switch keeps, resets and destroys across the whole plane is
<a class="xref" href="index.html">Program Data Plane</a>.</li>
</ul>
<h3 id="a-run-writes-into-the-same-table">A run writes into the same table</h3>
<p>These are not a snapshot of what a program did — they are the store the program uses. Each brand's
syntax list carries a reading syntax that consumes a literal assignment out of the parsed block and
writes it straight into the table: <code>#500 = 1.234</code> on the three Fanuc-style brands, <code>R63 = 100.5</code> on
Siemens. No mirror is kept in the per-block dataflow; the table is the single source of truth. A
non-literal right-hand side is resolved to a literal earlier in the same block by the expression
normaliser and then lands by the same route, so <code>#600 = #500 + 1</code> reaches the table as well.</p>
<p>Reads run the other way through the same object: each table is a variable lookup on the effective
dependency list, so an expression naming an id in range reads whatever the leaf shows.</p>
<p>Two consequences follow for a reader of this leaf. A played program changes what the table holds, and
because no panel in the branch is pushed at, the change appears only when the panel is remounted by
selecting another node and coming back. And because the table is written into the project file, those
run-time writes are what the next load of that project starts from — once the project is saved.
Nothing in the branch and nothing on the run path saves on its own: a panel edit and a run-time write
alike stand in memory until an explicit save, and closing the project without one discards them.</p>
<h3 id="vacant-is-a-value-and-it-fails-loud">Vacant is a value, and it fails loud</h3>
<p>A stored null is a <strong>vacant</strong> entry, and so is a missing key — the table reads the two identically.
Vacant is not zero. An expression that evaluates a vacant id does not fall back to a number: the
evaluator returns a failure carrying the code <code>Variable--Vacant</code> and a message naming the key, which
is what the R-parameter hint means by reporting an error instead of silently using 0.</p>
<p>Vacant and deleted are different operations even though they read the same at run time. Clearing a
cell keeps the key with a null value; the delete button drops the key. The distinction survives a save,
because a vacant entry is written as an element carrying an id and no value while a deleted one is
written as nothing at all — so a vacant row comes back as a row, and a deleted one does not come back.</p>
<h2 id="the-editor-body">The Editor Body</h2>
<p>Above the table sits a one-line hint, and it is the only prose either panel shows. Both render the
literal text <code>&lt;vacant&gt;</code> inside it as inline code.</p>
<ul>
<li>Retained Common Variables: <em>&quot;Retained macro variables #500–#999 (power-off safe). Empty =
<code>&lt;vacant&gt;</code>. #100–#499 are volatile and live in the run's dataflow, not here.&quot;</em></li>
<li>R Parameters: <em>&quot;Sinumerik R parameters R0–R999 (retentive). Empty = <code>&lt;vacant&gt;</code> — a program reading
a vacant R parameter reports an error instead of silently using 0.&quot;</em></li>
</ul>
<p>The table below it has three columns. The first is the key — headed <strong>Variable</strong> or <strong>Parameter</strong> —
rendered bold as plain text and not editable, which is the branch's rule for a key column. The second
is headed <strong>Value</strong> and holds the shared numeric field. The third carries the row's delete button
under a blank header, so the branch's shared <strong>Actions</strong> heading does not appear on either leaf.</p>
<h3 id="editing-a-value">Editing a value</h3>
<p>A value cell is the shared numeric widget, so it commits on blur or on Enter and never per keystroke;
its parsing, its bound behaviour and the double commit an Enter can cause are
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>. No value cell is bounded — neither panel passes a minimum
or a maximum to it — so any finite number is accepted and stored, negatives included.</p>
<p><strong>These two panels are the branch's only pair that commits a null.</strong> Every other numeric handler in
the branch treats a cleared cell as no edit and returns before the request; here the null is sent,
because vacating an entry is a real state of both tables and there is no other control that reaches
it. Clearing the cell is therefore how a row is made vacant while keeping its key.</p>
<p>The handler is otherwise the branch's ordinary optimistic write — assign, send, and on failure put the
captured value back and raise the toast — and it carries no finiteness test. The widget parses
<code>Infinity</code> and <code>-Infinity</code> as values, and neither survives JSON encoding: the request body reaches the
server carrying a null, and the entry is set vacant. The cell keeps showing <code>Infinity</code> until the panel
is remounted, so that is the one place in the table body where the screen and the store disagree
after a write the server accepted. A typed <code>NaN</code> takes the same route to vacant, and the cell renders
it as empty, which is what a vacant entry looks like anyway.</p>
<h3 id="adding-and-removing-a-row">Adding and removing a row</h3>
<p>The footer under the table is the branch's fielded add form: a numeric field for the id — labelled
<strong>Variable # (500–999)</strong> or <strong>Parameter # (0–999)</strong> — a second numeric field labelled <strong>Value</strong>, and a
primary <strong>Add / Set</strong> button. Both fields are the same numeric widget as the value cells, so what they
hold reaches the panel on blur or Enter rather than per keystroke, and neither input submits on Enter.</p>
<p>The button label's second word is the accurate one: the write is an upsert. Entering an id the table
already lists overwrites that row's value rather than adding a second row. Leaving <strong>Value</strong> empty
creates the row vacant. A successful add is followed by a full re-read of the table, which is what
puts the new row in ascending order; the two fields keep what was typed in them.</p>
<p>Two refusals sit in front of that write, and they read differently. An id outside the field's own
bounds is rejected inside the widget, which shows its message under the box, emits nothing and leaves
the rejected text on screen — so the panel still holds the id the field last committed to it. On a
panel that has committed none, that is nothing at all, and pressing <strong>Add / Set</strong> reports the panel's
own message: <em>&quot;Variable number must be an integer in 500–999.&quot;</em> or <em>&quot;Parameter number must be an
integer in 0–999.&quot;</em> The panel's message is also what a fractional id produces, since the panel
requires a whole number and the field does not.</p>
<p>A field that has already committed a valid id keeps it, and nothing clears either field after an add.
So an out-of-range id typed over one the field already accepted is the second place on these leaves
where the screen and the store part company: the box shows the rejected number, the panel still holds
the earlier id, and <strong>Add / Set</strong> upserts that earlier id with whatever the value field holds — no
message of any kind, and the re-read that follows leaves the rejected number in the box.</p>
<p>Removal is one icon button per row, with no label, no tooltip and no confirmation dialog: the click
sends the delete, and the row is filtered out of the panel's list once the request resolves. The branch
guards some of its deletions with a confirmation dialog; neither of these two leaves is among them.
The endpoint removes the key and reports success whether or not the key was there.</p>
<h2 id="what-the-surface-covers">What the Surface Covers</h2>
<p>A third table of the same shape exists in the engine and has no node.
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Heidenhain.HeidenhainQParameterTable.html">HeidenhainQParameterTable</a> holds the Heidenhain persistent
Q parameters — the free range <code>Q0</code>–<code>Q99</code> and the permanent <code>QR0</code>–<code>QR499</code>, in two stores of its own —
and the Heidenhain preset carries a proxy for it beside the two documented above. It is materialised
into the project's per-case list exactly as they are, is serialized into the project file the same way,
is swept by a brand switch away from Heidenhain the same way, and is read and written during a run by
the Heidenhain reading syntax that routes <code>Qn</code> and <code>QRn</code> assignments by id range.</p>
<p>What it has no part in is this branch. The snapshot the tree is built from declares no flag that
probes it, the branch's REST surface declares no route that reaches it, and the branch builder mints
no node for it. The Controller branch's editable persistent-variable surface is the two tables named
above; a Heidenhain project's Q parameters are set and read by the program that runs, and are carried
by the project file between runs.</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>Program Data</strong> Group Row — <code>equipment/controller/program-data</code>
<ul>
<li><strong>Retained Common Variables</strong> Node Row —
<code>equipment/controller/program-data/retained-variables</code>; grown while the snapshot reports a
retained common variable table</li>
<li><strong>R Parameters</strong> Node Row — <code>equipment/controller/program-data/r-parameters</code>; grown while the
snapshot reports a Siemens R-parameter table</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Retained Common Variables</strong> Panel
<ul>
<li>No-Runner Body — the shared <em>&quot;No NC runner — load a project first.&quot;</em>; shown first</li>
<li>Absent-Table Body — <em>&quot;No retained common variable table on the active runner.&quot;</em></li>
<li>Editor Body
<ul>
<li>Hint Caption — <em>&quot;Retained macro variables #500–#999 (power-off safe). Empty = <code>&lt;vacant&gt;</code>.
#100–#499 are volatile and live in the run's dataflow, not here.&quot;</em></li>
<li>Variable Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Variable</strong>, <strong>Value</strong>, and a blank third heading</li>
<li>Variable Row, one per stored id
<ul>
<li>Variable Number Label — bold plain text, <code>#</code> then the number, not editable</li>
<li>Value Numeric Field — unbounded; cleared commits vacant</li>
<li>Delete Button — a bin icon, flat and dense, no label and no confirmation</li>
</ul>
</li>
</ul>
</li>
<li>Add Row Footer
<ul>
<li><strong>Variable # (500–999)</strong> Numeric Field — minimum 500, maximum 999</li>
<li><strong>Value</strong> Numeric Field — unbounded; left empty adds the row vacant</li>
<li><strong>Add / Set</strong> Button — primary, unelevated</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><strong>R Parameters</strong> Panel — the same shell with the Siemens strings
<ul>
<li>No-Runner Body — the shared <em>&quot;No NC runner — load a project first.&quot;</em></li>
<li>Absent-Table Body — <em>&quot;No R-parameter table on the active runner.&quot;</em></li>
<li>Editor Body
<ul>
<li>Hint Caption — <em>&quot;Sinumerik R parameters R0–R999 (retentive). Empty = <code>&lt;vacant&gt;</code> — a program
reading a vacant R parameter reports an error instead of silently using 0.&quot;</em></li>
<li>Parameter Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Parameter</strong>, <strong>Value</strong>, and a blank third heading</li>
<li>Parameter Row, one per stored id
<ul>
<li>Parameter Number Label — bold plain text, <code>R</code> then the number, not editable</li>
<li>Value Numeric Field — unbounded; cleared commits vacant</li>
<li>Delete Button — a bin icon, flat and dense, no label and no confirmation</li>
</ul>
</li>
</ul>
</li>
<li>Add Row Footer
<ul>
<li><strong>Parameter # (0–999)</strong> Numeric Field — minimum 0, maximum 999</li>
<li><strong>Value</strong> Numeric Field — unbounded; left empty adds the row vacant</li>
<li><strong>Add / Set</strong> Button — primary, unelevated</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Neither panel opens a dialog, and neither carries a select, a toggle or a <strong>Show all</strong> control.</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/SoftNcRetainedVariablesPanel.vue</code> — the <code>#500</code>–<code>#999</code>
ledger: the hint with its vacant slot, the three-column table, the value handler that sends a null
without a finiteness test, the unconfirmed delete, and the add form with its whole-number and range
check.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcRParametersPanel.vue</code> — the <code>R0</code>–<code>R999</code> ledger: the
same shell with the Siemens strings, the <code>R</code>-prefixed key cell, and the add field bounded from 0.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: the two node ids,
their labels and label keys, the two flags each is pushed behind, and the item types binding them to
the two panels.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the two readers, their identical row shape with a nullable
value, the two setters and the two removers, and the two snapshot flags the builder tests.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the helper that turns a success-flagged failure body into a thrown
error, which is how an out-of-range write would reach a panel.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the value and add fields: commit on blur or
Enter, the empty-text-to-null parse both panels forward, the infinity and NaN literals it also
accepts, and the bound rejection that shows a message and emits nothing.</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/composables/useSoftNcRunner.ts</code> — the shared snapshot the first empty layer reads,
and the events that re-read it.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the two node labels, the two hints, the two absent-table
bodies, the two add-field labels, the two invalid-id messages and the error contexts each panel
prefixes its toast with.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Value</strong> column label and the <strong>Add / Set</strong> button
label.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the two flag probes in the snapshot builder,
the two reads that order rows by ascending id and report presence from the table alone, the two
upsert writers with their range guards, the two removers, and the 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/Dependencys/Fanuc/RetainedCommonVariableTable.cs</code> — the <code>#500</code>–<code>#999</code> store: the
range constants, the nullable dictionary that makes vacant a value, the writer that ignores an
out-of-range id, the variable lookup that answers only in range, and the serialization that keeps a
vacant entry as a valueless element.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/RetainedCommonVariableTableProxy.cs</code> — the seedless
get-or-create placeholder the three Fanuc-style presets carry, the bare table it installs into a
project holding none, and the legacy element name an older project file still deserializes through.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensRParameterTable.cs</code> — the <code>R0</code>–<code>R999</code> store: the range
constants and the reason the upper one is 999, and the lookup that accepts an uppercase or lowercase
<code>R</code> key.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensRParameterTableProxy.cs</code> — the matching seedless
placeholder carried by the Siemens preset alone.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainQParameterTable.cs</code> — the third table of the same
shape: the free and permanent Q stores, the ranges each accepts, and the system and volatile ranges
it deliberately declines.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainQParameterTableProxy.cs</code> — its placeholder in the
Heidenhain preset, seedless like the other two.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets as literal dependency lists, which is
where the mutual exclusion of the two nodes is actually decided, and the proxy resolution every flag
probe and every read goes through.</li>
<li><code>HiMech/NcParsers/NcRunnerSuit.cs</code> — the per-case list these tables live in, and its inline
serialization beside the runner.</li>
<li><code>HiMech/NcParsers/Dependencys/INcDependencyProxy.cs</code> — the maker-and-taker contract, and why a
proxy's resolved table is never written into the runner file.</li>
<li><code>HiMech/NcParsers/ISessionResettable.cs</code> — the session-scoped contract neither table implements, and
its own statement of the boundary between session state and persistent state.</li>
<li><code>HiMech/NcParsers/Dependencys/IPowerResettable.cs</code> — the volatile-subset contract a power reset
sweeps, which no dependency of any brand preset declares.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/ProgramEndCleanSyntax.cs</code> — the program-end clear that empties the
block's volatile dictionary and names the retained range as untouched.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/VolatileVariableReadingSyntax.cs</code> — the <code>#100</code>–<code>#499</code> range that
has no node here: the per-block dictionary it carries forward, and the session that bounds it.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucPositionVariableLookup.cs</code> and
<code>HiMech/NcParsers/EvaluationSyntaxs/Fanuc/FanucSystemControlVariableSyntax.cs</code> — the two <code>#1000</code>-and-up
groups no dependency answers: the position variables read from the previous block's record, and the
system-control writes recorded on the block rather than emulated.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/RetainedCommonVariableReadingSyntax.cs</code> — the run-time writer for
the retained range: the literal assignment it consumes and writes straight into the table, with no
mirror kept.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Siemens/SiemensRParameterReadingSyntax.cs</code> — the same shape for
<code>Rn</code> assignments.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Heidenhain/HeidenhainQParameterReadingSyntax.cs</code> — the same shape
for <code>Qn</code> and <code>QRn</code>, routing by id range into the free store, the permanent store, a read-only warning
or the volatile dataflow.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/VariableEvaluatorSyntax.cs</code> — the normaliser that resolves a
non-literal right-hand side to a literal before the reading syntaxes run, and the lookup chain these
tables join.</li>
<li><code>HiMech/NcParsers/EvaluationSyntaxs/Evaluation/IVariableLookup.cs</code> and
<code>HiMech/NcParsers/EvaluationSyntaxs/Evaluation/NcExpressionEvaluator.cs</code> — the lookup contract both
tables implement, and the vacant failure an expression raises instead of reading zero.</li>
<li><code>HiMech/NcParsers/Syntaxs/FanucSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SyntecSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/MazakSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/SiemensSyntaxUtil.cs</code> and
<code>HiMech/NcParsers/Syntaxs/HeidenhainSyntaxUtil.cs</code> — the five syntax lists, each carrying the reading
syntax for its brand's persistent variable range.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the facade setter every install and brand switch
assigns through, and the power reset that sweeps volatile subsets and then drops the session
dataflow.</li>
<li><code>HiNc/MachiningProcs/SessionShell.cs</code> — the scriptable power-reset entry point over that service
call.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Program Data Plane</a> — the plane both leaves sit on: where their tables are stored,
the seedless proxies that make them, and what a brand switch keeps or sweeps beside 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,596 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Siemens Tool Offsets | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Siemens Tool Offsets | 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-ControllerSiemensToolOffsets">
<h1 id="siemens-tool-offsets">Siemens Tool Offsets</h1>
<p>Siemens Tool Offsets is the Sinumerik <code>$TC_DP</code> ledger — one offset row per cutting edge of a tool,
addressed by the tool number and the edge number together — plus the tool-name map that resolves a
string tool call to a tool number. It occupies two leaves of the Controller branch on the General
Setup page at <code>/general-setup</code>, under the Control-Tree ids
<code>equipment/controller/program-data/siemens-tool-offsets</code> and
<code>equipment/controller/program-data/tool-names</code>, and those two ids are the whole of what this page
owns. Both are grown by one snapshot flag over one backing object, which is why one page carries
them.</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>Both panels follow the branch's shared rules — one read on mount, per-control commit, the
capture-assign-await-restore write, the two empty layers, and a dense markup table with no sort and
no pagination. Those rules, and the places these two panels depart from them, are
<a class="xref" href="../editing-contract.html">Editing Contract</a>. What is these leaves' own is everything
below.</p>
<h2 id="two-nodes-over-one-table">Two Nodes Over One Table</h2>
<p>The tree labels differ, the panels differ, and the object behind them does not.</p>
<table>
<thead>
<tr>
<th>Node id</th>
<th>Tree label</th>
<th>Which half of the object</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>equipment/controller/program-data/siemens-tool-offsets</code></td>
<td><strong>Tool Offsets ($TC_DP)</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolOffsetTable_Edges">Edges</a> — the offset rows</td>
</tr>
<tr>
<td><code>equipment/controller/program-data/tool-names</code></td>
<td><strong>Tool Names</strong></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolOffsetTable_ToolNames">ToolNames</a> — the name map</td>
</tr>
</tbody>
</table>
<p>The branch builder pushes both inside a single test of the snapshot's Siemens tool-offset flag, so
they appear and disappear together and there is no separate tool-name flag anywhere in the snapshot.
The flag itself is a type probe for <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a>
over the proxy-resolved dependency list, and the Siemens preset is the only shipped brand list that
carries the proxy for it — <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>The same flag does one more thing: it relabels the ungated generic offset leaf one row above from
<strong>Tool Offsets</strong> to <strong>Tool Offsets (ISO G43 H)</strong>, without touching that leaf's id. The two ledgers
are present together on a Siemens project and an unqualified label would blur them into one — see
<a class="xref" href="tool-offsets.html">Tool Offsets</a>.</p>
<p>Each panel reads only its own half. The <code>$TC_DP</code> panel reads and writes rows; the Tool Names panel
reads and writes name mappings; neither reports the other's edits, and neither re-reads when the
other writes. The presence flag both receive is the same one — whether the table object resolved —
so the Tool Names panel's absent-table line, <em>&quot;No Siemens tool-name table on the active runner.&quot;</em>,
names a table that is not a type of its own.</p>
<h2 id="the-key-has-two-parts">The Key Has Two Parts</h2>
<p><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.ISiemensToolOffsetConfig.html">ISiemensToolOffsetConfig</a> addresses an offset by a <strong>tool
number</strong> and a <strong>cutting edge number</strong> together, where
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IToolOffsetConfig.html">IToolOffsetConfig</a> — the contract behind the generic ledger — takes a
single integer. That is the structural difference between the two ledgers, and it is why one tool can
carry several rows: a tool with more than one usable edge stores an offset per edge, and the program
selects among them with the <code>D</code> word after the tool is mounted.</p>
<p>The pair is the dictionary key, and both halves are rendered as bold plain text with a hard-coded
letter prefix — <code>T</code> on the tool cell and <code>D</code> on the edge cell. Neither is editable and no rename
route exists on either half of the object, so a key cannot be corrected in place. Neither can a
chosen one be asked for: the <code>$TC_DP</code> add accepts no target pair — it mints the next tool, or the
next edge of the tool it is given — so arriving at a particular pair means growing the table up to it
and deleting what was stepped through. Only the tool-name half takes a typed key. That same
server-side minting is why nothing on this leaf can create a duplicate.</p>
<p>An unconfigured pair is not an error. The interface contract returns 0 for a pair the table has no
row for, and the try-form is the only reliable miss signal, because 0 is itself a legal stored
offset — a distinction that decides the fallback below.</p>
<h2 id="the-columns">The Columns</h2>
<p>Ten labelled columns: the two key columns, then eight value columns in millimetres over the four
geometry components and their four wear counterparts.</p>
<table>
<thead>
<tr>
<th>Column header</th>
<th><code>$TC_DP</code> index</th>
<th>Stored as</th>
<th>Read by the pipeline</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Tool T</strong></td>
<td>—</td>
<td>the key's tool number</td>
<td>the tool the offsets belong to</td>
</tr>
<tr>
<td><strong>Edge D</strong></td>
<td>—</td>
<td>the key's edge number</td>
<td>the <code>D</code> word</td>
</tr>
<tr>
<td><strong>Length 1 (Z)</strong></td>
<td>3</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_Length1_mm">Length1_mm</a></td>
<td>the height path</td>
</tr>
<tr>
<td><strong>Length 2 (X)</strong></td>
<td>4</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_Length2_mm">Length2_mm</a></td>
<td>nothing</td>
</tr>
<tr>
<td><strong>Length 3 (Y)</strong></td>
<td>5</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_Length3_mm">Length3_mm</a></td>
<td>nothing</td>
</tr>
<tr>
<td><strong>Radius</strong></td>
<td>6</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_Radius_mm">Radius_mm</a></td>
<td>nothing</td>
</tr>
<tr>
<td><strong>Length 1 Wear</strong></td>
<td>12</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_WearLength1_mm">WearLength1_mm</a></td>
<td>the height path</td>
</tr>
<tr>
<td><strong>Length 2 Wear</strong></td>
<td>13</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_WearLength2_mm">WearLength2_mm</a></td>
<td>nothing</td>
</tr>
<tr>
<td><strong>Length 3 Wear</strong></td>
<td>14</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_WearLength3_mm">WearLength3_mm</a></td>
<td>nothing</td>
</tr>
<tr>
<td><strong>Radius Wear</strong></td>
<td>15</td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_WearRadius_mm">WearRadius_mm</a></td>
<td>nothing</td>
</tr>
</tbody>
</table>
<p>The table declares accessors for all three length components and for the radius, and the shipped
pipeline calls exactly one of them: the try-form that returns length 1 plus its wear. The radius
accessor and the direction-indexed length accessor are called by nothing in it. Radius compensation
reads the <strong>generic</strong> ledger's radius by a single offset number, so a radius entered here changes
nothing that plays. Length 2 and Length 3 are stored, serialized and handed back unchanged, and no
consumer reads them.</p>
<p>The axis letters in the three length headers are part of the header string and not a binding. Length
1 is the component the height path consumes whatever axis the machine calls it; the other two are
indexed positionally by an accessor nothing calls.</p>
<p>There is no unit column and no conversion — every field is a plain millimetre double, written and
read back as typed. None of the eight numeric cells passes a bound, so a negative value is accepted
and stored, which the sign convention below makes load-bearing rather than incidental.</p>
<h3 id="the-fields-the-row-carries-but-does-not-interpret">The fields the row carries but does not interpret</h3>
<p>A row also holds <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolEdgeOffset.html#Hi_NcParsers_Dependencys_Siemens_SiemensToolEdgeOffset_VerbatimDpFields">VerbatimDpFields</a>, a
bag keyed by <code>$TC_DP</code> index for every index outside the eight above. Nothing interprets those values
and no cell edits them; they exist so that a full controller dump survives a load and a save
unchanged. The panel shows only their count, as a small grey <strong>+N</strong> beside the row's action buttons
with a tooltip explaining that the fields are stored and saved with the project and not used by the
simulation. The row write sends the whole row object including that count, and the endpoint assigns
only the eight doubles, so the bag survives every edit made here.</p>
<p>The two halves stay separate on both sides of serialization. Writing an index into the row routes a
consumed index onto its typed property and every other index into the bag, and the serializer filters
the consumed indices back out of the bag before writing, so an index cannot end up stored twice and
shadow its own typed value on the next load. The element the table writes carries one <code>Edge</code> entry
per key with the eight components as named attributes and the bag as <code>DP</code>-indexed ones, and one
<code>ToolName</code> entry per mapping; the reader accepts both spellings of a component, so a <code>DP</code>-indexed
geometry value lands on the typed property rather than in the bag.</p>
<h2 id="the-sign-convention">The Sign Convention</h2>
<p><strong>On this table an effective value is the geometry plus the wear.</strong> The height the pipeline reads is
length 1 added to length-1 wear, and the radius and per-direction accessors add their wear the same
way. A positive wear therefore lengthens the compensation, and a tool that has worn shorter is
recorded as a <strong>negative</strong> wear value. The panel states the addition twice: the description above the
table ends <em>&quot;Wear adds onto geometry (enter a shortened tool as negative wear).&quot;</em>, and the footnote
below it reads <em>&quot;All values in mm. Effective length 1 = Length 1 (Z) + Length 1 Wear; same for the
other columns.&quot;</em></p>
<div class="WARNING">
<h5>Warning</h5>
<p><strong>The generic ledger one node above uses the opposite convention.</strong> On
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTable.html">ToolOffsetTable</a> an effective value is the ideal <strong>minus</strong>
the wear — <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetRow.html#Hi_NcParsers_Dependencys_Generic_ToolOffsetRow_FullHeight_mm">FullHeight_mm</a> is the ideal height
less the axial wear and <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetRow.html#Hi_NcParsers_Dependencys_Generic_ToolOffsetRow_FullRadius_mm">FullRadius_mm</a> the
ideal radius less the radial wear — and that panel states no sign rule at all. On a Siemens project
both leaves are present at once, so the same wear number typed into the two ledgers moves the tool
in opposite directions. See <a class="xref" href="tool-offsets.html">Tool Offsets</a>, which carries this
warning from the other side.</p>
</div>
<p>The two conventions are not a defect to reconcile: each matches the control whose vocabulary its
table borrows. What makes the pair sharp is that they meet — the fallback below reads a height out of
the subtracting table and uses it where an adding table's height was expected.</p>
<h2 id="how-a-d-word-resolves">How a D Word Resolves</h2>
<p>The Siemens height path consumes a standalone <code>D</code> word from the block and writes the same downstream
state as the ISO <code>G43</code> path: a tool-height compensation section marked with the term <code>D</code>, and a
translation of the effective height composed into the program-to-machine transform chain. Both
brands' paths compose that entry under one source key and replace it in place, so the two are
mutually exclusive on a file that mixes the dialects.</p>
<p>Resolution runs in this order.</p>
<ol>
<li><strong><code>D0</code> cancels.</strong> The section is written with an offset of 0 and an identity transform, with no
table lookup and no diagnostic.</li>
<li><strong>The active tool number comes from the block's tool-change section</strong>, or from the previous
block's when this one carries none. A numeric tool id is taken as it stands.</li>
<li><strong>A string tool id is resolved through the tool-name map</strong> on this same table. An unmapped name
raises an unsupported warning and leaves the tool number at 0.</li>
<li><strong>The <code>(T, D)</code> pair is looked up here.</strong> A configured row answers with length 1 plus its wear.</li>
<li><strong>A pair with no row falls back to the generic ledger</strong>, described next.</li>
</ol>
<p>A block that carries no <code>D</code> word re-resolves the previous block's edge modally, and only while that
previous section is still owned by the <code>D</code> term; a section already claimed by an ISO term on the same
block, or carried modally from one, is left to the ISO path. The modal re-resolve deliberately raises
no diagnostic — the warnings below are attached to blocks that spell a <code>D</code> word out, so a single
missing row reports once instead of on every block that inherits it.</p>
<h3 id="what-a-missing-row-falls-back-to">What a missing row falls back to</h3>
<p>For a <code>(T, D)</code> pair this table has no row for — and equally when no Siemens offset table resolves at
all — the height is taken from the <strong>generic</strong> tool-offset table's height for the tool number,
through the single-integer contract the ISO <code>G43 H</code> path already reads. Every Siemens runner carries
that table, so the fallback always has a source. It exists so that an unfilled per-case <code>$TC_DP</code>
table follows the tool-house-fed ledger instead of resolving 0 and machining the whole program one
tool length low.</p>
<p>Both arms report. A fallback that finds a usable height raises a configuration warning naming the
missing pair and the millimetre value it used instead. A fallback that finds <code>NaN</code> — the sentinel the
generic ledger's tool-house refresh writes for a tool whose tip length does not resolve — raises a
configuration warning of its own and degrades to 0 rather than composing a non-finite translation.
Both carry the code <code>SiemensToolOffset--TcdpRowMissing</code>.</p>
<p>The fallback crosses the sign boundary. What it reads out of the generic table is that table's own
effective height, ideal minus wear; what it substitutes is a <code>$TC_DP</code> height, geometry plus wear. A
project that keeps wear in both places therefore compensates in one direction while the row exists
here and in the other once it does not.</p>
<p>An unresolved tool name reaches the same fallback with the tool number left at 0, and the generic
table's own panel cannot create a row 0 — its tool-number cell takes a minimum of 1 and refuses
anything below it, and its add route mints from 1 upward — so the height resolves to 0.</p>
<h2 id="what-the-tool-name-map-is-for">What the Tool-Name Map Is For</h2>
<p>Siemens programs may name a tool as a string rather than a number, as in the <code>T=&quot;D16R3Z6&quot;</code> form the
panel's own hint quotes. The act chain the simulation builds is keyed by integer tool id, so the
string has to become a number before anything can use it, and this map is the only surface in the
engine that performs that translation. Lookup is case-insensitive: the map is constructed with an
ordinal case-insensitive comparer.</p>
<p>Two consumers read it, and they fail differently.</p>
<ul>
<li><strong>The tool change itself.</strong> The tool-change semantic resolves a string tool id through this map
before it emits a tooling step. An unmapped name raises a <code>ToolChange--NameUnresolved</code> warning and
emits <strong>no step at all</strong>, so the tool change is not simulated rather than simulated with the wrong
tool.</li>
<li><strong>The <code>D</code> word.</strong> The height path resolves the same name for its own <code>(T, D)</code> lookup and, on a
miss, raises <code>SiemensToolOffset--ToolUnresolved</code> and carries on with tool number 0.</li>
</ul>
<p>Both consumers reach the map through the concrete Siemens table rather than through an interface, so
a runner that resolves no such table resolves no tool name either, whatever its brand marker says.</p>
<p>The map's own panel is a two-column table over a name and a tool number. The name is the key and is
rendered as bold plain text; the tool number is a numeric cell with a minimum of 1. Row order is the
server's — the read sorts the keys case-insensitively. The panel never sorts, and its add path
re-reads the whole table rather than appending a row locally.</p>
<p>Its footer is an <strong>Add / Set</strong> pair of fields, and the verb is both: the write is an upsert keyed on
the name. Entering a name that already exists overwrites that mapping's number instead of adding a
second row. Because the underlying dictionary keeps the key it already holds when a value is
assigned, a name entered in different casing updates the existing row and the re-read redisplays the
casing that was stored first.</p>
<h2 id="row-life-cycle">Row Life Cycle</h2>
<p>The two leaves spell adding and deleting differently, and the difference is which side mints the key.</p>
<p><strong>The <code>$TC_DP</code> table's adds are fieldless toolbar and row buttons, because the server mints both
halves of the key.</strong> <strong>Add Tool</strong> in the toolbar sends no tool number and the endpoint takes the
highest tool present plus one — or 1 on an empty table — with edge 1. The per-row button, tooltipped
<em>&quot;Add a cutting edge for T{tool}&quot;</em>, sends that row's tool number and the endpoint takes that tool's
highest edge plus one. Neither half of the key fills a gap: a deleted middle tool or edge is stepped
over rather than reused. Either way the panel appends a locally zeroed row for the returned pair and
re-sorts by tool then edge; those zeros mirror the fresh row the server actually inserted rather than
standing in as a placeholder.</p>
<p><strong>The Tool Names table's add is the footer pair described above</strong>, since its key is typed rather than
minted. A blank name is refused before any request with <em>&quot;Tool name must not be empty.&quot;</em>, and a
number that is not a positive integer with <em>&quot;Tool # must be a positive integer.&quot;</em>; a successful write
clears both fields and re-reads the table. The endpoint refuses a blank name a second time.</p>
<p>Deletion is guarded on one leaf and not the other. A <code>$TC_DP</code> row opens a confirmation dialog first —
<strong>Remove tool offset row</strong> over <em>&quot;Remove the offset row for (T{tool}, D{edge})?&quot;</em> — and the row is
filtered out locally once the request resolves; deleting a pair the table no longer holds answers
unsuccessfully with a message naming it. A tool name deletes on the click with no dialog and no undo,
and a name the map does not hold answers unsuccessfully with a message naming it.</p>
<p>Cell edits differ from both. Every numeric cell on the <code>$TC_DP</code> table sends the <strong>whole row</strong> — all
eight components — and rolls back only the one cell it captured on failure. The Tool Names number
cell sends the mapping it belongs to. Neither panel commits a cleared cell: 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 returns on the next remount. The tool-number cell additionally drops a
non-integer silently, while a value below 1 is refused inside the numeric widget itself, which marks
the box and emits nothing.</p>
<h2 id="what-materializes-the-table">What Materializes the Table</h2>
<p>Three of the writes re-wire the runner suit's dependency proxies before resolving the table: the
<code>$TC_DP</code> row write, the <code>$TC_DP</code> add, and the tool-name write. The proxy is a get-or-create
placeholder, so that wiring installs a bare table into the project's per-case list when it holds none,
and the write then lands on a real table instead of answering that one is missing. The reads and the
two deletes do not re-wire.</p>
<p>Neither panel reports a structural change and neither refreshes the shared snapshot, so a table
created by one of those writes does not re-evaluate the flag or regrow the branch. In practice the
guard never fires from these leaves: the suit wires its proxies when it is deserialized and on every
runner assignment, and the nodes are built only after the flag already read true.</p>
<h2 id="when-the-tables-are-absent">When the Tables Are Absent</h2>
<p>Both leaves carry the branch's two empty layers. The shared one renders <em>&quot;No NC runner — load a
project first.&quot;</em> while the snapshot reports no runner. Each panel's own read then gates on whether the
Siemens table resolved, and renders <em>&quot;No Siemens tool-offset table on the active runner.&quot;</em> or <em>&quot;No
Siemens tool-name table on the active runner.&quot;</em> when it did not.</p>
<p>That second layer is not reachable by navigating, because the flag that grows either node is the same
probe the reads answer with. It is a live guard for a table that goes away under a mounted panel. A
failed write answers inside a success envelope, and the sentence it carries depends on the route. The
four that resolve the table through the shared dependency helper — the row upsert, the row delete and
the two tool-name routes — answer with the type name, <code>No SiemensToolOffsetTable on the active runner</code>; the <code>$TC_DP</code> add builds its own envelope instead and answers word for word with the
<code>$TC_DP</code> panel's own absent-table line. Either way the panel shows its localized context followed by
the sentence the server sent.</p>
<p>The table is stored on the project rather than in the runner file. It reaches the pipeline as a
seedless proxy in the Siemens preset's dependency list and resolves to the concrete table on the
project's per-case list, so a controller file saved from the branch root carries none of these rows.
A brand switch away from Siemens removes it along with the two nodes, and a switch back creates an
empty one rather than restoring what was there —
<a class="xref" href="index.html">Program Data 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>Program Data</strong> Group Row
<ul>
<li><strong>Tool Offsets ($TC_DP)</strong> Node Row — <code>equipment/controller/program-data/siemens-tool-offsets</code></li>
<li><strong>Tool Names</strong> Node Row — <code>equipment/controller/program-data/tool-names</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Tool Offsets ($TC_DP)</strong> Panel
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em>; shown instead of everything below</li>
<li>Absent-Table Body — <em>&quot;No Siemens tool-offset table on the active runner.&quot;</em>; shown instead of
the body</li>
<li>Description Caption — <em>&quot;Sinumerik $TC_DP tool offsets, one row per (tool T, cutting edge D). A
<code>D1</code> word in the program reads this table; when a row is missing, playback falls back to the
generic Tool Offsets table and reports a warning. Wear adds onto geometry (enter a shortened
tool as negative wear).&quot;</em>, with <code>D1</code> set in code style</li>
<li>Toolbar — right-aligned
<ul>
<li><strong>Add Tool</strong> Button — primary, add icon</li>
</ul>
</li>
<li>Offset Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Tool T</strong>, <strong>Edge D</strong>, <strong>Length 1 (Z)</strong>, <strong>Length 2 (X)</strong>, <strong>Length 3 (Y)</strong>,
<strong>Radius</strong>, <strong>Length 1 Wear</strong>, <strong>Length 2 Wear</strong>, <strong>Length 3 Wear</strong>, <strong>Radius Wear</strong>, then an
unlabelled action column</li>
<li>Offset Row, one per <code>(tool, edge)</code> pair, ascending by tool then edge
<ul>
<li>Tool Cell — bold plain text, prefixed <code>T</code></li>
<li>Edge Cell — bold plain text, prefixed <code>D</code></li>
<li>Eight Numeric Fields — the four geometry components, then their four wear components; no
bounds and no unit suffix</li>
<li>Add-Cutting-Edge Button — flat, <code>playlist_add</code> icon, tooltipped <em>&quot;Add a cutting edge for
T{tool}&quot;</em></li>
<li>Delete Button — flat, delete icon</li>
<li>Verbatim-Field Badge — a grey <strong>+N</strong>; shown only where the row carries unconsumed <code>$TC_DP</code>
fields, tooltipped that they are stored and saved with the project and not used by the
simulation</li>
</ul>
</li>
</ul>
</li>
<li>Footnote — <em>&quot;All values in mm. Effective length 1 = Length 1 (Z) + Length 1 Wear; same for the
other columns.&quot;</em></li>
</ul>
</li>
<li>Remove-Row Confirmation Dialog — <strong>Remove tool offset row</strong>, <em>&quot;Remove the offset row for (T{tool},
D{edge})?&quot;</em>, with a cancel button</li>
<li><strong>Tool Names</strong> Panel
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em></li>
<li>Absent-Table Body — <em>&quot;No Siemens tool-name table on the active runner.&quot;</em></li>
<li>Hint Caption — <em>&quot;Maps the tool names written in the NC program (<code>T=&quot;D16R3Z6&quot;</code>) to tool numbers.
Names are case-insensitive. An unmapped name cannot mount a tool and its <code>D</code> word resolves no
offset.&quot;</em>, with the call form and the <code>D</code> word set in code style</li>
<li>Name Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Tool Name</strong>, <strong>Tool #</strong>, then an unlabelled action column</li>
<li>Name Row, one per mapping, ordered case-insensitively by name
<ul>
<li>Name Cell — bold plain text</li>
<li>Tool Number Numeric Field — minimum 1</li>
<li>Delete Button — flat, delete icon, no confirmation</li>
</ul>
</li>
</ul>
</li>
<li>Add Row Footer
<ul>
<li><strong>Tool name</strong> Text Field</li>
<li><strong>Tool #</strong> Numeric Field — minimum 1</li>
<li><strong>Add / Set</strong> Button — primary; the only submit path, since neither field submits on Enter</li>
</ul>
</li>
</ul>
</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/SoftNcSiemensToolOffsetsPanel.vue</code> — the <code>$TC_DP</code> panel:
the eight unbounded numeric cells, the whole-row write with its finite-value guard, the two add
buttons and the local append-and-sort, the confirmed delete, and the verbatim-field badge.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcToolNamesPanel.vue</code> — the tool-name panel: the
plain-text key column, the bounded tool-number cell, the unconfirmed delete, and the <strong>Add / Set</strong>
footer with its two client-side refusals and its full re-read.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: the single flag
test that pushes both node ids, their labels and label keys, and the relabel it applies to the
generic offset leaf at the same time.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the typed wrappers: the row and mapping shapes with their
presence flags, the whole-row upsert, the add that returns a minted pair, the two deletes, and the
name-keyed upsert with its URL encoding.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot behind the first empty layer
and behind the tree's flag test.</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,
the minimum rejected inside the widget, and the empty-text-to-null parse both panels drop.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcToolOffsetsPanel.vue</code> — the neighbouring generic
ledger, its subtracting sign rule, and the Siemens caveat it shows from the brand string.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the two node labels, the description and the hint, the ten
column headers, the footnote, the two absent-table lines, the add-strip refusals and the error
contexts.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Tool #</strong> header and the <strong>Add / Set</strong> label.</li>
<li><code>wwwroot-src/src/i18n/en/dialog.ts</code> — the <code>$TC_DP</code> remove-row dialog's title and message, a
different key pair from the generic ledger's.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the snapshot probe behind both nodes, the
ordered reads whose presence flag is the table object itself, the row upsert that assigns the eight
components and leaves the verbatim bag alone, the add that mints a tool or an edge, the two deletes
with their refusals, the name upsert with its blank-name refusal, and the proxy re-wire the three
writes run first.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensToolOffsetTable.cs</code> — the object both leaves edit: the
pair-keyed row dictionary, the case-insensitive name map and its lookup, the additive effective
values, the row type with its typed components and verbatim bag, and the serializer that keeps a
consumed index out of the bag.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/ISiemensToolOffsetConfig.cs</code> — the two-key offset contract:
the try-form that is the only miss signal, and the direction-indexed and radius accessors nothing
calls.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensToolOffsetTableProxy.cs</code> — the seedless get-or-create
placeholder the Siemens preset carries, and the bare table it installs into a project holding none.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets, of which only the Siemens list carries
that proxy, and the proxy resolution every read of the table goes through.</li>
<li><code>HiMech/NcParsers/NcRunnerSuit.cs</code> — the per-case list this table is stored in, and the proxy wiring
the three writes re-run.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Siemens/SiemensToolOffsetSyntax.cs</code> — the <code>D</code> path: the cancel arm,
the modal ownership rule, the tool-number resolution through the name map, the fallback onto the
generic ledger with its two warnings, and the suppression of both on a modal re-resolve.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/ToolHeightOffsetSyntax.cs</code> — the ISO sibling, and the shared
composition of the height as one replaceable entry of the transform chain.</li>
<li><code>HiMech/NcParsers/Semantics/ToolChangeSemantic.cs</code> — the other consumer of the name map: the string
tool id it resolves, and the tooling step it declines to emit when the name is unmapped.</li>
<li><code>HiMech/NcParsers/PostLogicSyntaxs/RadiusCompensationSyntax.cs</code> — the radius side, which reads the
generic single-integer table rather than this one.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTable.cs</code> — the fallback source: the subtracting
effective values, and the zero it answers for an offset number it does not hold.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTableProxy.cs</code> — the placeholder every brand preset
carries, which is why the fallback always resolves a table on Siemens.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Program Data Plane</a> — the plane both leaves sit on, where this table is stored, and
what a brand switch does to it</li>
<li><a class="xref" href="tool-offsets.html">Tool Offsets</a> — the generic ledger one node above, whose label this table's
presence changes and whose wear convention is the opposite of the one stated 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,40 @@
<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="work-coordinates.html" name="" title="Work Coordinates">Work Coordinates</a>
</li>
<li>
<a href="tool-offsets.html" name="" title="Tool Offsets">Tool Offsets</a>
</li>
<li>
<a href="siemens-tool-offsets.html" name="" title="Siemens Tool Offsets">Siemens Tool Offsets</a>
</li>
<li>
<a href="datum-tables.html" name="" title="Datum Tables">Datum Tables</a>
</li>
<li>
<a href="frames.html" name="" title="Frames">Frames</a>
</li>
<li>
<a href="persistent-variables.html" name="" title="Persistent Variables">Persistent Variables</a>
</li>
</ul>
</div>
</div>
</div>
</div>
@@ -0,0 +1,2 @@
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"Work Coordinates","href":"work-coordinates.html","topicHref":"work-coordinates.html"},{"name":"Tool Offsets","href":"tool-offsets.html","topicHref":"tool-offsets.html"},{"name":"Siemens Tool Offsets","href":"siemens-tool-offsets.html","topicHref":"siemens-tool-offsets.html"},{"name":"Datum Tables","href":"datum-tables.html","topicHref":"datum-tables.html"},{"name":"Frames","href":"frames.html","topicHref":"frames.html"},{"name":"Persistent Variables","href":"persistent-variables.html","topicHref":"persistent-variables.html"}]}
@@ -0,0 +1,498 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tool Offsets | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Tool Offsets | 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-ControllerToolOffsets">
<h1 id="tool-offsets">Tool Offsets</h1>
<p>Tool Offsets is the Controller branch's brand-neutral offset ledger — one row per integer offset
number, each carrying a height and a radius split into an ideal (geometry) component and a wear
component. It lives on the General Setup page at <code>/general-setup</code> under the Control-Tree id
<code>equipment/controller/program-data/tool-offsets</code>, and that one id is the whole of what this page
owns. The leaf is ungated: every brand preset proxies the table behind it, so the node is grown
wherever a runner resolves — <a class="xref" href="../brand-matrix.html">Brand Matrix</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>
<p>The panel follows the branch's shared rules — one read on mount, per-control commit, the
capture-assign-await-restore write, the two empty layers, and a dense markup table with no sort and
no pagination. Those rules, and the places this panel departs from them, are
<a class="xref" href="../editing-contract.html">Editing Contract</a>. What is this leaf's own is everything
below.</p>
<h2 id="one-node-two-labels">One Node, Two Labels</h2>
<p>The node id never changes, and the tree label does. The branch builder pushes this leaf
unconditionally, then chooses its label from the snapshot's Siemens tool-offset flag:</p>
<table>
<thead>
<tr>
<th>Condition</th>
<th>Label the tree row reads</th>
</tr>
</thead>
<tbody>
<tr>
<td>No <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a> resolves</td>
<td><strong>Tool Offsets</strong></td>
</tr>
<tr>
<td>One does</td>
<td><strong>Tool Offsets (ISO G43 H)</strong></td>
</tr>
</tbody>
</table>
<p>Among the shipped presets only Siemens carries that table, so only Siemens sees the second label —
and it sees it beside a second offset leaf, <strong>Tool Offsets ($TC_DP)</strong>, grown from the same flag. The
qualifier exists so that the two ledgers do not read as one row of the tree; the id under it is
<code>equipment/controller/program-data/tool-offsets</code> either way, so an existing deep link is unaffected
by which label is showing. Which flag grows which leaf, and what that flag probes, is
<a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</p>
<p>A second, independent Siemens signal appears <strong>inside</strong> the panel. While the runner's brand marker
reads <code>Siemens</code>, an orange caption sits above the toolbar:</p>
<blockquote>
<p><em>&quot;On Siemens this table serves the ISO <code>G43 H</code> path only — it is not the Sinumerik tool-offset
ledger. <code>D</code> tool calls read the Tool Offsets ($TC_DP) table; when a (T, D) row is missing there,
playback falls back to this table's height for the tool number and reports a warning.&quot;</em></p>
</blockquote>
<p>The label is chosen from the table flag and the caption from the brand string. Those are computed
from different things and can disagree — one of the departures catalogued in
<a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="what-a-row-holds">What a Row Holds</h2>
<p>The table is the Fanuc Memory C layout: the H number and the D number address the same row, so one
row carries both the length and the radius of one offset.</p>
<table>
<thead>
<tr>
<th>Column</th>
<th>Stores</th>
<th>Read by</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Tool #</strong></td>
<td>the offset number, the row's key</td>
<td>the H word, the D word, or the Heidenhain tool number</td>
</tr>
<tr>
<td><strong>Ideal Height (mm)</strong></td>
<td>the tool's length before wear</td>
<td>the height side</td>
</tr>
<tr>
<td><strong>Axial Wear (mm)</strong></td>
<td>accumulated length wear</td>
<td>the height side</td>
</tr>
<tr>
<td><strong>Ideal Radius (mm)</strong></td>
<td>the cutter's radius before wear</td>
<td>the radius side</td>
</tr>
<tr>
<td><strong>Radial Wear (mm)</strong></td>
<td>accumulated radius wear</td>
<td>the radius side</td>
</tr>
</tbody>
</table>
<p>There is no unit column and no unit conversion:
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetRow.html">ToolOffsetRow</a> stores four plain millimetre doubles, and the
panel writes and reads them unchanged. No cell is bounded except the key, which takes a minimum of</p>
<ol>
<li>The panel renders no description caption above its table: the Siemens caveat is the only line it
shows there, and the only other prose it carries is the closing caption the tool-house dependence
turns on below the table.</li>
</ol>
<p>The key column is the branch's one renameable key, and the rename is guarded on both sides of the
wire: the panel refuses a value already present in its own rows with the toast <em>&quot;Tool id {id}
already exists.&quot;</em> and re-reads the table, and the endpoint refuses the same case with a message
naming the existing id. Below 1 never reaches either — the numeric widget rejects the bound itself,
marks the box and emits nothing. A non-integer that the widget does emit is dropped by the handler
with no request and no message at all.</p>
<h2 id="the-sign-convention">The Sign Convention</h2>
<p><strong>On this table an effective value is the ideal minus the wear.</strong>
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetRow.html#Hi_NcParsers_Dependencys_Generic_ToolOffsetRow_FullHeight_mm">FullHeight_mm</a> is the ideal height less the
axial wear and <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetRow.html#Hi_NcParsers_Dependencys_Generic_ToolOffsetRow_FullRadius_mm">FullRadius_mm</a> the ideal radius
less the radial wear, and those two are exactly what
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IToolOffsetConfig.html">IToolOffsetConfig</a> hands every consumer. A positive wear therefore
shortens the compensation and a negative wear lengthens it. Nothing in the panel states this: the
four column headers name the components and no footnote names the operation, which is why the rule
belongs here. An offset number the table holds no row for is not an error either: both getters
answer 0 for it, and neither the ISO height path nor the radius side reports the miss — a mistyped H
is a zero-length compensation that plays to the end of the program without a warning.</p>
<div class="WARNING">
<h5>Warning</h5>
<p><strong>The neighbouring Siemens ledger uses the opposite convention.</strong> On
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensToolOffsetTable.html">SiemensToolOffsetTable</a> an effective value is the geometry
<strong>plus</strong> the wear, and that panel prints the addition in a footnote under its own table. On a
Siemens project both leaves are present at once, so one wear number entered in the two ledgers
moves the tool in opposite directions. See
<a class="xref" href="siemens-tool-offsets.html">Siemens Tool Offsets</a>.</p>
</div>
<h3 id="how-the-height-reaches-the-machine">How the height reaches the machine</h3>
<p>The height side is consumed as a translation, not as a property of the tool. On a G43 block the ISO
tool-height syntax reads the effective height for the H number and composes it into the
program-to-machine transform chain as a translation of that many millimetres along the current tool
orientation; G44 negates the same number and G49 writes zero. The G43.4 RTCP path reads the same
number for the same H word and re-aims it through the machine kinematics. On Heidenhain there is no
cancel word: a TOOL CALL block reads the effective height for the <strong>tool number</strong> and adds the
block's <code>DL</code> delta to it, and the result composes into the same chain entry.</p>
<p>Nothing on that path reads the tool the project actually carries. The modelled tool assembly's own
spindle-buckle-to-tool-tip length is what decides where its tip sits once the machine has moved by
the compensation, so the stored ideal height and the modelled tool's length are two independent
numbers that have to agree for the programmed contour to land on the workpiece. Making them agree is
the entire purpose of the tool-house dependence below, which writes
<a class="xref" href="../../../../api/Hi.Machining.IMachiningTool.html#Hi_Machining_IMachiningTool_SpindleBuckleToToolTipLength">SpindleBuckleToToolTipLength</a> straight into the ideal column.</p>
<h3 id="how-the-radius-reaches-the-path">How the radius reaches the path</h3>
<p>The radius side is read by the G41/G42 radius-compensation syntax against the D number, and it is
signed rather than absolute. A negative effective radius — a radial wear larger than the ideal
radius — is accepted, and it offsets the path to the opposite of the programmed side. On the first
block of a compensation move the parser raises a validation warning for that case on Heidenhain
only, mirroring the look-ahead check a TNC control performs; the other brands take it silently.</p>
<h3 id="the-macro-variable-window">The macro-variable window</h3>
<p>On the Fanuc, Mazak and Syntec syntax lists a variable lookup maps <code>#2001</code> through <code>#2200</code> onto the
effective <strong>height</strong> of offsets 1 through 200, so a macro program can read the same subtraction the
G43 path applies. It is a read: the lookup exposes no writer, and neither the Siemens nor the
Heidenhain syntax list registers it.</p>
<h3 id="where-a-blank-cell-comes-from">Where a blank cell comes from</h3>
<p>A cleared numeric cell is not a stored value — the panel drops a null or a non-finite edit before
sending anything, so clearing a cell leaves the number on the server and the box repopulates on the
next remount. A blank cell that arrives <strong>from</strong> the server is a different thing: it is <code>NaN</code>, which
the numeric widget renders as empty text. The tool-house refresh is what writes one, for a tool
whose tip length does not resolve. Only the Siemens <code>$TC_DP</code> fallback tests for it — a NaN height
there is reported as a configuration warning and degrades to zero — while the ISO G43 path composes
whatever the table returned.</p>
<h2 id="tool-house-dependence">Tool-House Dependence</h2>
<p>Above the table sits the toggle <strong>Set ideal offset dependent on tool house</strong>. It is the one setting
this leaf edits that is not a dependency at all: it writes
<a class="xref" href="../../../../api/Hi.MachiningProcs.MachiningActRunnerConfig.html#Hi_MachiningProcs_MachiningActRunnerConfig_IsIdealOffsetDependentOnToolHouse">IsIdealOffsetDependentOnToolHouse</a>, a project-level
element serialized beside the runner suit, so it is untouched by a runner install and by the sweep
that follows a brand switch. Its stored default is on; the reader answers <em>off</em> when no project is
loaded, which is a fallback rather than the model's own default.</p>
<p>With the dependence on, the panel hands three things over:</p>
<ul>
<li>The two <strong>ideal</strong> columns become read-only, and a closing caption says so: <em>&quot;Ideal columns mirror
the Tool House; only the wear columns are editable.&quot;</em> Read-only is a screen guarantee only — the
write is row-scoped, so the ideal values ride along on every wear edit, and the endpoint writes
all four components unconditionally. That the two ideal cells still commit on Enter is one of the
panel's catalogued departures from <a class="xref" href="../editing-contract.html">Editing Contract</a>.</li>
<li>The <strong>Tool #</strong> cell is replaced by bold plain text, so the key cannot be renamed.</li>
<li>Row CRUD disappears: the <strong>Add</strong> button is not rendered and each row's delete button becomes a
grey em dash. A <strong>Refresh from Tool House</strong> button appears next to the toggle in their place.</li>
</ul>
<h3 id="the-table-does-not-otherwise-track-the-tool-library">The table does not otherwise track the tool library</h3>
<p>Offset number and tool id are independent integers, and with the dependence off nothing reconciles
them. A row may name a number the project's tool library has never held, and the library may hold
tools with no row; neither state is reported anywhere on this panel, and both are stored and played
as written. The library itself is edited on <a class="xref" href="../../../tool-house/index.html">Tool House Page</a>.</p>
<p>The refresh is what imposes a one-to-one mapping, and it does so in both directions.</p>
<h3 id="the-refresh-is-destructive-and-it-is-not-only-a-button">The refresh is destructive, and it is not only a button</h3>
<p>One endpoint backs both the button and the toggle, and it runs three steps. For every tool in the
project's library it writes the ideal height from that tool's spindle-buckle-to-tool-tip length —
<code>NaN</code> where the length does not resolve — and, where the cutter is a milling cutter, the ideal
radius from the largest radius on its cutter profile; a row is created for a library tool that had
none. <strong>It then deletes every row whose number is not in the library.</strong> The wear columns are never
touched.</p>
<p>The engine carries the same three steps of its own, as
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTable.html#Hi_NcParsers_Dependencys_Generic_ToolOffsetTable_UpdateIdealByToolHouse_">UpdateIdealByToolHouse</a><small>(API)</small>,
and that copy is what playback calls. The two agree step for step and part company only on a missing
library: the engine method returns without doing anything, while the endpoint answers unsuccessfully
with <em>&ldquo;No tool house available&rdquo;</em>.</p>
<p>Three consequences deserve stating plainly.</p>
<ul>
<li><strong>There is no confirmation.</strong> The dialog that guards a single delete button does not guard this,
and there is no undo.</li>
<li><strong>Turning the toggle on runs it immediately.</strong> Enabling the dependence is two server calls in one
attempt: the flag write, then the refresh. A project with no tool library fails the second with
<em>&ldquo;No tool house available&rdquo;</em> after the first has already committed, and the panel's local toggle
springs back while the server-side flag stays on — the two-call rollback recorded in
<a class="xref" href="../editing-contract.html">Editing Contract</a>. Turning the toggle off writes the flag
and stops there — no refresh, and no re-read.</li>
<li><strong>Playback runs it too.</strong> While the flag is on, starting an NC program refreshes the table from
the library before the first block, so a hand-typed ideal value is replaced whether or not the
panel was ever opened, and an empty library at that moment empties the table. No panel is told:
what is on screen is whatever the last read returned until the selection moves away and back.</li>
</ul>
<h2 id="row-life-cycle">Row Life Cycle</h2>
<p>The add and the delete are the two flows the branch spells one way here and another way elsewhere.</p>
<p><strong>Add</strong> is a fieldless toolbar button, because the server mints the key: the endpoint takes the
highest existing number plus one — or 1 on an empty table — inserts an all-zero row and returns the
number, and the panel appends that row locally rather than re-reading. All four zeros are the
server's actual new-row values, not a screen placeholder.</p>
<p><strong>Delete</strong> opens a confirmation dialog first, titled <strong>Remove tool offset</strong> over <em>&quot;Remove the offset
row for tool #{id}?&quot;</em>, and filters the row out locally once the request resolves. Deleting a number
the table no longer holds answers unsuccessfully with a message naming it.</p>
<p><strong>Rename</strong> is optimistic and re-sorts the rows ascending once the write returns; a failure restores
the previous number. Every other cell is a per-cell handler that sends the whole row.</p>
<h2 id="when-the-table-is-absent">When the Table Is Absent</h2>
<p>The leaf carries both of the branch's empty layers. The shared one renders <em>&quot;No NC runner — load a
project first.&quot;</em> while the snapshot reports no runner. The panel's own read then gates on whether a
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTable.html">ToolOffsetTable</a> resolved, and renders <em>&quot;No tool-offset table
on the active runner.&quot;</em> when it did not — which hides the dependence toggle along with the table,
since the whole body sits behind that guard. A failed write answers inside a success envelope. The
row write, the delete, the rename and the refresh all go through the branch's shared dependency
helper and carry <code>No ToolOffsetTable on the active runner</code>, which the panel shows as its own context
followed by that sentence. <strong>Add</strong> does not use that helper: it tests the table itself and answers
<code>No tool-offset table on the active runner</code> — the panel's own absent-table line, less its full stop.
The dependence write reaches neither string, because what it writes is a project-level element
rather than the table; it is the one route on this leaf that answers a coded payload instead, and it
reports no project loaded.</p>
<p>That second layer is hard to reach. <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.ToolOffsetTableProxy.html">ToolOffsetTableProxy</a> is
a seedless get-or-create placeholder: on wiring it installs a bare table into the project's per-case
list whenever the project holds none, and the suit wires its proxies when it is deserialized and
again on every runner assignment.</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>Program Data</strong> Group Row
<ul>
<li><strong>Tool Offsets</strong> Node Row — <code>equipment/controller/program-data/tool-offsets</code>; reads
<strong>Tool Offsets (ISO G43 H)</strong> while a Siemens <code>$TC_DP</code> table resolves</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Tool Offsets</strong> Panel
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em>; shown instead of everything below</li>
<li>Absent-Table Body — <em>&quot;No tool-offset table on the active runner.&quot;</em>; shown instead of the body</li>
<li>Siemens Caveat Line — orange caption, shown while the brand marker reads <code>Siemens</code>, with
<code>G43 H</code> and <code>D</code> set in code style</li>
<li>Toolbar
<ul>
<li><strong>Set ideal offset dependent on tool house</strong> Toggle</li>
<li><strong>Refresh from Tool House</strong> Button — flat, refresh icon; only while the dependence is on</li>
<li><strong>Add</strong> Button — primary, add icon, right-aligned; only while the dependence is off</li>
</ul>
</li>
<li>Offset Table — dense, flat, bordered
<ul>
<li>Header Row — <strong>Tool #</strong>, <strong>Ideal Height (mm)</strong>, <strong>Axial Wear (mm)</strong>, <strong>Ideal Radius (mm)</strong>,
<strong>Radial Wear (mm)</strong>, then an unlabelled action column</li>
<li>Offset Row, one per offset number, ascending
<ul>
<li>Tool Number Cell — a numeric field with a minimum of 1, or bold plain text while the
dependence is on</li>
<li>Ideal Height, Axial Wear, Ideal Radius and Radial Wear Numeric Fields — the two ideal ones
read-only while the dependence is on</li>
<li>Delete Button — or a grey em dash while the dependence is on</li>
</ul>
</li>
</ul>
</li>
<li>Dependent Footnote — <em>&quot;Ideal columns mirror the Tool House; only the wear columns are
editable.&quot;</em>; only while the dependence is on</li>
</ul>
</li>
<li>Remove-Row Confirmation Dialog — <strong>Remove tool offset</strong>, <em>&quot;Remove the offset row for tool
#{id}?&quot;</em>, with a cancel button</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/SoftNcToolOffsetsPanel.vue</code> — the panel: the dependence
toggle and its two-call enable, the refresh button, the read-only ideal columns, the renameable
key with its duplicate guard, the confirmed delete, the locally appended add, and the Siemens
caption gated on the brand string.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: this leaf's id,
its two labels and label keys, and the snapshot flag that switches between them.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the typed wrappers: the row shape with its presence and
dependence flags, the whole-row write, the add that returns a minted number, the rename, the
delete, and the refresh call.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the numeric cell: commit on blur or Enter,
the minimum rejected inside the widget, the empty-text-to-null parse the panel drops, and the
<code>NaN</code> that renders as an empty box.</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/composables/useSoftNcRunner.ts</code> — the shared snapshot behind that first guard and
behind the tree's choice of label.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcSiemensToolOffsetsPanel.vue</code> — the neighbouring
ledger on Siemens, and the footnote in which it states the opposite sign rule.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the two node labels, the Siemens caveat, the four column
headers, the dependent footnote, the duplicate-key toast, the absent-table line and the panel's
error contexts.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Tool #</strong>, <strong>Add</strong>, toggle and refresh labels.</li>
<li><code>wwwroot-src/src/i18n/en/dialog.ts</code> — the remove-row dialog's title and message.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the reader with its presence and dependence
flags, the whole-row write, the add that mints the next number, the delete and the rename with
their refusals, the project-level dependence write, and the refresh with its drop of rows the
library no longer holds.</li>
<li><code>Common/ApiError.cs</code> — the coded no-project payload the dependence write answers with, the one
answer on this leaf that can be re-rendered in the application locale.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTable.cs</code> — the ledger itself: the four stored
components, the subtraction that makes an effective value ideal minus wear, the integer-keyed row
dictionary, and the tool-house update with its row creation and its row removal.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/ToolOffsetTableProxy.cs</code> — the seedless get-or-create
placeholder every brand preset carries in place of the table, and the bare instance it installs
into a project holding none.</li>
<li><code>HiMech/NcParsers/Dependencys/IToolOffsetConfig.cs</code> — the single-integer offset contract the
consumers read through, and the pointer to the Siemens tool-and-edge contract beside it.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensToolOffsetTable.cs</code> — the neighbouring ledger, and
the addition that makes its effective values geometry plus wear.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets, each of which carries the tool-offset
proxy, and the proxy resolution every read of the table goes through.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/ToolHeightOffsetSyntax.cs</code> — the ISO height path: the H word, the
G43 / G44 / G49 arms, and the translation along the tool orientation both brand siblings compose
through.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/G43p4RtcpSyntax.cs</code> — the RTCP path that reads the same height for
the same H word and re-aims it through the kinematics.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainToolOffsetSyntax.cs</code> — the TOOL CALL path: the
tool number as the offset id, the <code>DL</code> delta added to the table height, and the absence of a
cancel word.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Siemens/SiemensToolOffsetSyntax.cs</code> — the <code>D</code> path's fallback onto
this table when the <code>$TC_DP</code> map has no row, its warning, and the <code>NaN</code> case it degrades to zero.</li>
<li><code>HiMech/NcParsers/PostLogicSyntaxs/RadiusCompensationSyntax.cs</code> — the radius side: the D word, the
signed offset that flips the compensated side when it goes negative, and the Heidenhain-only
warning for that case.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucToolOffsetVariableLookup.cs</code> — the read-only macro window
onto the effective heights, and the address range it answers for.</li>
<li><code>HiMech/NcParsers/Syntaxs/FanucSyntaxUtil.cs</code>,
<code>HiMech/NcParsers/Syntaxs/MazakSyntaxUtil.cs</code> and
<code>HiMech/NcParsers/Syntaxs/SyntecSyntaxUtil.cs</code> — the three syntax lists that register that window.</li>
<li><code>HiMech/MachiningProcs/MachiningActRunnerConfig.cs</code> — the tool-house dependence flag, its stored
default and the element the project serializes it as.</li>
<li><code>HiMech/MachiningProcs/MachiningSession.cs</code> — the play loop's refresh of the table from the
library before the first block, run whenever the flag is on.</li>
<li><code>HiMech/Machining/MachiningToolHouse.cs</code> — the tool-id-keyed library the refresh walks.</li>
<li><code>HiMech/Machining/IMachiningTool.cs</code> — the tool contract the library's entries answer to, and
where the spindle-buckle-to-tool-tip length the refresh reads is declared.</li>
<li><code>HiMech/Milling/MillingTools/MillingTool.cs</code> — the assembly walk behind that length, and the <code>NaN</code>
it answers when the walk does not resolve.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the facade that owns the library and the runner
suit, and the entry point the play-time refresh reaches this table through.</li>
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — where the library, the runner configuration and the
suit hang off the project, and the load path that leaves the library unset when the project file
names none.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Program Data Plane</a> — the plane this leaf sits on, where its table is stored, and
what a brand switch does to the tables beside it</li>
<li><a class="xref" href="siemens-tool-offsets.html">Siemens Tool Offsets</a> — the <code>$TC_DP</code> ledger that appears next to this one on
Siemens, and whose wear convention is the opposite of the one stated above</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,627 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Work Coordinates | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Work Coordinates | 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-ControllerWorkCoordinates">
<h1 id="work-coordinates">Work Coordinates</h1>
<p>Work Coordinates is the Controller branch's G54 ledger: one table of coordinate ids against an X / Y
/ Z machine offset, edited through whichever offset provider the active runner resolves. It lives on
the General Setup page at <code>/general-setup</code> under the Control-Tree id
<code>equipment/controller/program-data/work-coordinates</code>, reached as
<code>?tree=equipment/controller/program-data/work-coordinates</code>, and no snapshot flag gates it — every
runner grows it. The face is the same on all five brands; what sits behind it is a different object
on each, and on two of them that object is also the subject of a node of its own.</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-discriminator">The Discriminator</h2>
<p>The panel does not probe the runner. It reads one field of the shared runner snapshot,
<code>workCoordinateKind</code>, and uses it for exactly one thing: the grey caption above the table. The field
is a type switch over the first <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> in the runner's
proxy-resolved dependency list, and it has six arms.</p>
<table>
<thead>
<tr>
<th>Arm</th>
<th>Provider matched</th>
<th>Brands that reach it</th>
<th>Caption the panel shows</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>fanuc</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Fanuc.FanucParameterTable.html">FanucParameterTable</a></td>
<td>Fanuc, Mazak</td>
<td><em>&quot;Stored in the Fanuc parameter table (#5221+ / #7001+).&quot;</em></td>
</tr>
<tr>
<td><code>syntec</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Syntec.SyntecParameterTable.html">SyntecParameterTable</a></td>
<td>Syntec</td>
<td><em>&quot;Stored in the Syntec parameter table (Pr5221+ / Pr7001+).&quot;</em></td>
</tr>
<tr>
<td><code>siemens</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Siemens.SiemensFrameTable.html">SiemensFrameTable</a></td>
<td>Siemens</td>
<td><em>&quot;Stored as Siemens settable frames ($P_UIFR; G500 cancels and is always zero).&quot;</em></td>
</tr>
<tr>
<td><code>heidenhain</code></td>
<td><a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.HeidenhainDatumTable.html">HeidenhainDatumTable</a></td>
<td>Heidenhain</td>
<td><em>&quot;G54–G59 map onto Heidenhain datum preset rows 1–6.&quot;</em></td>
</tr>
<tr>
<td><code>generic</code></td>
<td>any other implementer, i.e. <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.Generic.IsoCoordinateTable.html">IsoCoordinateTable</a></td>
<td>none</td>
<td><em>&quot;Brand-neutral work-coordinate table.&quot;</em></td>
</tr>
<tr>
<td><code>none</code></td>
<td>nothing resolved</td>
<td>none</td>
<td><em>&quot;Brand-neutral work-coordinate table.&quot;</em></td>
</tr>
</tbody>
</table>
<p>Two arms answer for no shipped preset, and they answer alike because the caption's switch falls
through to one default for both.</p>
<ul>
<li><strong><code>generic</code></strong> needs the brand-neutral table to be the FIRST provider the runner resolves. The
Fanuc, Mazak and Syntec presets do carry that table — seeded with the nine <code>G59.x</code> ids — but
always behind their brand table, so the arm is reachable only from a runner composed by hand or
deserialized from a file that put the brand-neutral table first. Which brand carries which
dependency is <a class="xref" href="../brand-matrix.html">Brand Matrix</a>.</li>
<li><strong><code>none</code></strong> has two producers, and a different guard stops each of them. The snapshot's early
return leaves the field's default in place when no runner resolves at all — the same string the
client's empty snapshot carries — and the shared no-runner guard then replaces the whole body. The
type switch's own null arm answers <code>none</code> for a runner that does resolve but carries no offset
provider; that state reports a runner, so what replaces the body is the absent-table guard
instead, because the lookup that answered <code>none</code> is the one the panel's own read reports absent.
Neither path reaches the caption.</li>
</ul>
<p>The brand marker plays no part here. <code>workCoordinateKind</code> is derived from the resolved object's
type, while the badge on the branch root reads a free-form string, so a marker edited to say
something else changes the badge and leaves this caption exactly where it was.</p>
<h2 id="the-row-inventory">The Row Inventory</h2>
<p>Every row is one id from the provider's own id enumeration, and each provider enumerates a different
set. That is the whole reason the row counts differ: the endpoint asks the resolved provider what ids
it has and fills each row from its offset accessor, substituting a zero triad for a null answer.</p>
<table>
<thead>
<tr>
<th>Brand</th>
<th>Provider</th>
<th>Ids enumerated</th>
<th>Always visible</th>
<th>Hidden while all-zero</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc, Mazak</td>
<td>Fanuc parameter table, then the extended table</td>
<td>G54–G59, G54.1P1–G54.1P48; G59.1–G59.9</td>
<td>the six G5x rows and the nine <code>G59.x</code> rows</td>
<td>the 48 <code>G54.1P</code> rows</td>
</tr>
<tr>
<td>Syntec</td>
<td>Syntec parameter table, then the extended table</td>
<td>G54–G59, G54.1P1–G54.1P48; G59.1–G59.9</td>
<td>the six G5x rows and the nine <code>G59.x</code> rows</td>
<td>the 48 <code>G54.1P</code> rows</td>
</tr>
<tr>
<td>Siemens</td>
<td>Siemens frame table</td>
<td>G54–G57, G505–G599</td>
<td>the four G5x rows</td>
<td>the 95 <code>G5xx</code> rows</td>
</tr>
<tr>
<td>Heidenhain</td>
<td>Heidenhain datum table</td>
<td>G54–G59</td>
<td>all six</td>
<td>none</td>
</tr>
<tr>
<td>—</td>
<td>brand-neutral table (standalone)</td>
<td>G54–G59, G59.1–G59.9</td>
<td>all fifteen</td>
<td>none</td>
</tr>
</tbody>
</table>
<p>The <em>extended table</em> on the three ISO presets is the brand-neutral table seeded with the nine
<code>G59.x</code> ids only; a standalone brand-neutral table (a runner built without a brand table) seeds all
fifteen.</p>
<p>Three consequences of that column are worth reading twice.</p>
<ul>
<li><strong>Siemens seeds no G58 and no G59 row.</strong> The frame table's constructor seeds G54, G55, G56 and G57
plus the extended G505–G599 series, and nothing else, and no panel on the branch can add an id to
the set. A <code>G58</code> in a Siemens program resolves through the same ISO path as on any other brand,
finds no frame, and falls back to a zero offset.</li>
<li><strong>Fanuc and Syntec enumerate an id only while at least one of its three parameter addresses is
present.</strong> <a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IsoCoordinateAddressMap.html">IsoCoordinateAddressMap</a> maps G54–G59 onto <code>#5221</code> with a
stride of 20 and G54.1 P1–P48 onto <code>#7001</code> with the same stride, three consecutive addresses per
entry, and both brand tables seed all 162 of them with zero at construction. So all 54 rows exist
from the first read; the enumeration rule bites only a table whose addresses have been removed.
<a class="xref" href="../machine/native-parameters.html">Native Parameters</a> is the surface that removes one — its
row delete drops a system-parameter address outright, and dropping all three of an entry's
addresses removes that row from this leaf. The same form is where those addresses are visible under
their own numbers, each annotated with the component it holds.</li>
<li><strong>Heidenhain shows six ids for a twenty-row table.</strong> The datum table seeds preset rows 1–20 and
shift rows 1–20, and its ISO face aliases only preset rows 1–6 onto G54–G59. Rows 7–20, and every
shift row, have no id here at all.</li>
</ul>
<p><code>G500</code> is a row on no brand. The frame table treats it as the cancel frame: reads answer a zero
offset, a write through the coordinate accessor is dropped, and it is deliberately not stored in the
frame dictionary, so it never reaches the id enumeration — which is why the Siemens caption names a
code the table below it never shows.</p>
<h2 id="two-nodes-one-table">Two Nodes, One Table</h2>
<div class="WARNING">
<h5>Warning</h5>
<p>On Siemens and on Heidenhain this leaf is a second face on an object that already has a node of
its own. The Siemens frame table is edited here <em>and</em> under <strong>Frames (Siemens)</strong>; the Heidenhain
datum preset table is edited here <em>and</em> under <strong>Datum Presets (Q339)</strong>. These are not two copies
kept in step — they are one instance, and a write through either node lands in the same cell.</p>
</div>
<p>The aliasing is exact, and its two halves are addressed differently.</p>
<ul>
<li><strong>Siemens.</strong> This leaf reads and writes through the ISO interface, so the payload is keyed by the
G-code id. <a class="xref" href="frames.html">Frames</a> reads and writes the frame dictionary directly,
keyed by the same string. Both panels render the same id set with the same values, and a <code>G54</code> edit
made on either is the same assignment. The two routes part on one id only: this leaf's write goes
through the coordinate accessor and drops a <code>G500</code>, where the frame route would assign it into the
dictionary — an id neither panel ever sends. Nor are the three value columns the whole of what the
frame table holds: they are the X / Y / Z translation, while the translation components for other
axis letters live in a second dictionary that only the <code>$P_UIFR</code> bridge writes and neither panel
exposes. What does differ between the two panels is furniture — this leaf carries the <strong>Actions</strong>
column with P0 and M0, and Frames has no counterpart for it.</li>
<li><strong>Heidenhain.</strong> This leaf's <code>G54</code>…<code>G59</code> rows are preset rows 1–6.
<a class="xref" href="datum-tables.html">Datum Tables</a> shows the same six cells under the <strong>Q339</strong>
column, alongside rows 7–20 and the whole datum-shift table, which this leaf cannot reach. So the
aliasing is one-directional in coverage: everything on this leaf is on that one, and most of that
one is not here.</li>
</ul>
<p>Nothing in the branch reconciles the two views while both are in scope, and nothing needs to: the
editor row mounts one panel at a time and each panel fetches once on mount, so moving the selection
from one node to the other is itself the refresh. What that costs, and what a mounted panel therefore
never sees, is <a class="xref" href="../editing-contract.html">Editing Contract</a>.</p>
<h2 id="which-rows-show">Which Rows Show</h2>
<p>A row is always visible when its id reads <code>G5</code>, one digit in 4–9, and optionally a dot and one more
digit. Every other row is hidden unless one of its three values is non-zero, or the <strong>Show all</strong>
toggle is on. That rule is the panel's own regular expression, evaluated in the browser over the rows
the read returned; nothing about it reaches the server.</p>
<p>The toggle itself is conditional. It renders only while at least one row fails the always-visible
test, so it is present on Fanuc, Mazak, Syntec and Siemens and absent on Heidenhain — and absent on
the brand-neutral table too, whose fifteen ids all pass. The Frames leaf carries the same toggle
unconditionally and applies a narrower always-visible test of its own, <code>G54</code> through <code>G57</code>; on the
Siemens table the two tests keep the same four rows visible, so only the toggle's presence differs.</p>
<h2 id="editing-a-row">Editing a Row</h2>
<p>Every value cell is the shared numeric field, so it commits on blur or on Enter and never per
keystroke — its parsing, its bounds behaviour and the double commit that follows Enter are
<a class="xref" href="../../../widget/numeric-input.html">Numeric Input</a>. No cell here passes a minimum or a maximum, and the id
column is plain bold text that cannot be edited.</p>
<p><strong>The write is the whole row.</strong> A commit assigns the new number into the local row, then sends that
row's <code>x</code>, <code>y</code> and <code>z</code> as they now stand to the row's id. A failure restores the one component the
handler captured and raises the branch's standard toast; the request that failed carried all three. A
cleared cell parses to null, and a cell holding <code>NaN</code> or an infinity parses to a non-finite number;
the handler returns before the request in both cases, so neither empties a stored offset. The cell
keeps showing what the field made of the text — blank for a cleared cell and for <code>NaN</code>, the literal
<code>Infinity</code> or <code>-Infinity</code> for an infinity — until the panel is remounted.</p>
<p>Where that row lands differs by provider, and this is the point at which the uniform face ends.</p>
<ul>
<li>Fanuc, Mazak and Syntec write three consecutive parameter addresses, all three unconditionally.</li>
<li>Siemens replaces the frame entry for that id.</li>
<li>Heidenhain replaces the preset row the id aliases onto.</li>
</ul>
<p>The Fanuc-family and Heidenhain writes silently drop an id they do not map: the Fanuc-family write
resolves the address first and does nothing without one, and the Heidenhain write matches the id
against its two recognised forms. The Siemens frame table and the brand-neutral table do the
opposite — an id they have never held is stored as a new row, the frame table refusing only <code>G500</code>.
Neither behaviour is reachable from this leaf, because the panel only ever sends an id the same
provider enumerated.</p>
<h3 id="p0-and-m0">P0 and M0</h3>
<p>Two flat buttons sit in the <strong>Actions</strong> column of every row and write the whole triad in one call.</p>
<ul>
<li><strong>M0</strong> sets the row to machine zero.</li>
<li><strong>P0</strong> sets it to the machine coordinate at which the workpiece's program-zero anchor sits with
every dynamic axis of the equipment assembly — not only the machining chain's — stepped to zero,
falling back to the machining chain's table buckle when the workpiece declares no program-zero
anchor.</li>
</ul>
<p>Both re-read the whole table on success rather than patching the local row, so a value the provider
stored differently from what was sent is picked up at once. Neither is confirmed. P0 carries a
failure of its own that no other write in the branch produces: with no chain resolving a tool buckle
the position is null, and the endpoint answers unsuccessfully with <em>&ldquo;Could not get the machine
position at program zero&rdquo;</em> before it ever reaches the offset provider.</p>
<h2 id="the-canvas-marker">The Canvas Marker</h2>
<p>Clicking a row also chooses which coordinate the General Setup canvas draws its triad on. That is the
one write in this branch addressed to another surface, and it behaves differently from the rest in
four ways worth naming.</p>
<ul>
<li><strong>Where it is stored.</strong> The id goes to the equipment-display surface, which assigns it on the
shared user configuration and schedules a debounced save of that file. It is device state, not
project state: it is not written into the project, it outlives the project that set it, and a
failure of the file write itself is logged rather than reported.</li>
<li><strong>What reads it.</strong> The canvas marker resolves the <em>first</em>
<a class="xref" href="../../../../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> on the active runner — the same object this
panel edits — so an offset edited here moves the marker on the next frame. A marked id the provider
does not answer draws nothing at all, which is the state after a brand switch leaves behind an id
the new provider has never heard of. The marker is also suppressed while its Scene flag is off, and
while no machining chain resolves an anchor.</li>
<li><strong>How it fails.</strong> The handler follows the same optimistic shape as a cell commit — assign, await,
restore and toast on failure — but it is the branch's one call through the plain-JSON helper rather
than the envelope helper, so only a non-2xx status throws there. On mount the panel seeds its
highlight from the same surface and swallows any failure, so an unhighlighted table is not evidence
that nothing is marked.</li>
<li><strong>What triggers it.</strong> The click handler is bound on the whole row and nothing inside the row stops
propagation, so editing a cell or pressing P0 or M0 marks that row as well as doing its own work. A
click on the row already marked returns immediately, which is what keeps repeated cell edits on the
marked row from re-writing the user configuration on every pass.</li>
</ul>
<p>The highlight is a tinted row background, and the row carries the native tooltip <em>&ldquo;Click to mark this
coordinate on the General Setup canvas&rdquo;</em>.</p>
<h2 id="what-a-brand-switch-carries">What a Brand Switch Carries</h2>
<p>The brand switch is the one operation in the branch that treats work coordinates specially. Its panel
carries a checkbox, <strong>Carry work-coordinate XYZ (G54…) into the new brand's table</strong>, ticked by
default, and the flag reaches the endpoint with the brand.</p>
<p>With it set, the offsets are read from the outgoing provider before the preset is swapped in, and
written into the incoming one afterwards — but only for the ids the incoming provider already
enumerates, and only when the two providers are different instances. Everything else is dropped. So
the carry is lossy in a shape that follows directly from the inventory above: Fanuc to Siemens keeps
G54 through G57 and loses G58, G59 and all 48 extended entries; Siemens to Fanuc keeps the same four
and loses the whole G505 series; a switch to Heidenhain keeps at most six.</p>
<p>The instance guard matters on the pair that shares a table. Mazak and Fanuc proxy the same parameter
table, so a switch between them resolves the same instance on both sides and the carry is skipped as
redundant — the values are already there. What a switch keeps, resets and destroys elsewhere is
<a class="xref" href="../machine/brand-switch.html">Brand Switch</a>.</p>
<h2 id="what-the-rows-feed">What the Rows Feed</h2>
<p>A work-coordinate word in a program is resolved into a translation composed onto the block's
program-to-machine transform. Two details of that path do not match what this panel shows.</p>
<p><strong>The run-time lookup walks every provider; so does the panel.</strong> The resolver iterates all offset
providers in the effective list and takes the first non-null answer, which is what lets a brand
table cover its hardware-mapped ids while a second provider covers ids the brand table does not map
— the shape the Fanuc, Mazak and Syntec presets ship in, with the extended <code>G59.1</code>–<code>G59.9</code> on a
brand-neutral table behind the brand table. The panel lists every provider's ids in the same order
(the first provider that holds an id wins) and writes an edit to the provider that enumerates the
id; the snapshot's kind field still names the first provider only, which is why the caption reads
&ldquo;Fanuc parameter table&rdquo; above rows the second table holds.</p>
<p><strong>The 48 extended rows are reached by <code>G54.1 Pn</code> — and by <code>G54 Pn</code>.</strong> The ISO coordinate syntax
reads G54–G59 and G59.1–G59.9 from the block's parsed flags, and the additional work coordinate
systems from the <code>G54.1 P</code> capture the parsing bundle writes as a sub-object; both spellings Fanuc's
manual gives that chapter (&ldquo;G54.1 or G54&rdquo;) land in that capture, with or without a space before the
P word, so <code>G54 P48</code> and <code>G54P48</code> select row 48 exactly as <code>G54.1 P48</code> does. A <code>G54</code> with no P word
in its scope stays the plain G54 flag. The row's id is the un-padded <code>G54.1P48</code>, the same key the
Fanuc-family table maps to <code>#7001</code>+, so on Fanuc, Mazak and Syntec the extended rows this panel edits
are the rows the program selects. A row that is selected but was never entered — the brand tables
seed every extended row with zero, as a fresh-battery controller reads them — reports
<code>Coord-WorkOffset--AdditionalZero</code> on the selecting block; the program then runs on the machine
origin, which is almost never what a pallet or fixture offset meant. A zero G54–G59 stays silent: it
is a common authoring convention. The Fanuc-family table is also the macro variable lookup, so on
Fanuc and Mazak those rows read back from a macro program as <code>#7001</code>–<code>#7999</code>, alongside G54–G59's
own <code>#5221</code>–<code>#5328</code>; the Syntec table implements no variable lookup, so on Syntec the rows are
reached by the coordinate words only.</p>
<p><strong>The extended G59.1–G59.9 ids live on a second table behind the brand table.</strong> The Fanuc-family
tables map G54–G59 and the 48 additional rows and nothing else, so the Fanuc, Mazak and Syntec
presets mount a brand-neutral coordinate table holding only <code>G59.1</code>–<code>G59.9</code> right behind their
brand table. The two id sets are disjoint, the run-time lookup finds each id on the one provider
that holds it, and this leaf shows the nine rows among the others and writes each edit to the
provider that carries the id. Those rows are seeded with zero and, like <code>G54</code>–<code>G59</code> and unlike the
48 additional rows, a zero one stays silent: leaving a row of the standard series at zero is a
common authoring convention. A project that kept a <code>G59.x</code> value in its
legacy coordinate table carries it into this second table when the legacy table migrates (each row
lands on the provider that carries its id). A controller saved before the second table existed
gains it on load; one that lost it resolves no offset for a <code>G59.x</code> at all and reports
<code>Coord-WorkOffset--NoTableEntry</code> instead.</p>
<p>Which row an untagged program starts on is the brand preset's static initializer, and the three
answers differ.</p>
<table>
<thead>
<tr>
<th>Preset</th>
<th>Initial coordinate id</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fanuc, Mazak, Syntec</td>
<td><code>G54</code></td>
<td>the G54 row is active from the first block</td>
</tr>
<tr>
<td>Siemens</td>
<td><code>G500</code></td>
<td>no frame is active; the offset is zero until a frame word appears</td>
</tr>
<tr>
<td>Heidenhain</td>
<td>none</td>
<td>no coordinate section is seeded; the datum cycles set one</td>
</tr>
</tbody>
</table>
<p>On Heidenhain the program's own route into the same table is the datum cycle rather than a G word: a
preset cycle reads the preset row its number names and writes a synthetic coordinate id that the
table resolves back, which is how rows 7–20 are consumed at run time despite having no row on this
leaf. A word or a cycle that resolves no offset composes a zero translation rather than failing.</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>Program Data</strong> Group Row — <code>equipment/controller/program-data</code>
<ul>
<li><strong>Work Coordinates (G54…)</strong> Node Row — <code>equipment/controller/program-data/work-coordinates</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>Work Coordinates (G54…)</strong> Panel
<ul>
<li>No-Runner Body — <em>&quot;No NC runner — load a project first.&quot;</em>; the shared empty state, shown
instead of everything below while the snapshot reports no runner</li>
<li>Absent-Table Body — <em>&quot;No work-coordinate table on the active runner.&quot;</em>; shown instead of
everything below while the panel's own read reports no provider</li>
<li>Header Strip
<ul>
<li>Storage Caption — the one-line note for the snapshot's coordinate kind, left-aligned and grey</li>
<li><strong>Show all</strong> Toggle — right-aligned, dense; rendered only while a row exists that the
always-visible test rejects</li>
</ul>
</li>
<li>Coordinate Table — dense, flat, bordered; no sort, no pagination, no column menu
<ul>
<li>Header Row — <strong>Id</strong>, <strong>X (mm)</strong>, <strong>Y (mm)</strong>, <strong>Z (mm)</strong>, <strong>Actions</strong>. The three axis headers
are literals in the template; <strong>Id</strong> and <strong>Actions</strong> are the shared translated labels</li>
<li>Coordinate Row, one per visible id — the whole row is clickable and carries the tooltip
<em>&ldquo;Click to mark this coordinate on the General Setup canvas&rdquo;</em>; the marked row is tinted
<ul>
<li>Id Label — bold plain text, never editable</li>
<li>Value Numeric Field, one per axis column — no minimum, no maximum, no unit suffix</li>
<li><strong>P0</strong> Button — flat, dense, tooltip <em>&ldquo;Set to the machine position at program zero&rdquo;</em></li>
<li><strong>M0</strong> Button — flat, dense, tooltip <em>&ldquo;Set to machine zero&rdquo;</em></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Toast — negative, three seconds, the panel's context followed by the server's own message</li>
</ul>
</li>
</ul>
<p>The panel carries no heading, no save button, no unsaved marker and no dialog, and it reports no
structural change, so no edit made on this leaf rebuilds the branch.</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/SoftNcWorkCoordinatesPanel.vue</code> — the panel: the storage
caption's five-way switch, the always-visible test and the conditional <strong>Show all</strong> toggle, the
whole-triad cell commit with its non-finite guard, the P0 / M0 actions and their re-read, and the
row click that writes the canvas marker.</li>
<li><code>wwwroot-src/src/components/controlTree/softNcItemTypes.ts</code> — the branch builder: this leaf's node
id and label key, and its position as the first ungated member of the program-data stem.</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/widgets/NumericInput.vue</code> — the value cell: commit on blur or Enter,
the empty-text-to-null parse, and the infinity and NaN literals the panel's handler then rejects.</li>
<li><code>wwwroot-src/src/api/softNcRunner.ts</code> — the snapshot's coordinate-kind field and its parser, the
work-coordinate reader and its row shape, the whole-triad setter, the two zeroing actions, and the
brand switch's carry flag.</li>
<li><code>wwwroot-src/src/api/equipmentSetup.ts</code> — the canvas marker id: the getter the panel seeds its
highlight from and the setter the row click writes, on the equipment-display surface rather than
this branch's own.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the two helpers this panel mixes: the envelope helper every table
call uses, and the plain-JSON helper the marker call uses.</li>
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the shared snapshot the coordinate kind and the
no-runner guard are read from.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcFramesPanel.vue</code> — the other node on the Siemens
frame table: the same rows through the frame-keyed reader, with an unconditional toggle and a
narrower always-visible test.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcDatumTablePanel.vue</code> — the other node on the
Heidenhain datum table: the preset rows this leaf aliases, plus the rows and the shift table it
does not reach.</li>
<li><code>wwwroot-src/src/components/controlTree/SoftNcBrandPanel.vue</code> — the carry checkbox and its default.</li>
<li><code>wwwroot-src/src/i18n/en/softNc.ts</code> — the node label, the two empty bodies, the five storage
captions, the row and button tooltips, and the five error contexts.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared <strong>Id</strong> and <strong>Actions</strong> column headers and the
<strong>Show all</strong> label.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the REST surface: the coordinate-kind type switch in the
snapshot builder, the reader that enumerates the provider's ids and zero-fills a null offset, the
whole-triad writer, the two zeroing actions with the program-zero lookup's own failure message, and
the brand switch's capture-swap-carry-sweep with its accepted-id filter and its instance guard.</li>
<li><code>Mech/EquipmentSetupDisplayController.cs</code> — the marker id's reader and writer, the user-config slot
it lands in, and the debounced save that follows.</li>
<li><code>Disp/EquipmentSetupDisplayee.cs</code> — the marker's binding to the first offset provider on the active
runner, and the guards that drop it with no chain or with its Scene flag off.</li>
<li><code>Disp/EquipmentSetupDisplayeeConfig.cs</code> — the stored marker id, its <code>G54</code> default and its
serialization.</li>
<li><code>Environments/UserService.cs</code> — the loose save behind the marker write, and the failure it logs
rather than returns.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs</code> — the offset-provider contract: the id-keyed
get and set, and the id enumeration every row of this leaf comes from.</li>
<li><code>HiMech/NcParsers/Dependencys/IsoCoordinateAddressMap.cs</code> — the Fanuc-family address scheme shared
by the Fanuc and Syntec tables: the two base addresses and the stride, the read that treats an
entry with no address as absent, the write that lays down all three, the enumeration this leaf's
rows follow, the per-address description the native form annotates with, and the seeding of every
entry with zero.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTable.cs</code> — the Fanuc and Mazak provider: its
offset accessors over the address map, its default table, and the variable lookup that also exposes
the same addresses to a macro program.</li>
<li><code>HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTable.cs</code> — the Syntec provider: the same
address scheme without the variable lookup.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTable.cs</code> — the Siemens provider: the seeded
G54–G57 and G505–G599 ids, the cancel-frame handling that keeps one code out of the row set, and
the per-axis translation dictionary neither panel exposes.</li>
<li><code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs</code> — the Heidenhain provider: the
twenty preset and twenty shift rows, the G54–G59 alias onto preset rows 1–6, and the synthetic ids
the datum cycles resolve through.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/IsoCoordinateTable.cs</code> — the brand-neutral table behind the
<code>generic</code> arm, and the fifteen ids it seeds.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/IsoCoordinateTableProxy.cs</code> — its get-or-create proxy, the
other shape a runner can carry it in.</li>
<li><code>HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTableProxy.cs</code>,
<code>HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTableProxy.cs</code> — the seedless proxies those
two brands carry: each installs a fresh table into a project holding none, which is why those two
brands open on their constructors' defaults.</li>
<li><code>HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs</code> — the seeded proxy the Fanuc and
Mazak presets share, and the reason those two resolve one instance.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the five brand presets and the offset provider each carries,
the proxy resolution every read goes through, and the legacy import that replays an older
coordinate table into whichever provider resolves.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/CoordinateOffsetUtil.cs</code> — the run-time resolver that walks every
provider and takes the first non-null answer, and the translation it composes.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/IsoCoordinateOffsetSyntax.cs</code> — the G-word path: the flag vocabulary
it consumes, the modal lookback that keeps a coordinate alive across blocks, and the zero fallback
when nothing answers.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainCoordinateOffsetSyntax.cs</code> — the datum-cycle
path into the same table, and the synthetic ids it writes.</li>
<li><code>HiMech/NcParsers/Keywords/Generic/IsoKeywords.cs</code> — the recognised G-word series.</li>
<li><code>HiMech/NcParsers/Keywords/Siemens/SiemensKeywords.cs</code> — the extended frame series and the cancel
frame.</li>
<li><code>HiMech/NcParsers/Initializers/StaticInitializer.cs</code> — the three brand defaults for the coordinate
active at the first block.</li>
<li><code>HiMech/Machining/MachiningEquipmentUtils/MachiningEquipmentUtil.cs</code> — the program-zero machine
position P0 writes: the reflection that zeroes every dynamic branch of the equipment assembly, and
the anchor fallback when the workpiece declares none.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">Program Data Plane</a> — the plane this leaf opens, and the per-case tables beside it
that a brand switch keeps or sweeps</li>
<li><a class="xref" href="frames.html">Frames</a> — the other node on the Siemens frame table, where the same rows are
edited as settable frames rather than as G54 offsets</li>
<li><a class="xref" href="datum-tables.html">Datum Tables</a> — the other node on the Heidenhain datum table, carrying the
preset rows this leaf aliases plus the fourteen rows and the shift table it cannot reach</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,34 @@
<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-matrix.html" name="" title="Brand Matrix">Brand Matrix</a>
</li>
<li>
<a href="editing-contract.html" name="" title="Editing Contract">Editing Contract</a>
</li>
<li>
<a href="machine/index.html" name="machine/toc.html" title="Machine / Controller Plane">Machine / Controller Plane</a>
</li>
<li>
<a href="program-data/index.html" name="program-data/toc.html" title="Program Data Plane">Program Data Plane</a>
</li>
</ul>
</div>
</div>
</div>
</div>
@@ -0,0 +1,2 @@
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"Brand Matrix","href":"brand-matrix.html","topicHref":"brand-matrix.html"},{"name":"Editing Contract","href":"editing-contract.html","topicHref":"editing-contract.html"},{"name":"Machine / Controller Plane","href":"machine/index.html","tocHref":"machine/toc.html","topicHref":"machine/index.html","homepage":"machine/index.html"},{"name":"Program Data Plane","href":"program-data/index.html","tocHref":"program-data/toc.html","topicHref":"program-data/index.html","homepage":"program-data/index.html"}]}
@@ -0,0 +1,323 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Fixture | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Fixture | 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-Fixture">
<h1 id="fixture">Fixture</h1>
<p>The fixture is what holds the workpiece on the machine table: one geometry, and the two anchor
transformers that place that geometry against the workpiece and against the table. It has two
surfaces:</p>
<ul>
<li>the <code>equipment/fixture</code> branch of the General Setup page's Control Tree
(<code>/general-setup?tree=equipment/fixture</code>), an equipment child of the General Setup group between
Coolant and Workpiece. <code>/fixture</code> and anything below it redirects there;</li>
<li>in the WPF client, a sub-window opened from the <a class="xref" href="../shell/main-panel.html">Main Panel</a>.</li>
</ul>
<p>Both edit the one fixture the project owns.</p>
<ul>
<li>Key Model:
<ul>
<li><a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html">Fixture</a>
On the web it is addressed by an IndexService key handed to the
<a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> and installed back onto
the project through the controller's UpdateFixture. On the WPF client the same button is wired
with a TargetObjectGetter / TargetObjectSetter pair (see that page for the design pattern).</li>
</ul>
</li>
<li>Assistant Model:
<ul>
<li><a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html">LocalProjectService</a> — owns the fixture as
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_Fixture">Fixture</a>.</li>
</ul>
</li>
</ul>
<p>The displayee differs by client. The web branch has no fixture-only canvas: it shares the General
Setup canvas, whose displayee is the equipment-setup displayee (<code>Disp/EquipmentSetupDisplayee.cs</code>) —
the merged fixture + workpiece scene with the anchor, buckle and controller-coordinate overlays. Its
option set is the equipment-setup config (<code>Disp/EquipmentSetupDisplayeeConfig.cs</code>), carried on
<code>UserService.UserConfig</code> as its <code>EquipmentSetupDisplayeeConfig</code> property. The WPF sub-window has a
canvas of its own, whose displayee is <a class="xref" href="../../api/Hi.NcMech.Fixtures.FixtureEditorDisplayee.html">FixtureEditorDisplayee</a> configured by
<a class="xref" href="../../api/Hi.NcMech.Fixtures.FixtureEditorDisplayeeConfig.html">FixtureEditorDisplayeeConfig</a>, taken from <code>UserService.UserConfig</code> as its
<code>FixtureSetupDisplayeeConfig</code> property.</p>
<h2 id="layout">Layout</h2>
<h3 id="control-tree-branch">Control Tree Branch</h3>
<p>The root panel is rendered inline by the dock's primary editor pane, so it has no title label of its
own — the wrapping expansion header carries the selection breadcrumb. Every item below is a tree
node whose editor is swapped into that same PRIMARY row.</p>
<ul>
<li>Fixture Root Panel — <code>equipment/fixture</code>, item type <code>FixtureRoot</code>
<ul>
<li><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> — file extension
<code>.Fixture</code>, load type <code>Hi.NcMech.Fixtures.Fixture, HiMech</code>, rel file <code>Fixture.xml</code>, based at the
project directory. Load / Save As / Copy / Paste / XML.</li>
<li>Geometry Type Badge — the attached geometry's kind name, <code>none</code> when the slot is empty.</li>
<li>Intro caption, and an empty-state caption while no fixture key is minted.</li>
</ul>
</li>
<li><code>equipment/fixture/geometry</code> — &ldquo;Geometry&rdquo;, a Geometry slot backing
<a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html#Hi_NcMech_Fixtures_Fixture_Geom">Geom</a>. The slot's own panel is the kind picker
(<a class="xref" href="../geom/geom-manage-control.html">Geometry Management Control</a>, selector only); the picked kind's
editor is the slot's child item. Kinds offered: <code>Box3d</code>, <code>Cylindroid</code>, <code>StlFile</code>,
<code>TransformationGeom</code>, <code>GeomCombination</code>, plus None. The geometry must satisfy <code>IStlSource</code>, so the
runtime-only voxel <code>CubeTreeFile</code> is not among them.</li>
<li><code>equipment/fixture/anchor</code> — &ldquo;Anchor&rdquo;, a Group stem whose info text states how the fixture is
placed. Two Transformer slots hang from it, each a kind picker
(<a class="xref" href="../geom/transformer-panel.html">Transformer Manage Panel</a>) over the picked kind's child editor:
<ul>
<li><code>equipment/fixture/geom-to-workpiece</code> — &ldquo;Geom To Workpiece&rdquo;,
<a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html#Hi_NcMech_Fixtures_Fixture_GeomToWorkpieceTransformer">GeomToWorkpieceTransformer</a>, which places the workpiece buckle
— where the workpiece attaches — relative to the fixture geometry (see
<a class="xref" href="workpiece.html">Workpiece</a>).</li>
<li><code>equipment/fixture/geom-to-table</code> — &ldquo;Geom To Table&rdquo;,
<a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html#Hi_NcMech_Fixtures_Fixture_GeomToTableTransformer">GeomToTableTransformer</a>, which pins the fixture geometry onto
the machine table buckle.</li>
<li>Kinds offered on both: <code>StaticTranslation</code>, <code>StaticRotation</code>, <code>StaticFreeform</code>,
<code>DynamicTranslation</code>, <code>DynamicRotation</code>, <code>GeneralTransform</code>, <code>NoTransform</code>.</li>
</ul>
</li>
<li>Shared slave view: the General Setup equipment canvas in the page's MAIN column, shared with
Machine Tool, Workpiece and Controller (see <a class="xref" href="index.html">General Setup Page</a>). Its
Display Options dropdown carries this branch's share of the scene — the <strong>Fixture</strong>
Solid / Edge / Hide radio group, and the Fixture Geom Anchor, Workpiece Buckle and Table Buckle
flags in the Anchors group.</li>
</ul>
<h3 id="wpf-page">WPF Page</h3>
<ul>
<li>Head Line
<ul>
<li><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> — file extension
<code>.Fixture</code>, load type <a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html">Fixture</a>, rel file <code>Fixture.xml</code>. The pointed
Editor Panel is the Management Tabs Panel, a ContentPresenter the button swaps between the tab
stack and XML mode.</li>
<li>Title Label</li>
</ul>
</li>
<li>Management Tabs Panel — the same content the tree renders as nodes, stacked as tabs:
<ul>
<li>Geometry Tab — <a class="xref" href="../geom/geom-manage-control.html">Geometry Management Control</a> over
<a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html#Hi_NcMech_Fixtures_Fixture_Geom">Geom</a>.</li>
<li>Anchor Tab — a nested tab stack, each tab a
<a class="xref" href="../geom/transformer-panel.html">Transformer Manage Panel</a>:
<ul>
<li>Geom To Workpiece Tab</li>
<li>Geom To Table Tab</li>
</ul>
</li>
</ul>
</li>
<li>Viewer Panel
<ul>
<li>Viewer ToolBar
<ul>
<li><a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a></li>
<li>Display Options menu — the Show Geom Anchor, Show Workpiece Buckle and Show Table Buckle
checkboxes and the Solid / Edge / Hide rendering-mode radio group, all writing
<a class="xref" href="../../api/Hi.NcMech.Fixtures.FixtureEditorDisplayeeConfig.html">FixtureEditorDisplayeeConfig</a>.</li>
</ul>
</li>
<li>RenderingCanvas — its <a class="xref" href="../../api/Hi.Disp.DispEngine.html">DispEngine</a>.<a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_Displayee">Displayee</a> is
<a class="xref" href="../../api/Hi.NcMech.Fixtures.FixtureEditorDisplayee.html">FixtureEditorDisplayee</a>.</li>
</ul>
</li>
</ul>
<p>Both clients put a draggable divider between the editor column and the canvas: a GridSplitter in the
WPF page, nested draggable splitters on the web page (dock / content column / canvas), plus the
dock's own height divider between the tree row and the editor row.</p>
<h2 id="behavior">Behavior</h2>
<ul>
<li><a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html#Hi_NcMech_Fixtures_Fixture_ClearGeomCache">ClearGeomCache()</a> runs after any change at or below the branch. On
the web each node's afterChange chain ends in the controller's ClearGeometryCache, which calls it;
in WPF the page calls it directly from the geometry control's setter and update callbacks.</li>
<li>The two anchor slots re-commit their swap-in <em>before</em> clearing the cache: an inner-value edit
re-posts UpdateGeomToWorkpieceTransformer / UpdateGeomToTableTransformer, so the object the
switchboard produced is always the one installed on the owning field when the redraw happens.</li>
<li>Load / Paste / XML-Apply only swap the IndexService entry. The tree root then installs the result
onto the project through UpdateFixture — the equipment setter re-attaches the buckles itself —
re-runs Initialize to re-mint the branch's keys, and clears the geometry cache. The button's XML
dialog re-emits its object-loaded event on Apply, so that install and refresh chain runs exactly
once.</li>
<li>Kind pickers are parent-aware. Picking a geometry kind calls CreateGeometry, which installs the
new object on <a class="xref" href="../../api/Hi.NcMech.Fixtures.Fixture.html#Hi_NcMech_Fixtures_Fixture_Geom">Geom</a> server-side and maps the literal <code>None</code> to
null; picking a transformer kind creates the object and then rebinds the owning field.</li>
<li>View snapping differs by client. The WPF page snaps its canvas to the isometric view when the
displayee is bound, and again whenever a new geometry object is set, on the assumption that a
shape swap changes the scene more than an edit within one shape does. The shared web canvas snaps
once, when its displayee is bound — the display controller's initialize does it server-side.
Afterwards a geometry set only clears the cache, and the operator re-frames from the
<a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a>'s view picker.</li>
<li>Both canvases draw the live model, so an edit shows on the next frame with no further calls.</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/useControlTreeHost.ts</code> — builds the <code>equipment/fixture</code>
root (item type <code>FixtureRoot</code>, keyed on the IndexService fixture key) with its <code>geometry</code> slot and
the <code>anchor</code> Group holding <code>geom-to-workpiece</code> and <code>geom-to-table</code>; owns the clear-cache and
transformer-rebind afterChange chains, the create hooks and the Object Management install
handlers.</li>
<li><code>wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue</code> — renders the root editor inline:
the Object Management button, the geometry-type badge and the intro / empty-state captions.</li>
<li><code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> — the ItemType registry that binds the
branch's Geometry and Transformer slots and every concrete kind to their panels, and grows the
sub-tree from the indexed object's type.</li>
<li><code>wwwroot-src/src/components/controlTree/GeometrySlotPanel.vue</code> — the Geometry node's kind picker.</li>
<li><code>wwwroot-src/src/components/controlTree/TransformerSlotPanel.vue</code> — the Geom To Workpiece and
Geom To Table nodes' kind picker.</li>
<li><code>wwwroot-src/src/components/controlTree/SoleEditorPanel.vue</code> — hosts the concrete kind's own
editor once a geometry or transformer kind is chosen.</li>
<li><code>wwwroot-src/src/components/controlTree/ControlTreeDock.vue</code> — the left dock: the Control Tree row
over the PRIMARY editor row, with a draggable height divider.</li>
<li><code>wwwroot-src/src/pages/GeneralSetupPage.vue</code> — the three-column host page (dock / content column /
canvas) that provides the equipment-scope control-tree host.</li>
<li><code>wwwroot-src/src/components/mech/EquipmentSetupPanel.vue</code> — the shared canvas column: the
RenderingCanvasToolBar, the Display Options dropdown and the RenderingCanvas.</li>
<li><code>wwwroot-src/src/components/widgets/ObjectManagementMenuButton.vue</code> — the reused
object-management dropdown, keyed entirely on its <code>modelKey</code> (see
<a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a>).</li>
<li><code>wwwroot-src/src/components/geom/GeometryEditor.vue</code> — the generic geometry kind switchboard
reached from the Geometry slot panel, including its None (unset) entry.</li>
<li><code>wwwroot-src/src/components/topo/TransformerSelectPanel.vue</code> — the generic transformer kind
switchboard reached from the Transformer slot panels.</li>
<li><code>wwwroot-src/src/api/fixture.ts</code> — typed client for <code>/api/Fixture/*</code>: Initialize, UpdateFixture,
the three Get…Type reads, UpdateGeometry, the two Update…Transformer rebinds, CreateGeometry and
ClearGeometryCache.</li>
<li><code>wwwroot-src/src/api/equipmentSetup.ts</code> — typed client for the shared canvas: binds the
equipment-setup displayee onto the connection's engine and drives its display options.</li>
<li><code>wwwroot-src/src/router/routes.ts</code> — declares <code>general-setup</code> and the <code>fixture/:rest(.*)*</code>
redirect onto <code>?tree=equipment/fixture</code>.</li>
<li><code>Mech/FixtureController.cs</code> — REST surface at <code>/api/Fixture</code>: Initialize, UpdateFixture,
GetGeometryType, GetGeomToWorkpieceType, GetGeomToTableType, UpdateGeometry,
UpdateGeomToWorkpieceTransformer, UpdateGeomToTableTransformer, CreateGeometry,
IndexCurrentGeometry and ClearGeometryCache.</li>
<li><code>Mech/EquipmentSetupDisplayController.cs</code> — <code>/api/mech/equipment-setup-display</code>: binds and
configures the displayee per rendering connection and serves its whole option set — the three
rendering-mode endpoints (fixture, raw-geom, ideal-geom), show-machine, show-tool,
show-dimension-bar, show-fixture-geom-anchor, show-workpiece-buckle, show-table-buckle,
show-workpiece-geom-anchor, show-program-zero-anchor, show-meshed-geom,
show-controller-coordinate and controller-coordinate-id.</li>
<li><code>Disp/EquipmentSetupDisplayee.cs</code> — the displayee the General Setup canvas renders: the merged
fixture + workpiece scene with the anchor, buckle and controller-coordinate overlays.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/NcMech/Fixtures/Fixture.cs</code> — the key model: the geometry, the table and workpiece
buckles, the geom anchor, the two anchor transformers and ClearGeomCache.</li>
<li><code>HiMech/NcMech/Fixtures/FixtureEditorDisplayee.cs</code> — the WPF fixture canvas's displayee.</li>
<li><code>HiMech/NcMech/Fixtures/FixtureEditorDisplayeeConfig.cs</code> — its option set: ShowGeomAnchor,
ShowWorkpieceBuckle, ShowTableBuckle and the rendering mode.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../util/mech-builder.html">Mechanism Builder Page</a> — reuses this page’s parent-aware transformer rebind pattern</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,610 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hidden Controller Branches | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Hidden Controller Branches | 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-HiddenControllers">
<h1 id="hidden-controller-branches">Hidden Controller Branches</h1>
<p>Two Control-Tree nodes on the General Setup page are absent from a fresh installation: <strong>CSV
Controller</strong> and <strong>CL Controller</strong>, on <code>/general-setup</code> under the ids <code>equipment/controller-csv</code> and
<code>equipment/controller-cl</code>. Each is the entire editing face of one resident non-brand runner suit —
the CSV control-table pipeline and the NX-CL (CLSF) pipeline — and each is minted only when its
device-local Preference checkbox is on or the current URL targets it. This page is the answer to
&ldquo;why is that node not in the tree&rdquo;, because the equipment tree withholds a node in two different ways
and only one of them can be talked out of it.</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="two-kinds-of-invisibility">Two Kinds of Invisibility</h2>
<table>
<thead>
<tr>
<th style="text-align: left;"></th>
<th>Preference-gated</th>
<th>Brand-gated</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left;">Which nodes</td>
<td><code>equipment/controller-csv</code>, <code>equipment/controller-cl</code></td>
<td>every gated leaf under <code>equipment/controller</code></td>
</tr>
<tr>
<td style="text-align: left;">What decides</td>
<td>a checkbox stored in the browser</td>
<td>the active runner's dependency list</td>
</tr>
<tr>
<td style="text-align: left;">Where the decision lives</td>
<td>one device</td>
<td>the project</td>
</tr>
<tr>
<td style="text-align: left;">Reachable by a link anyway</td>
<td>yes, and deliberately</td>
<td>no</td>
</tr>
<tr>
<td style="text-align: left;">What a link to it does</td>
<td>mints the node and selects it</td>
<td>nothing at all</td>
</tr>
</tbody>
</table>
<p>The distinction is not a matter of degree. A preference-gated node exists in the code that builds
the tree and is simply not pushed; the same build reads the URL, so naming the node in a link is
enough to bring it back for that visit. A brand-gated node has no such second door: the branch
builder never creates it, and the selection code refuses an id the built tree does not carry, so
there is nothing for a link to select. Which leaf that branch grows on which brand, and the snapshot
flag behind each one, is <a class="xref" href="controller/brand-matrix.html">Brand Matrix</a>.</p>
<h2 id="preference-gated-the-two-runner-suit-nodes">Preference-Gated: The Two Runner-Suit Nodes</h2>
<p>The equipment tree host mints each of the two nodes when <strong>a project is open</strong> and <strong>either</strong> the
node's checkbox is on <strong>or</strong> the route's tree id begins with that node's id. All three parts matter.</p>
<p><strong>With no project open neither node is built</strong>, whatever the checkboxes say — the same guard that
keeps the Controller branch childless before a project is loaded. The consequence is not a dead end:
loading a project bumps the layout's project epoch, which destroys and rebuilds the page, and the
rebuilt host runs the build again and then adopts whatever <code>?tree=</code> the URL still carries. A link
opened against an empty application therefore lands on its node once a project is opened, without
the link being touched.</p>
<p><strong>The checkboxes are device-local.</strong> Both are held in one browser-storage record of view
preferences, off by default, written back a moment after each flip, and never sent to the server —
so the setting belongs to a browser rather than to a user account or to a project. They are the
<strong>CSV Controller</strong> and <strong>CL Controller</strong> entries of the app menu bar's Preference dropdown, described
in <a class="xref" href="../shell/preference-menu.html">Preference Menu</a>.</p>
<p><strong>Only the route reveals — not the persisted last selection.</strong> The host also remembers the last node
selected on each tree page, and that memory deliberately mints nothing: the landing selection is
honoured only when the id it names is already in the built tree, and falls back to the <code>equipment</code>
group root when it is not. Reveal-by-route has to happen while the tree is being built, because the
code that adopts a <code>?tree=</code> id afterwards refuses an id the tree lacks.</p>
<h3 id="switching-a-box-off-while-standing-on-its-node">Switching a box off while standing on its node</h3>
<p>The host watches both checkboxes and rebuilds the whole equipment tree when either flips. Before the
rebuild it checks whether the current selection is one of the nodes that just lost its box, and when
it is, it does two things in this order:</p>
<ol>
<li>Replaces the URL's <code>tree</code> query with <code>equipment</code>.</li>
<li>Moves the selection to the <code>equipment</code> group root.</li>
</ol>
<p>Only then does the tree rebuild. The order is the point: the URL already mirrors the selection, and
the URL is exactly what reveals an unchecked node, so a selection left standing there would leave its
id in the query, and the next build's prefix test would re-mint the node that was just switched off.
The reader lands on the General Setup group panel, whose clickable child list no longer carries the
row. The selection is moved directly rather than through the host's switch request, which would
matter only for a panel registering the before-switch gate; neither of these two does.</p>
<p>Switching a box off while the selection is elsewhere takes no such detour — the rebuild simply drops
the node from the tree and from the group panel's child list.</p>
<h3 id="what-the-two-nodes-are-not">What the two nodes are not</h3>
<p>Both are childless leaves. Neither declares a child builder, so nothing grows beneath them; neither
registers a content view, so the General Setup content column keeps its own empty hint for both; and
neither panel reports a structural change, so nothing either panel does rebuilds any part of the
tree. There is no Object-Management menu on either: a CSV or CL pipeline is not loaded, pasted or
saved as a file of its own the way a brand controller is.</p>
<h2 id="brand-gated-the-node-that-was-never-minted">Brand-Gated: The Node That Was Never Minted</h2>
<p>Under <code>equipment/controller</code> the node set is grown from a snapshot of the active NC runner, and a
leaf whose backing dependency that runner does not resolve is never created. Nothing reveals it. A
link naming such an id — <code>equipment/controller/program-data/frames</code> on a project running a Fanuc
runner, say — reaches the host, finds no such node in the built tree, and is dropped: the selection
is left as it was, the URL keeps the id it was given, and no redirect and no message follow. On a
first load that means the editor row shows its own hint, <em>&quot;Select an item in the Control Tree to edit
it here.&quot;</em>, under a tree in which the node the link names is simply not present.</p>
<p>The two behaviours meet at the same line of code from opposite sides. The runner-suit nodes are read
out of the URL <strong>while the tree is built</strong>, so by the time that check runs they exist; a brand-gated
leaf is not, so the check is the end of the road for it. That is why a checkbox can be overruled by a
link and a runner cannot.</p>
<h2 id="what-the-preference-captions-mean">What the Preference Captions Mean</h2>
<p>Under each checkbox sits a caption reading either <em>&ldquo;This project plays CSV&rdquo;</em> / <em>&ldquo;This project plays
CL&rdquo;</em> or <em>&ldquo;Not used by this project&rdquo;</em>. It is the informed-choice half of the design: the menu answers
whether the loaded project plays that kind, so the decision to show a node is made with evidence
rather than by trial. The caption is read when the Preference dropdown opens rather than on page
load, and both runner snapshots are fetched together; with no project open both captions are blank,
and a snapshot that fails to load leaves its own caption blank rather than asserting either answer.</p>
<p>The evidence is a scan of the loaded project, per kind:</p>
<ul>
<li>A mission <strong>Program File</strong> command whose effective kind is that kind — its explicit kind, or, for
the default <em>Auto</em>, the kind detected from the file extension: <code>.cl</code>, <code>.cls</code> and <code>.clsf</code> are CL,
<code>.csv</code> is CSV, and every other extension is brand NC code
(<a class="xref" href="../../api/Hi.MachiningProcs.NcKindUtil.html#Hi_MachiningProcs_NcKindUtil_DetectByPath_">DetectByPath</a><small>(API)</small>).</li>
<li>A mission <strong>script</strong> command whose text contains <code>CsvFile(</code> or <code>ClFile(</code>. It is a bare substring
test rather than a list of verbs. The play verbs end in it —
<a class="xref" href="../../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_PlayCsvFile_">PlayCsvFile</a><small>(API)</small> and
<a class="xref" href="../../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_PlayClFile_">PlayClFile</a><small>(API)</small> — and on the CSV side so do
the two sensor-mapping verbs,
<a class="xref" href="../../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_MapSingleByCsvFile_">MapSingleByCsvFile</a><small>(API)</small> and
<a class="xref" href="../../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_MapSeriesByCsvFile_">MapSeriesByCsvFile</a><small>(API)</small>, so a script that
only maps recorded telemetry onto steps counts as CSV evidence though it plays no control table.
The CL side carries no such neighbour: every member whose call text ends in <code>ClFile(</code> plays or runs
a CLSF file.</li>
<li>For CL only, a machining chain that is a <a class="xref" href="../../api/HiMachining.Milling.ClMillingDevice.html">ClMillingDevice</a>, since a
pure-CL project plays nothing else.</li>
</ul>
<p>Three properties of that scan are easy to guess wrong. It walks the whole mission, the entries of a
nested list command included, and it walks <strong>through</strong> each command's enable wrapper without reading
it — a Program File command that is switched off still counts as evidence. The script test cuts the
other way too: a play routed through the generic NC entry point with a <code>.csv</code> path is deliberately
not matched, because a bare <code>.csv</code> appears in step and shot output templates far more often than in
play paths. And the answer feeds the caption and nothing else — <strong>usage evidence mints no node.</strong>
The tree is built from the checkbox and the route, and from nothing the scan reports.</p>
<h2 id="how-both-panels-edit">How Both Panels Edit</h2>
<p>Neither node belongs to the Controller branch, so that branch's
<a class="xref" href="controller/editing-contract.html">Editing Contract</a> does not govern them. Both panels
nonetheless follow most of its shape, and the places they do not are worth naming.</p>
<p><strong>Shared with the contract.</strong> Each panel fetches its own snapshot once on mount and owns no store.
Each opens with two guards, in order: <em>&quot;No project loaded.&quot;</em> from the snapshot's own project flag,
then a line naming the absent config — <em>&quot;No CSV column config on the CSV runner.&quot;</em> or <em>&quot;No CLSF
config on the CL runner.&quot;</em> Every control commits on its own, with no save button and no dirty
marker. A failed write raises one negative toast, three seconds, composed as the panel's localized
context followed by the server's own English sentence, with a console line beside it, and leaves no
inline error state behind once it expires. Both REST surfaces answer a missing config inside a
success envelope rather than with an error status, and the shared write helper turns that envelope
into a thrown error; the read helper inspects no envelope, and neither endpoint has a non-200 path,
so a read fails only on transport. A failed read therefore leaves the empty snapshot in place and the
panel renders its <em>&quot;No project loaded.&quot;</em> line — the same body an absent project produces, with the
toast as the only signal that the two differ.</p>
<p><strong>Where they part from it.</strong> The CSV panel is not optimistic: its text fields are bound to a local
draft, so the screen already shows the new text, and the request is awaited before the panel's
server mirror is updated; a failure restores the field from that mirror. The CL panel is optimistic
in the contract's own shape, assigning first and restoring on failure, and its two numeric fields are
the shared numeric widget, committing on blur or on Enter and never per keystroke; the full widget
contract is <a class="xref" href="../widget/numeric-input.html">Numeric Input</a>.</p>
<p><strong>Both panels guard on equality, and the guard is the panel's rather than the widget's.</strong> Every
commit handler on both — the CSV tag fields, the two CL rates and the tool-house toggle — compares
the incoming value against the fetched server mirror first and returns when the two match, so
committing an untouched field, or blurring after an Enter that already succeeded, sends nothing.
That is the departure from the Controller branch, whose numeric leaves carry no such comparison and
lean on the shared widget alone, and the widget emits on every blur and every Enter. The one control
across the two panels with no such comparison is the CL chip field.</p>
<h2 id="csv-controller">CSV Controller</h2>
<p>The node is the face of the project's resident CSV runner suit
(<a class="xref" href="../../api/Hi.MachiningProcs.MachiningProject.html#Hi_MachiningProcs_MachiningProject_CsvRunnerSuit">CsvRunnerSuit</a>), a whole runner suit constructed alongside
the brand one and never absent from a loaded project. Its runner is a
<a class="xref" href="../../api/Hi.NcParsers.SoftNcRunner.html">SoftNcRunner</a> assembled as the CSV pipeline by the factory
<a class="xref" href="../../api/Hi.Numerical.CsvParsers.GeneralCsvRunner.html">GeneralCsvRunner</a>, and it replays a CSV control table. A project file
that stores the column configuration flat, outside any suit, has that element migrated into the
suit's pipeline as it loads, so such a file keeps its tags.</p>
<p>The whole face is one flat configuration, <a class="xref" href="../../api/Hi.Numerical.CsvParsers.CsvRunnerConfig.html">CsvRunnerConfig</a> — no brand
presets, no per-case tables and no native parameter form, which is why the node is a leaf rather than
a branch. The panel's caption says what it edits: <em>&quot;Column tags of the CSV control table (matched
against the header line). Played by a mission Program File of CSV kind or a script's
<code>PlayCsvFile(&quot;…&quot;)</code>.&quot;</em> The header line is the file's first row, split on commas; each title then has
quote characters stripped from its two ends and is trimmed of whitespace, in that order, so a title
whose opening quote sits behind a space keeps that quote and never matches its tag.</p>
<table>
<thead>
<tr>
<th>Field label</th>
<th>Default tag</th>
<th>What reads it</th>
</tr>
</thead>
<tbody>
<tr>
<td>Machine coordinate prefix</td>
<td><code>MC.</code></td>
<td>the machine-coordinate columns, composed as the prefix plus <code>X</code>, <code>Y</code>, <code>Z</code>, <code>A</code>, <code>B</code>, <code>C</code></td>
</tr>
<tr>
<td>Cutter location prefix</td>
<td><code>CL.</code></td>
<td>nothing in the shipped pipeline</td>
</tr>
<tr>
<td>Tool id column</td>
<td><code>ToolId</code></td>
<td>the tool-change section</td>
</tr>
<tr>
<td>Spindle speed column (rpm)</td>
<td><code>SpindleSpeed_rpm</code></td>
<td>the spindle section</td>
</tr>
<tr>
<td>Spindle direction column</td>
<td><code>Spd.Dir.</code></td>
<td>the spindle section, parsed as a direction name; a speed with no direction turns clockwise</td>
</tr>
<tr>
<td>Feedrate column (mm/min)</td>
<td><code>Feedrate_mmdmin</code></td>
<td>the feedrate section</td>
</tr>
<tr>
<td>Step duration column (s)</td>
<td><code>StepDuration</code></td>
<td>the recorded-timing section</td>
</tr>
<tr>
<td>Actual time column</td>
<td><code>ActualTime</code></td>
<td>the recorded-timing section</td>
</tr>
<tr>
<td>Coolant column</td>
<td><code>Coolant</code></td>
<td>the coolant section</td>
</tr>
<tr>
<td>Line-begin C# script column</td>
<td><code>LineBeginCsScript</code></td>
<td>the script section, run before the row</td>
</tr>
<tr>
<td>Line-end C# script column</td>
<td><code>LineEndCsScript</code></td>
<td>the script section, run after the row</td>
</tr>
</tbody>
</table>
<p>Four things that table does not show on its own:</p>
<ul>
<li><strong>The cutter-location prefix is stored, serialized and editable, and no syntax in the shipped CSV
pipeline reads it.</strong> Columns under it are not consumed as coordinates; they survive into the
residual telemetry below.</li>
<li><strong>Naming a column consumes it.</strong> Each syntax removes the columns its tags name from the decoded
row, and whatever is left over is carried onto the step as recorded data — sensor channels, file
and line bookkeeping, anything else the file holds. So clearing a tag does more than stop the value
being used: it moves that column into the residual set.</li>
<li><strong>The duration cell is read as a time span rather than as a number of seconds</strong>, despite the
field's own <code>(s)</code>. A cell that does not parse falls back to the difference between this row's
actual time and the previous parseable one, and a resolved duration longer than one minute is
clamped to one minute, on the reasoning that a longer gap is spliced recordings rather than
machining time.</li>
<li><strong>An empty field is a real edit.</strong> The write applies every field the payload carries, and the panel
sends exactly the field that changed, so blanking a box clears that tag on the model rather than
leaving it alone.</li>
</ul>
<p>Each field commits on blur or on Enter — never while typing — and sends only itself. There is no
validation on either side of the wire: no trimming, no bounds, no uniqueness.</p>
<h2 id="cl-controller">CL Controller</h2>
<p>The node is the face of the project's resident NX-CL suit
(<a class="xref" href="../../api/Hi.MachiningProcs.MachiningProject.html#Hi_MachiningProcs_MachiningProject_ClsfRunnerSuit">ClsfRunnerSuit</a>), constructed the same way beside the brand
suit and the CSV suit, its runner a <a class="xref" href="../../api/Hi.NcParsers.SoftNcRunner.html">SoftNcRunner</a> assembled as the CLSF pipeline
by the factory <a class="xref" href="../../api/Hi.Numerical.ClsfParsers.NxClRunner.html">NxClRunner</a>. Its caption reads <em>&quot;Plays NX
cutter-location files (CLSF) — a mission Program File of CL kind or a script's <code>PlayClFile(&quot;…&quot;)</code>.&quot;</em></p>
<p>Everything on the panel is one <a class="xref" href="../../api/Hi.Numerical.ClsfParsers.ClsfRunnerConfig.html">ClsfRunnerConfig</a>, and every field on
it answers something a cutter-location file cannot say for itself.</p>
<p><strong>Rapid feedrate (assumed)</strong>, suffixed <code>mm/min</code>, and <strong>Rotary rapid feedrate (assumed)</strong>, suffixed
<code>deg/min</code>, default to 20000 and 36000. A CLSF carries no machine axes, so a <code>RAPID</code> move has nothing
to be timed against — but the two rates are not read on the same path, and the machining chain
decides which. On a pure-CL chain the CLSF pipeline's own motion semantic reads the linear rate
straight off this configuration and times every rapid from it; the rotary rate is never consulted.
On a machine-tool chain, where each CLSF block is re-expressed in machine coordinates and routed to
the motion semantics reused from the NC pipeline, the configuration doubles as the pipeline's
rapid-feedrate provider and answers axis-uniformly — every linear axis gets the first rate, every
rotary axis the second — and that is the only place the rotary rate is read.</p>
<p>Both fields carry a minimum of 1: a smaller value is refused inside the widget itself, which shows
<em>Must be ≥ 1</em> under the box and emits nothing, so no request is made and no toast appears. The
server's own positivity check sits behind that and is not reachable from this panel. A cleared or
non-finite entry is dropped by the panel before the request, which leaves the box showing what was
typed while the stored rate stands; selecting another node and returning restores the box.</p>
<p><strong>Prefer Tool House on LOAD/TOOL</strong> is a toggle, on by default, committing on the click. Its caption
sits below it permanently rather than as a field hint: <em>&quot;On: a LOAD/TOOL id already configured in the
Tool House keeps that tool (CLSF TLDATA geometry ignored). Off: TLDATA overwrites the tool-house
entry on every load.&quot;</em> That is what the code does — a tool change requesting an id the tool house
already holds is left alone while the toggle is on, and the file's own tool geometry rebuilds the
entry when it is off. Either way an id the house does not hold is built from the file's tool data,
and an id with neither is reported as a configuration error.</p>
<p><strong>Excluded record words</strong> is a chip field: a word typed and entered joins the list, and there is no
dropdown, because the option list is deliberately empty. Its preset list is <code>PAINT</code>, <code>TOOLNO</code>,
<code>LOADTL</code>, <code>TOOL PATH</code>, <code>TOOLPATH</code> and <code>END-OF-PATH</code>. A CLSF record that reaches the end of the
pipeline unhandled is otherwise reported as a validation warning naming the record word; a word on
this list is consumed silently instead, matched whole and without regard to case. Adding or removing
one chip writes the whole list, the server trims each word and drops the blank ones before replacing
the stored list, and the panel keeps what was typed — so a word entered with stray whitespace reads
differently on screen from what was stored, until the panel is remounted.</p>
<h2 id="layout">Layout</h2>
<ul>
<li>App Menu Bar
<ul>
<li><strong>Preference ▾</strong> Dropdown — fetches both runner snapshots as it opens, and only with a project
open
<ul>
<li><strong>CSV Controller</strong> CheckBox
<ul>
<li>Usage Caption — <em>&ldquo;This project plays CSV&rdquo;</em>, <em>&ldquo;Not used by this project&rdquo;</em>, or blank</li>
</ul>
</li>
<li><strong>CL Controller</strong> CheckBox
<ul>
<li>Usage Caption — the same three states, for CL</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><strong>General Setup</strong> Control Tree — the left dock of <code>/general-setup</code>
<ul>
<li><strong>General Setup</strong> Group Row — <code>equipment</code>
<ul>
<li><strong>Controller</strong> Node Row — the brand branch, built ahead of the two rows below</li>
<li><strong>CSV Controller</strong> Node Row — <code>equipment/controller-csv</code>; present only under the conditions
above, and pushed before the CL row when both are</li>
<li><strong>CL Controller</strong> Node Row — <code>equipment/controller-cl</code>; the last row the group can carry. Each
is a plain label with no icon, no checkbox and no children</li>
</ul>
</li>
</ul>
</li>
<li>Editor Row — the panel of whichever node is selected
<ul>
<li><strong>General Setup</strong> Group Panel — where a switched-off node's selection lands: the group's intro
caption over a bordered, separated list of its children, one clickable row each</li>
<li><strong>CSV Controller</strong> Panel
<ul>
<li>No-Project Body — <em>&quot;No project loaded.&quot;</em></li>
<li>No-Config Body — <em>&quot;No CSV column config on the CSV runner.&quot;</em></li>
<li>Editor Body
<ul>
<li>Intro Caption — the column-tags line, with <code>PlayCsvFile(&quot;…&quot;)</code> set in code</li>
<li>Tag Text Field, one per row, in this order: <strong>Machine coordinate prefix</strong> (hint <em>e.g. &ldquo;MC.&rdquo; →
MC.X / MC.Y / MC.Z</em>), <strong>Cutter location prefix</strong> (hint <em>e.g. &ldquo;CL.&rdquo; → CL.X / CL.Y / CL.Z</em>),
<strong>Tool id column</strong>, <strong>Spindle speed column (rpm)</strong>, <strong>Spindle direction column</strong>,
<strong>Feedrate column (mm/min)</strong>, <strong>Step duration column (s)</strong> (hint <em>Overrides feedrate-derived
timing.</em>), <strong>Actual time column</strong> (hint <em>Wall-clock instant of the row.</em>), <strong>Coolant column</strong>
(hint <em>Flood / Mist / Off or on/off.</em>), <strong>Line-begin C# script column</strong> (hint <em>Runs before the
row.</em>), <strong>Line-end C# script column</strong> (hint <em>Runs after the row.</em>)</li>
</ul>
</li>
</ul>
</li>
<li><strong>CL Controller</strong> Panel
<ul>
<li>No-Project Body — <em>&quot;No project loaded.&quot;</em></li>
<li>No-Config Body — <em>&quot;No CLSF config on the CL runner.&quot;</em></li>
<li>Editor Body
<ul>
<li>Intro Caption — the plays-CLSF line, with <code>PlayClFile(&quot;…&quot;)</code> set in code</li>
<li><strong>Rapid feedrate (assumed)</strong> Numeric Field — suffixed <code>mm/min</code>, minimum 1</li>
<li><strong>Rotary rapid feedrate (assumed)</strong> Numeric Field — suffixed <code>deg/min</code>, minimum 1</li>
<li><strong>Prefer Tool House on LOAD/TOOL</strong> Toggle</li>
<li>Policy Caption — the <em>On: … Off: …</em> explanation, always shown</li>
<li><strong>Excluded record words</strong> Chip Field — free-text entry with no dropdown list, hint <em>Record
words consumed silently as intentional skips (case-insensitive). Type + Enter to add.</em></li>
</ul>
</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/useControlTreeHost.ts</code> — the build rule that mints the two
nodes and where in the equipment group they sit, the checkbox watch that steps the selection off a
node losing its box before the rebuild, and the route-adoption guard that refuses an id the built
tree lacks.</li>
<li><code>wwwroot-src/src/components/controlTree/runnerSuitItemTypes.ts</code> — the two item types and the panels
they bind to; its own docblock names the usage evidence and the persisted selection as reveal
conditions, which the host's build rule does not.</li>
<li><code>wwwroot-src/src/components/controlTree/CsvRunnerPanel.vue</code> — the column-tag form: the two guards,
the field list with its labels and hints, the blur-and-Enter commit with its equality guard against
the fetched mirror, and the single-field payload.</li>
<li><code>wwwroot-src/src/components/controlTree/ClRunnerPanel.vue</code> — the CLSF form: the two rapid rates,
the cleared and non-finite values it drops before the request, the equality guard on each rate and
on the tool-house toggle, and the unguarded whole-list chip write.</li>
<li><code>wwwroot-src/src/composables/useViewPrefs.ts</code> — the device-local browser-storage record behind both
checkboxes: their default, their persistence, and the record they share with the tree's expansion
and last-selection memory.</li>
<li><code>wwwroot-src/src/components/AppMenuBar.vue</code> — the Preference dropdown's two checkboxes, and the
caption refresh that fetches both snapshots as the menu opens.</li>
<li><code>wwwroot-src/src/api/csvRunner.ts</code> and <code>wwwroot-src/src/api/clRunner.ts</code> — the two snapshot shapes,
their pre-fetch empty values, and the typed wrappers over each REST surface.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the plain-JSON read that inspects no envelope, beside the envelope
write that throws a <code>success: false</code> message.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the field behind the two rapid rates: commit
on blur or Enter, and the minimum that reports inline without emitting.</li>
<li><code>wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue</code> — the editor row: the remount key
that refetches when the selection moves, and the select-an-item hint a refused deep link leaves
behind.</li>
<li><code>wwwroot-src/src/components/controlTree/GroupInfoPanel.vue</code> — the General Setup group's editor,
whose clickable child list gains and loses these two rows with the tree.</li>
<li><code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> — the registry the two item types are spread
into, and the panel lookup the editor row resolves through.</li>
<li><code>wwwroot-src/src/router/treeRoutes.ts</code> — the id migration applied to a <code>?tree=</code> value before the
reveal test reads it, and the page roots a foreign id is redirected by.</li>
<li><code>wwwroot-src/src/pages/GeneralSetupPage.vue</code> — the route that creates the equipment-scoped tree host
and calls its initialization.</li>
<li><code>wwwroot-src/src/layouts/MainLayout.vue</code> — the project epoch that rebuilds the page when a project
loads, which is what lets a link opened against an empty application land afterwards.</li>
<li><code>wwwroot-src/src/i18n/en/tree.ts</code> — the two node labels, both panels' intros, empty lines, field
labels, hints and error contexts.</li>
<li><code>wwwroot-src/src/i18n/en/menu.ts</code> — the two checkbox labels and the two usage-caption strings.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared &ldquo;No project loaded.&rdquo; line both panels open with.</li>
<li><code>Mech/CsvRunnerController.cs</code> — the CSV suit's REST surface: the snapshot, the write that applies
only the fields the payload carries, and the success envelope a missing config is reported in.</li>
<li><code>Mech/ClRunnerController.cs</code> — the CL suit's REST surface: the same envelope, the positivity checks
on the two rates, and the trim-and-replace of the excluded-word list.</li>
<li><code>Mech/NcSuitUsage.cs</code> — the usage scan behind the two captions: the mission walk that reads through
a command's enable wrapper, the two script substrings, the extension detection for an Auto Program
File, and the CL-only chain test.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — the two resident suits as property-initialized members,
their load paths with the fall back to a fresh pipeline, and the migration of the older flat CSV
column element into the suit.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the re-bind that wires both suits to the host
alongside the brand suit, and the chain walk that clears a generic axis configuration before
stamping the chain's axes onto it.</li>
<li><code>HiNc/MachiningProcs/SessionShell.cs</code> — the script verbs whose call text the caption's evidence
scan matches: the two play verbs, and the two CSV sensor-mapping verbs that end in the same
substring.</li>
<li><code>HiNc/SessionCommands/NcFileCommand.cs</code> — the Program File command whose kind the scan reads, and
its Auto default.</li>
<li><code>HiNc/SessionCommands/EnablingWrapper.cs</code> — the enable wrapper the scan walks through without
reading, which is why a switched-off command still counts as evidence.</li>
<li><code>HiMech/MachiningProcs/NcKind.cs</code> — the kind enum and the closed extension sets behind the Auto
detection.</li>
<li><code>HiMech/Numerical/CsvParsers/GeneralCsvRunner.cs</code> — the factory that assembles the suit's runner as
the CSV pipeline, and the place its column configuration sits in that pipeline.</li>
<li><code>HiMech/Numerical/CsvParsers/CsvRunnerConfig.cs</code> — the eleven tags, their serialization, and the
constants they default to.</li>
<li><code>HiMech/Mapping/MappingUtil.cs</code> — those constants.</li>
<li><code>HiMech/Numerical/CsvParsers/CsvSegmenter.cs</code> — the header row the tags are matched against, and
the quote-then-whitespace trim applied to each title.</li>
<li><code>HiMech/Numerical/CsvParsers/RowToSyntaxs/RowToMachineCoordinateSyntax.cs</code> — the six axis names the
machine-coordinate prefix is composed with, and the consumption of those columns whether or not
they resolved.</li>
<li><code>HiMech/Numerical/CsvParsers/CsvSemantics/CsvTimingUtil.cs</code> — the duration parse, the actual-time
fallback and the one-minute clamp.</li>
<li><code>HiMech/Numerical/CsvParsers/CsvSemantics/CsvActDataSemantic.cs</code> — the residual columns carried onto
the step, which is where a column no tag names ends up.</li>
<li><code>HiMech/Numerical/ClsfParsers/NxClRunner.cs</code> — the factory that assembles the CLSF pipeline, and
the configuration's place in it.</li>
<li><code>HiMech/Numerical/ClsfParsers/ClsfRunnerConfig.cs</code> — the four values, their defaults, and the
axis-uniform rapid-rate provider the machine-coordinate motion semantics read.</li>
<li><code>HiMech/Numerical/ClsfParsers/ClMotionValveSemantic.cs</code> — the router that sends a block carrying
machine coordinates to the semantics reused from the NC pipeline and every other block to the
pure-CL one, which is what decides whether the rotary rate is read at all.</li>
<li><code>HiMech/Numerical/ClsfParsers/ClMotionSemantic.cs</code> — the pure-CL motion semantic, timing a rapid
from the linear rate taken directly off the configuration.</li>
<li><code>HiMech/Numerical/ClsfParsers/ClsfRecordCleanupSyntax.cs</code> — the excluded-word match, and the
validation warning it replaces with a silent consume.</li>
<li><code>HiMech/Numerical/ClsfParsers/ClsfToolBuildSemantic.cs</code> — the tool-house policy: the entry that
wins, the file's tool data that builds a missing one, and the error when neither answers.</li>
<li><code>HiMech/Milling/ClMillingDevice.cs</code> — the chain type that is CL usage evidence on its own.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="index.html">General Setup Page</a> — the page whose tree these two nodes are minted into, and the
equipment items they sit after</li>
<li><a class="xref" href="../shell/preference-menu.html">Preference Menu Dropdown</a> — the dropdown holding the two checkboxes and their usage captions,
and the other per-user settings beside them</li>
<li><a class="xref" href="controller/brand-matrix.html">Brand Matrix</a> — the other kind of missing node: which leaf the brand branch
grows on which brand, and the snapshot flag behind each one</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,150 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>General Setup Page | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="General Setup Page | 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-GeneralSetup">
<h1 id="general-setup-page">General Setup Page</h1>
<p>General Setup is the equipment page at <code>/general-setup</code>. It is one of the two Control-Tree pages:
the left dock names every piece of equipment in the project and edits whatever is selected, and the
canvas on the right shows all of it at once. Selection rides the URL as <code>?tree=equipment/…</code>, so any
branch of it is a link. The tree's one root is a pure Group stem at <code>equipment</code>, selectable in its
own right: it renders an orientation panel listing the equipment items beneath it.</p>
<p>The page renders three columns in nested splitters — the Control-Tree dock, the content column, and
the equipment canvas. The dock carries two stacked rows: the tree above, and below it the editor
panel of whichever node is selected. The content column holds a second, wider view of the same
selection, mounted only for the item types that register one — on this page the spindle branch and
nothing else — while every other selection leaves it showing its empty hint. Column widths are
device-local, and the dock aligns with the Execution page's so the two tree pages feel like one
application.</p>
<p>Ordered as the page builds its equipment children.</p>
<h2 id="pages">Pages</h2>
<ul>
<li><a href="machine-tool.html">Machine Tool</a> — The kinematic chain the whole scene hangs from, and the standalone route that loads and previews it</li>
<li><a href="spindle-capability.html">Spindle Capability</a> — The spindle envelope: thermal condition, gear shift, dry run, power and torque</li>
<li><a href="background-coolant.html">Background / Coolant</a> — The two scene branches that are not machine parts: the backdrop and the coolant model</li>
<li><a href="fixture.html">Fixture</a> — What holds the workpiece, its geometry and the two anchors that place it</li>
<li><a href="workpiece.html">Workpiece</a> — Raw and target geometry, the anchors that locate them, the mesh, and the material</li>
<li><a href="controller/index.html">Controller</a> — The SoftNcRunner-native controller branch: its two planes, the brand matrix behind which nodes appear at all, and the contract every leaf edits by</li>
<li><a href="hidden-controllers.html">Hidden Controller Branches</a> — The two runner-suit nodes a fresh installation never builds, the device-local checkboxes that reveal them, and the second kind of invisibility a link cannot undo</li>
</ul>
<p>The superseded <a class="xref" href="../legacy-controller.html">Legacy Controller</a> screen at its own route is a separate
surface, editing a different model from the branch above.</p>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../execution/index.html">Execution Page</a> — the other Control-Tree page, and the one this was split out of</li>
<li><a class="xref" href="../tool-house/index.html">Tool House Page</a> — the third equipment surface, a tab page rather than a tree page</li>
<li><a class="xref" href="../shell/main-panel.html">Main Panel</a> — the shell whose Page menu reaches this route</li>
<li><a class="xref" href="../platform/control-tree.html">Control Tree</a> — the engine behind this page's tree: how a branch is built, rebuilt and selected</li>
<li><a href="controller/index.html">Controller Branch</a> — the branch this page hosts whose node set changes with the controller brand</li>
<li><a href="hidden-controllers.html">Hidden Controller Branches</a> — the two equipment nodes this page withholds until a preference or a link asks for them</li>
<li><a class="xref" href="../legacy-controller.html">Legacy Controller</a> — the superseded controller screen at its own route, and the three settings only it edits</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,326 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Machine Tool | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Machine Tool | 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-MachineTool">
<h1 id="machine-tool">Machine Tool</h1>
<p>The machine tool is the project's kinematic chain — the linkage between the machine table and the
tool spindle. It has three surfaces:</p>
<ul>
<li>the <code>equipment/machine-tool</code> branch of the General Setup page's Control Tree
(<code>/general-setup?tree=equipment/machine-tool</code>), the first equipment child of the General Setup
group and a <strong>leaf</strong>: chain structure belongs to the
<a class="xref" href="../util/mech-builder.html">Mechanism Builder Page</a>, so the root carries no child slots;</li>
<li>the <code>/machine-tool</code> route, a standalone surface pairing a chain-only canvas with a load button
and a GUI / XML view of what is loaded. Its fields are read-only, but the route is not: its Load
installs the picked chain file onto the project. It carries no Page-menu entry and is reached by
URL;</li>
<li>in the WPF client, a sub-window opened from the <a class="xref" href="../shell/main-panel.html">Main Panel</a>.</li>
</ul>
<p>All three edit the one chain the project owns.</p>
<ul>
<li>Key Model:
<ul>
<li><a class="xref" href="../../api/Hi.Mech.IMachiningChain.html">IMachiningChain</a>
On the web the chain is addressed by an IndexService key handed to the
<a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> and installed back onto
the project through the controller's Update. On the WPF client the same button is wired with a
TargetObjectGetter / TargetObjectSetter pair (see that page for the design pattern).</li>
</ul>
</li>
<li>Assistant Model:
<ul>
<li><a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html">LocalProjectService</a> — owns the chain as the
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningChain">MachiningChain</a> /
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningChainFile">MachiningChainFile</a> pair.</li>
<li><a class="xref" href="../../api/Hi.MachiningProcs.MachiningProject.html">MachiningProject</a></li>
</ul>
</li>
</ul>
<h2 id="layout">Layout</h2>
<h3 id="control-tree-branch">Control Tree Branch</h3>
<p>The root panel is rendered inline by the dock's primary editor pane, so it has no title label of its
own — the wrapping expansion header carries the selection breadcrumb.</p>
<ul>
<li>Machine Tool Root Panel
<ul>
<li><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a>
<ul>
<li>New Items: one entry, <code>New ClMillingDevice</code> (&ldquo;CL-driven blank device&rdquo;). Its parameterless form
is the complete CL-driven device; any other chain type arrives by Load, Paste or XML.</li>
<li>Load / Save As / Copy / Paste / XML, over the project directory and the admin directory.</li>
<li>File extensions are <code>.MachineTool</code> and <code>.mt</code>, so the Load / Save As filter reads
<code>*.MachineTool / *.mt / *.xml</code>; the load type is <a class="xref" href="../../api/Hi.Mech.IMachiningChain.html">IMachiningChain</a>.</li>
</ul>
</li>
<li>Type Badge — the chain's runtime type name, <code>none</code> when no chain is attached.</li>
<li>Read-only caption lines, shown once a chain is attached:
<ul>
<li><code>Name:</code> — <a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Name">Name</a></li>
<li><code>Note:</code> — <a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Note">Note</a>, when non-empty</li>
<li><code>File:</code> — the chain's project-relative file, when it has one</li>
</ul>
</li>
<li>Intro caption, and an empty-state caption while no project is open.</li>
</ul>
</li>
<li>Shared slave view: the General Setup equipment canvas, whose Solid group's <strong>Machine</strong> flag draws
this chain (see <a class="xref" href="index.html">General Setup Page</a>). <strong>That flag starts cleared</strong>, so
the shared canvas shows a correctly attached chain only after it is ticked; the <code>/machine-tool</code>
route's own canvas has no such flag and always draws it.</li>
</ul>
<h3 id="machine-tool-route"><code>/machine-tool</code> Route</h3>
<p>Load and show only: no Save As, no ReLoad, no name editing.</p>
<ul>
<li>Header Row
<ul>
<li>Title Label &ldquo;Machine Tool&rdquo;</li>
<li><code>[relFile]</code> caption — the loaded chain's project-relative path, when it has one.</li>
<li>Folder-icon Load Button — opens the server-side file explorer dialog, restricted to the
<code>ProjectDirectory</code> and <code>ResourceDir</code> roots and filtered to <code>Machine Tool (.MachineTool|.mt)</code>.
Disabled until a project is open.</li>
<li>Read-only Name Field, shown once a chain is loaded.</li>
<li>GUI / XML View Toggle, disabled until a chain is loaded.</li>
</ul>
</li>
<li>Body — two columns
<ul>
<li>Left: Identity Card or XML Source
<ul>
<li>GUI mode — a read-only Name input, a read-only auto-grow Note textarea, and a Type chip.</li>
<li>XML mode — the chain serialised to XML, badged <code>read-only</code>, with a refresh button. The text is
fetched lazily on the first switch to XML and re-fetched after a load while XML mode is open.</li>
</ul>
</li>
<li>Right: Viewer Panel
<ul>
<li><a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a> and a connection badge.</li>
<li>RenderingCanvas, cache id <code>MachineToolCanvas</code> — this route's own canvas.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="wpf-page">WPF Page</h3>
<ul>
<li>Head Line
<ul>
<li><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a>
<ul>
<li>file extension is <code>mt</code>; the load type is <a class="xref" href="../../api/Hi.Mech.IMachiningChain.html">IMachiningChain</a>.</li>
<li>The pointed Editor Panel is Management Panel — a ContentPresenter the button swaps between the
built GUI panel and an AvalonEdit text editor for XML mode.</li>
</ul>
</li>
<li>Title Label &ldquo;Machine Tool&rdquo;</li>
</ul>
</li>
<li>Management Panel
<ul>
<li>If the key model inherits <a class="xref" href="../../api/Hi.Common.INameNote.html">INameNote</a>:
<ul>
<li>Name Setting Line
<ul>
<li>Name Label</li>
<li>Name TextField — its TextChanged writes straight onto the chain.</li>
</ul>
</li>
<li>Note Setting Line
<ul>
<li>Note Label</li>
<li>Note TextField — multi-line, same write-through.</li>
</ul>
</li>
</ul>
</li>
<li>Type Line — italic <code>Type: {chain type}</code>.</li>
<li>Empty-state label when no chain is loaded.</li>
</ul>
</li>
</ul>
<h2 id="behavior">Behavior</h2>
<ul>
<li>Load / Paste / XML-Apply only swap the IndexService entry. The tree root then installs the result
onto <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningChain">MachiningChain</a> — the setter re-attaches the
fixture and workpiece buckles and wires the runtime hooks itself — re-mints the key and rebuilds
the branch. The button's XML dialog re-emits its object-loaded event on Apply, so that install and
refresh chain runs exactly once.</li>
<li>A chain the file describes wrongly is refused, and which of the two steps refuses it depends on
the file. A machine tool file asking for its collision pairs to be generated is walked from the
ground anchor to each end while it is being read, so a chain carrying no anchor named exactly <code>t</code>
or exactly <code>w</code> never reaches the IndexService at all: Object Management's Load answers
<code>Load failed: This kinematic chain has no worktable-end anchor named 'w'.</code> A file that lists its
collision pairs instead is read without that walk and does reach the index; the install that
follows builds the kinematics solver, which requires the same two anchors, so the same sentence
arrives one step later as <code>Install machine tool: …</code> and the project keeps the chain it had. The
shipped machine tool files all ask for generated pairs, so the first form is the one usually met.
The worktable end is checked before the tool end, so a chain missing both names only <code>'w'</code> until
that one is fixed. The <code>/machine-tool</code> route runs both steps inside its single call, so either
refusal reaches it as <code>Load machine tool: …</code>, and — like the no-project refusal behind the
disabled folder button — leaves the loaded chain untouched.</li>
<li>The IndexService key is minted even on a project with no chain attached, so Load and Paste stay
usable from an empty root.</li>
<li>The <code>/machine-tool</code> route's own Load takes the shorter path: one call materialises the picked file
server-side and assigns the chain to <a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningChain">MachiningChain</a>
in the same request, with no IndexService swap in between. It re-points
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningChainFile">MachiningChainFile</a> too — project-relative when the
picked file sits under the project directory, and the path relative to the chosen root otherwise.
It is refused while no project is open, which is why the folder button is disabled there.</li>
<li><code>New ClMillingDevice</code> builds and installs the blank chain in one server call and clears the
project's chain-file reference, so a later project save embeds the fresh chain inline instead of
overwriting the previous chain's file.</li>
<li>When the replacement came from a server-file Load, the install also re-points
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_MachiningChainFile">MachiningChainFile</a>: project-relative when the file
sits under the project directory, resource-root-relative when it sits under the resource root,
absolute otherwise. An absolute path there would make every later project save rewrite the shared
resource file in place instead of copying it into the project. Paste and XML applies leave the
file reference unchanged.</li>
<li>Both canvases draw the live model, so a replacement shows on the next frame with no further calls.
The <code>/machine-tool</code> canvas snaps to the isometric view when it is bound and again after each load;
the shared equipment canvas snaps once, when its displayee is bound.</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>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — builds the
<code>equipment/machine-tool</code> node (item type <code>MachineToolRoot</code>, keyed on the IndexService chain key)
and owns the install / create / rebind handlers.</li>
<li><code>wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue</code> — renders that branch inline: the
Object Management button, its <code>New ClMillingDevice</code> entry, the type badge and the read-only
Name / Note / File captions.</li>
<li><code>wwwroot-src/src/components/widgets/ObjectManagementMenuButton.vue</code> — the reused object-management
dropdown, keyed entirely on its <code>modelKey</code> (see
<a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a>).</li>
<li><code>wwwroot-src/src/components/mech/EquipmentSetupPanel.vue</code> — the shared equipment canvas the tree
root's chain is drawn on.</li>
<li><code>wwwroot-src/src/pages/MachineToolPage.vue</code> — the <code>/machine-tool</code> route.</li>
<li><code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code> — the server-side file picker that
route opens, seeded to the resource root's <code>MachineTool</code> folder when nothing is loaded and to the
current file's directory otherwise; it emits <code>&quot;{rootName}:{relativePath}&quot;</code> selection keys.</li>
<li><code>wwwroot-src/src/api/machineTool.ts</code> — typed client for <code>/api/mech/machine-tool/*</code>: snapshot,
initialize, update, create, load, XML, plus the display initialize and reset-view calls. Shared by
the route page and the tree root.</li>
<li><code>wwwroot-src/src/router/routes.ts</code> — declares the <code>machine-tool</code> route.</li>
<li><code>wwwroot-src/src/components/AppMenuBar.vue</code> — the Page menu, which lists General Setup but not
<code>/machine-tool</code>.</li>
<li><code>Mech/MachineToolController.cs</code> — REST surface at <code>/api/mech/machine-tool</code>: the snapshot DTO,
<code>initialize</code> / <code>update</code> / <code>create</code> / <code>load</code> and the XML read. Only <code>ClMillingDevice</code> is offered
for blank creation.</li>
<li><code>Mech/MachineToolDisplayController.cs</code> — <code>/api/mech/machine-tool/display</code>: binds the project's
chain onto a rendering connection's DispEngine through a delegating displayee, and serves the
isometric reset-view the route page calls after a load.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../util/mech-builder.html">Mechanism Builder Page</a> — the user-scoped editor for the same anchor topology</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,328 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Spindle Capability | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Spindle Capability | 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-SpindleCapabilityPage">
<h1 id="spindle-capability">Spindle Capability</h1>
<p>The Spindle Capability editor is the <code>equipment/spindle</code> branch of the General Setup page's Control
Tree (<code>/general-setup?tree=equipment/spindle</code>), the equipment child after Machine Tool. Two older
paths redirect onto it: <code>/spindle-capability/:tab?</code>, whose tab segment maps straight onto one of the
branch's five children, and <code>/equipment/spindle</code>. The menu bar carries no entry of its own for it —
the Page menu reaches General Setup, and the tree reaches the branch.</p>
<p>It edits <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html">SpindleCapability</a> on the project's authored equipment face,
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html">SetupEquipment</a>, reached as
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_SetupEquipment">SetupEquipment</a>. It exposes the metadata (name, note),
the thermal scalars (energy efficiency, working-temperature ceiling), the gear-shift spindle speed,
the two dry-run coefficients, and the power / torque contour lists.</p>
<ul>
<li>Key Model: <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html">SpindleCapability</a></li>
<li>Assistant Model:
<ul>
<li><a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html">SetupEquipment</a> — owns the capability as
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_SpindleCapability">SpindleCapability</a> and its optional
side-file reference
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_SpindleCapabilityFile">SpindleCapabilityFile</a>.</li>
<li><a class="xref" href="../../api/Hi.MachiningProcs.MachiningProject.html">MachiningProject</a> — carries that face across the <code>.hincproj</code> save.</li>
</ul>
</li>
</ul>
<div class="NOTE">
<h5>Note</h5>
<p>The WPF desktop app has no spindle-capability surface: no page, no panel, and no handler for the
<code>.SpindleCapability</code> extension. There, the value is whatever the project XML carries on the
equipment face, or the class defaults when the XML says nothing. The <code>.SpindleCapability</code> object
management menu ships on the web only, on this branch's root panel.</p>
</div>
<h2 id="layout">Layout</h2>
<p>The branch is one root plus five children, six selectable nodes in all. Each node's editor occupies
the dock's PRIMARY row, and all six — the root included — register the <em>same</em> CONTENT-column view,
the two contour charts, so the charts stay mounted while the operator walks the branch.</p>
<h3 id="control-tree-branch">Control Tree Branch</h3>
<ul>
<li>Spindle Capability Root Panel — <code>equipment/spindle</code>, item type <code>SpindleCapabilityRoot</code>
<ul>
<li><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> — Load / Save As /
Copy / Paste / XML over file extension <code>.SpindleCapability</code>, load type
<code>Hi.Milling.SpindleCapability, HiMech</code>, rel file <code>SpindleCapability.xml</code>, based at the project
directory. This is the whole file surface of the editor.</li>
<li>Caption — the capability's name, or a &ldquo;no capability&rdquo; note.</li>
<li>Empty state, while nothing is attached: an hourglass, a &ldquo;not attached to project&rdquo; line and a
hint pointing at the ⋮ menu's Load entry.</li>
<li>Name TextField — <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_Name">Name</a>.</li>
<li>Note TextField — <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_Note">Note</a>.</li>
</ul>
</li>
<li><code>equipment/spindle/thermal</code> — &ldquo;Thermal / Energy&rdquo;, item type <code>SpindleScalars</code>
<ul>
<li>Energy Efficiency NumberField — <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_EnergyEfficiency">EnergyEfficiency</a>, clamped to
0 – 1.</li>
<li>Working Temperature Upper Boundary NumberField (°C) —
<a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_WorkingTemperatureUpperBoundary_C">WorkingTemperatureUpperBoundary_C</a>.</li>
</ul>
</li>
<li><code>equipment/spindle/gear-shift</code> — &ldquo;Gear Shift&rdquo;, item type <code>SpindleScalars</code>
<ul>
<li>Has-Gear-Shift CheckBox — flips
<a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_GearShiftSpindleSpeed_rpm">GearShiftSpindleSpeed_rpm</a> between <code>null</code> (no mechanism) and
<code>0</code> (mechanism present; the operator dials in the speed).</li>
<li>Gear Shift Spindle Speed NumberField (rpm) — always rendered, disabled until the checkbox is on.</li>
</ul>
</li>
<li><code>equipment/spindle/dry-run</code> — &ldquo;Dry-Run Coefficients&rdquo;, item type <code>SpindleScalars</code>
<ul>
<li>Friction Power Coefficient NumberField (mW/rpm) —
<a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_DryRunFrictionPowerCoefficient_mWdrpm">DryRunFrictionPowerCoefficient_mWdrpm</a>.</li>
<li>Windage Power Coefficient NumberField (pW/rpm³) —
<a class="xref" href="../../api/Hi.Milling.SpindleCapability.html#Hi_Milling_SpindleCapability_DryRunWindagePowerCoefficient_pWdrpm3">DryRunWindagePowerCoefficient_pWdrpm3</a>.</li>
</ul>
</li>
<li><code>equipment/spindle/power</code> — &ldquo;Power Contours&rdquo;, item type <code>SpindleContour</code>
<ul>
<li>Count line and an <strong>Add</strong> button.</li>
<li>Contour Selector — a select over the contour list, each entry labelled by its workable duration
(<code>n min</code>, or Continuous for the infinite one).</li>
<li>Edit-duration Button — opens the modal below.</li>
<li>Delete Button — confirms, then removes the selected contour. Disabled at one remaining contour.</li>
<li>Points Table — the selected contour's points, one row per point: a spindle-speed cell (rpm,
<code>min: 0</code>), a value cell, an insert-next button that clones the row, and a double-click remove
that is disabled at one remaining point. A cell edit replaces the whole point list server-side.</li>
<li>Empty state, when the axis has no contour yet.</li>
<li>Edit-duration Modal
<ul>
<li>Continuous (∞) CheckBox — disabled when another contour already holds the continuous key.</li>
<li>Workable Duration NumberField (min), shown while Continuous is off, <code>min: 0</code>.</li>
<li>Save rejects a non-positive duration and is a no-op when the key is unchanged.</li>
</ul>
</li>
</ul>
</li>
<li><code>equipment/spindle/torque</code> — &ldquo;Torque Contours&rdquo;, item type <code>SpindleContour</code>, the same shape over
the torque list.</li>
<li>Shared content view, registered by the root and by all five children: the two contour charts in
the General Setup page's CONTENT column.
<ul>
<li>Power chart and Torque chart, each with a chip legend — one chip per contour, showing its
duration and, on hover, its point count. Clicking a chip selects the contour the PRIMARY-row
editor edits.</li>
<li>A vertical marker line at the gear-shift spindle speed, while one is set.</li>
<li>Its own not-attached empty state, mirroring the root panel's.</li>
</ul>
</li>
</ul>
<h2 id="behavior">Behavior</h2>
<ul>
<li><strong>Rpm ↔ cycles/s conversion lives server-side.</strong> <a class="xref" href="../../api/Hi.Milling.SpindleCapability.html">SpindleCapability</a> stores
spindle speed in cycles/s (Hz); the controller converts to rpm in its DTOs and back on write. The
frontend only ever sees rpm.</li>
<li><strong>Nullable gear shift.</strong> The Has-Gear-Shift checkbox writes <code>0</code> when checked and <code>null</code> when
cleared, which is how the model distinguishes &ldquo;mechanism present, speed not yet dialled in&rdquo; from
&ldquo;no gear-shift mechanism&rdquo;.</li>
<li><strong>One state, thin views throughout.</strong> Every panel on the branch — root, the three scalar
sections, the two contour sections and the charts — reads and writes one module-singleton
composable. A point edit therefore redraws the chart in the same tick, and a legend click moves
the editor. The composable installs a single project-change watch that reloads on open and clears
on close.</li>
<li><strong>No cache chain.</strong> These scalars feed the cutting-force and thermal physics, not the setup
canvas, so the branch's afterChange does nothing — unlike the geometry branches, nothing here
clears a geometry cache.</li>
<li><strong>Add Contour.</strong> The prompt is pre-filled with <code>60</code>; a blank entry means continuous (∞). A
duration already present is refused, a second continuous contour included. The new contour is
seeded from the currently selected contour, falling back to the first in the list, and to a
built-in default curve when the axis is empty — so the operator rarely draws from scratch.</li>
<li><strong>Project-scoped, and forwarded to the run.</strong> The capability lives on the authored equipment face
and is persisted with the <code>.hincproj</code> save; setting
<a class="xref" href="../../api/Hi.Machining.MachiningEquipmentUtils.SetupEquipment.html#Hi_Machining_MachiningEquipmentUtils_SetupEquipment_SpindleCapabilityFile">SpindleCapabilityFile</a> externalizes it
to a side-file instead. The runtime equipment face holds the same object by reference, so a scalar
or point edit is visible to a run immediately; a <em>replacement</em> (Load, Paste, XML Apply) re-stamps
that reference through the project service's ForwardSetupEnvironmentToExecution.</li>
<li><strong>Load / Paste / XML-Apply only swap the IndexService entry.</strong> The root panel then installs the
result onto the equipment through the controller's <code>update</code>, and reloads the snapshot and the key.
The controller's <code>initialize</code> indexes a blank placeholder when nothing is attached yet, which is
what keeps the ⋮ menu usable — and Load reachable — on a project with no capability.</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/useControlTreeHost.ts</code> — builds the <code>equipment/spindle</code>
root (item type <code>SpindleCapabilityRoot</code>) and its five children: thermal, gear-shift and dry-run as
<code>SpindleScalars</code>, power and torque as <code>SpindleContour</code>.</li>
<li><code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> — registers those three item types and gives
all of them the same CONTENT-column view.</li>
<li><code>wwwroot-src/src/components/controlTree/SpindleCapabilityPanel.vue</code> — the branch root panel: the
Object Management button, the name caption, the not-attached empty state, and the Name / Note
fields.</li>
<li><code>wwwroot-src/src/components/controlTree/SpindleScalarsPanel.vue</code> — one scalar section per role
path: thermal, gear-shift (checkbox plus the rpm field, disabled while the value is null) and
dry-run.</li>
<li><code>wwwroot-src/src/components/controlTree/SpindleContourPanel.vue</code> — the power / torque child: the
count line, Add, the contour selector, the edit-duration modal and the delete button, hosting the
point editor.</li>
<li><code>wwwroot-src/src/components/spindle/SpindleContourEditor.vue</code> — the selected contour's numeric
points table; its own header is hidden when the tree panel drives it.</li>
<li><code>wwwroot-src/src/components/spindle/SpindleContoursPanel.vue</code> — the CONTENT-column view every
spindle item registers: both charts.</li>
<li><code>wwwroot-src/src/components/spindle/SpindleContoursChart.vue</code> — one plotted chart with the
clickable chip legend that selects the contour the editor edits, plus the gear-shift marker line.</li>
<li><code>wwwroot-src/src/components/controlTree/ContentSlavePanel.vue</code> — the General Setup page's CONTENT
column, which mounts whichever content panel the selection's item type registers; it deliberately
leaves that component unkeyed so the charts survive a move between the branch's six nodes.</li>
<li><code>wwwroot-src/src/composables/useSpindleCapability.ts</code> — the module-singleton state and every
mutation on the branch: the scalar handlers, add / remove / rename contour, the point updates and
the object-management install hooks.</li>
<li><code>wwwroot-src/src/components/widgets/ObjectManagementMenuButton.vue</code> — the reused ⋮ menu carrying
Load / Save As / Copy / Paste / XML for the <code>.SpindleCapability</code> file surface.</li>
<li><code>wwwroot-src/src/api/spindleCapability.ts</code> — typed client for <code>/api/mech/spindle-capability/*</code>.</li>
<li><code>wwwroot-src/src/router/routes.ts</code> — the <code>spindle-capability/:tab?</code> redirect (the tab mapped onto
the branch child id) and the <code>equipment/spindle</code> redirect, both landing on <code>general-setup?tree=…</code>.</li>
<li><code>wwwroot-src/src/router/treeRoutes.ts</code> — defines the tab segments that redirect accepts: thermal,
gear-shift, dry-run, power, torque.</li>
<li><code>Mech/SpindleCapabilityController.cs</code> — REST surface at <code>/api/mech/spindle-capability</code> over the
authored equipment face's capability:
<ul>
<li><code>GET /</code> — flat snapshot: metadata plus the contour keys of each axis.</li>
<li><code>GET /power-contours</code>, <code>GET /torque-contours</code> — per-axis contour lists with their points.</li>
<li><code>PUT /name | /note | /energy-efficiency | /working-temperature-upper-boundary-c | /gear-shift-spindle-speed-rpm | /dry-run-friction-power-coefficient-mwdrpm | /dry-run-windage-power-coefficient-pwdrpm3</code> — one scalar each.</li>
<li><code>POST /power-contours | /torque-contours</code> — add one contour.</li>
<li><code>DELETE /power-contours/{key} | /torque-contours/{key}</code> — remove one contour (<code>{key}</code> accepts
numeric strings and <code>&quot;inf&quot;</code> / <code>&quot;infinity&quot;</code>).</li>
<li><code>PUT /power-contours/{key}/points | /torque-contours/{key}/points</code> — replace one contour's
points; this is what backs per-point editing.</li>
<li><code>PUT /power-contours/{key}/key | /torque-contours/{key}/key</code> — rename a contour's
workable-duration key, refusing a key that already exists; this is what backs the edit-duration
modal.</li>
<li><code>GET /xml</code> — serialise to XML text.</li>
<li><code>POST /load</code>, <code>POST /load-file</code>, <code>POST /reload</code> — install from XML text, from a file under a
named root, or from the file already stamped on the equipment.</li>
<li><code>PUT /capability-file</code> — set the side-file reference without reloading.</li>
<li><code>POST /initialize</code>, <code>POST /update</code> — index the capability for the Object Management button, and
install the swapped-in object back onto the equipment.</li>
</ul>
</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiMech/Milling/SpindleCapability.cs</code> — the model: EnergyEfficiency,
WorkingTemperatureUpperBoundary_K with its <code>_C</code> accessor, the nullable GearShiftSpindleSpeed_cycleds
with its <code>_rpm</code> convenience property, the two dry-run coefficients and the power / torque contour
dictionaries keyed by workable duration.</li>
<li><code>HiMech/Machining/MachiningEquipmentUtils/SetupEquipment.cs</code> — the authored equipment face that
owns the capability and its file reference and serializes them, inline or as a side-file
reference.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../util/mech-builder.html">Mechanism Builder Page</a> — same file-level IO pattern (Load / Reload / Save As) but user-scoped rather than project-scoped.</li>
<li><a class="xref" href="background-coolant.html">Background / Coolant</a> — sibling branch of the same Control Tree, editing the same authored equipment face.</li>
<li><a class="xref" href="../../technique/machine-capability/spindle-capability.html">Spindle Capability</a> — what the model represents physically and how the per-step ratios come out of it.</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="machine-tool.html" name="" title="Machine Tool">Machine Tool</a>
</li>
<li>
<a href="spindle-capability.html" name="" title="Spindle Capability">Spindle Capability</a>
</li>
<li>
<a href="background-coolant.html" name="" title="Background / Coolant">Background / Coolant</a>
</li>
<li>
<a href="fixture.html" name="" title="Fixture">Fixture</a>
</li>
<li>
<a href="workpiece.html" name="" title="Workpiece">Workpiece</a>
</li>
<li>
<a href="controller/index.html" name="controller/toc.html" title="Controller">Controller</a>
</li>
<li>
<a href="hidden-controllers.html" name="" title="Hidden Controller Branches">Hidden Controller Branches</a>
</li>
</ul>
</div>
</div>
</div>
</div>
@@ -0,0 +1,2 @@
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"Machine Tool","href":"machine-tool.html","topicHref":"machine-tool.html"},{"name":"Spindle Capability","href":"spindle-capability.html","topicHref":"spindle-capability.html"},{"name":"Background / Coolant","href":"background-coolant.html","topicHref":"background-coolant.html"},{"name":"Fixture","href":"fixture.html","topicHref":"fixture.html"},{"name":"Workpiece","href":"workpiece.html","topicHref":"workpiece.html"},{"name":"Controller","href":"controller/index.html","tocHref":"controller/toc.html","topicHref":"controller/index.html","homepage":"controller/index.html"},{"name":"Hidden Controller Branches","href":"hidden-controllers.html","topicHref":"hidden-controllers.html"}]}
@@ -0,0 +1,428 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Workpiece | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Workpiece | 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-Workpiece">
<h1 id="workpiece">Workpiece</h1>
<p>The Workpiece editor is the <code>equipment/workpiece</code> branch of the General Setup page's Control Tree (<code>/general-setup?tree=equipment/workpiece</code>); the old <code>/workpiece</code> route redirects there. In the WPF client it is a sub-window opened from the <a class="xref" href="../shell/main-panel.html">Main Panel</a>.</p>
<p>The key model is <a class="xref" href="../../api/Hi.NcMech.Workpieces.Workpiece.html">Workpiece</a>, taken from the Main Panel's
<a class="xref" href="../../api/Hi.MachiningProcs.LocalProjectService.html#Hi_MachiningProcs_LocalProjectService_Workpiece">Workpiece</a>. The cached solids it is drawn from belong to
<a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceService.html">WorkpieceService</a>, which both clients share.</p>
<p>The display config differs by client. The web branch has none of its own: it shares the General Setup
canvas, gated by the equipment-setup config (<code>Disp/EquipmentSetupDisplayeeConfig.cs</code>), which carries
the fixture and workpiece flags together — the fixture and workpiece geometry anchors keep separate
flags, while the fixture↔workpiece buckle pair, being one attached identity at one location, is a
single flag. The WPF page takes its own <a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceEditorDisplayeeConfig.html">WorkpieceEditorDisplayeeConfig</a>
from <code>UserService.UserConfig</code>.</p>
<h2 id="layout">Layout</h2>
<h3 id="control-tree-branch">Control Tree Branch</h3>
<ul>
<li>Workpiece Root — a summary panel, rendered inline by the dock's primary editor pane. It carries no
Object Management button: the workpiece has no standalone <code>.Workpiece</code> file surface here, and its
geometry, anchors, mesh and material are all edited through the child items below.
<ul>
<li>Raw Geometry Type Badge and Target Geometry Type Badge (<code>none</code> when the slot is empty)</li>
<li>Intro caption, and an empty-state caption while the project has no workpiece</li>
</ul>
</li>
<li><code>equipment/workpiece/raw-geometry</code> — &ldquo;Raw Geometry&rdquo;, a Geometry slot backing
<a class="xref" href="../../api/Hi.NcMech.Workpieces.Workpiece.html#Hi_NcMech_Workpieces_Workpiece_InitGeom">InitGeom</a>. The slot's own panel is the kind picker
(<a class="xref" href="../geom/geom-manage-control.html">Geometry Management Control</a>, selector only); the picked kind's
editor is the slot's child item. Kinds offered: <code>Box3d</code>, <code>Cylindroid</code>, <code>StlFile</code>,
<code>TransformationGeom</code>, <code>GeomCombination</code>, <code>CubeTreeFile</code>, plus None. <code>ExtendedCylinder</code> is an
<code>IMakeXmlSource</code> and was offered here until its start section — wired by a host, never serialized
— was found to reload a saved project degenerate; see
<a class="xref" href="../geom/extended-cylinder-panel.html">Extended Cylinder Panel</a> for what an older project holding
one still shows. The source
choice folds into that one picker — picking the voxel <code>CubeTreeFile</code>, which the picker shows as
<code>MeshedGeomFile</code>, <em>is</em> the meshed source (see
<a class="xref" href="../geom/meshed-geom-panel.html">Meshed Geometry Panel</a>).</li>
<li><code>equipment/workpiece/target-geometry</code> — &ldquo;Target Geometry&rdquo;, the same slot shape backing
<a class="xref" href="../../api/Hi.NcMech.Workpieces.Workpiece.html#Hi_NcMech_Workpieces_Workpiece_IdealGeom">IdealGeom</a>. Kinds offered: <code>Box3d</code>, <code>Cylindroid</code>, <code>StlFile</code>,
<code>TransformationGeom</code>, plus None. The target must satisfy <code>IGetStl</code>, so <code>CubeTreeFile</code> is not among
them.</li>
<li><code>equipment/workpiece/anchor</code> — &ldquo;Anchor&rdquo;, a Group stem whose info text states how the workpiece is
placed. Two Transformer slots hang from it, each a kind picker
(<a class="xref" href="../geom/transformer-panel.html">Transformer Manage Panel</a>) over the picked kind's child editor:
<ul>
<li><code>.../anchor/geom-to-fixture</code> — &ldquo;Geom To Fixture&rdquo;, which positions the workpiece geometry on the
fixture's workpiece buckle (see <a class="xref" href="fixture.html">Fixture</a>).</li>
<li><code>.../anchor/geom-to-program-zero</code> — &ldquo;Geom To Program Zero&rdquo;, which places the program zero, the
NC origin, relative to the workpiece geometry.</li>
</ul>
</li>
<li><code>equipment/workpiece/runtime</code> — labelled <strong>Mesh</strong> in the tree.
<ul>
<li>Initial Resolution Select (<a class="xref" href="../../api/Hi.NcMech.Workpieces.Workpiece.html#Hi_NcMech_Workpieces_Workpiece_InitResolution">InitResolution</a>, in mm), a
fifteen-step powers-of-two ladder:
<ul>
<li>0.0009765625</li>
<li>0.001953125</li>
<li>0.00390625</li>
<li>0.0078125</li>
<li>0.015625</li>
<li>0.03125</li>
<li>0.0625</li>
<li>0.125</li>
<li>0.25</li>
<li>0.5</li>
<li>1</li>
<li>2</li>
<li>4</li>
<li>8</li>
<li>16</li>
</ul>
</li>
<li>Hint caption: a smaller resolution means a finer voxel grid and a slower runtime simulation.</li>
<li>The engine's own ladder bottoms out at <code>0.001953125</code>, so the first entry builds the same mesh
as the second. Values typed in from elsewhere are rounded to the next finer rung rather than
used as given — see <a class="xref" href="../../technique/simulation-performance/mesh-resolution.html">Mesh Resolution</a>.</li>
<li>The panel self-gates on the project having a workpiece.</li>
</ul>
</li>
<li><code>equipment/workpiece/material</code> — &ldquo;Material&rdquo;, a Group stem whose info text names the two
pre-prepared resources loaded by reference. Present whenever the project has a workpiece; nothing
on this branch gates it on a physics flag. Its two children share one panel, which reads its role
from the node's own path:
<ul>
<li><code>.../material/workpiece-material</code> — <code>.WorkpieceMaterial</code> files, seeded to the resource root's
<code>WorkpieceMaterial</code> folder.</li>
<li><code>.../material/cutting-parameter</code> — <code>.mp</code> files, seeded to the resource root's
<code>CuttingParameter</code> folder.</li>
<li>Each child is one Resource-only File Path Input — the project-directory Browse entry is
suppressed, leaving the Resource-root pick — plus a readonly Name field
(<a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Name">Name</a>) and a readonly Note field
(<a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Note">Note</a>).</li>
</ul>
</li>
</ul>
<h3 id="page-frame">Page Frame</h3>
<ul>
<li>The General Setup page lays the tree dock, the content column and the equipment canvas out in
nested splitters with device-local widths; inside the dock, a height divider between the Control
Tree row and the primary editor row trades their heights. See
<a class="xref" href="index.html">General Setup Page</a>.</li>
<li>Canvas Column — one RenderingCanvas bound to the equipment-setup displayee
(<code>Disp/EquipmentSetupDisplayee.cs</code>): the merged fixture + workpiece setup scene, with optional
machine and tool solids.
<ul>
<li><a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a></li>
<li>Display Options Menu — six groups; the workpiece's own entries are in <strong>bold</strong>:
<ul>
<li>Solid — Machine, Tool, <strong>Meshed Geometry</strong></li>
<li>Fixture Rendering Mode — Solid / Edge / Hide</li>
<li><strong>Raw Shape Rendering Mode</strong> — Solid / Edge / Hide</li>
<li><strong>Target Shape Rendering Mode</strong> — Solid / Edge / Hide</li>
<li>Anchors — Fixture Geometry Anchor, <strong>Workpiece Buckle</strong> (the one flag for the
fixture↔workpiece pair), Table Buckle, <strong>Workpiece Geometry Anchor</strong>,
<strong>Program-Zero Anchor</strong>, Controller Coordinate</li>
<li>Display Aids — Dimension Bar</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="wpf-page">WPF Page</h3>
<ul>
<li>Workpiece Page
<ul>
<li>Management Panel
<ul>
<li>Head Line
<ul>
<li><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a>
<ul>
<li>file extension is <code>.Workpiece</code>; the load type is <a class="xref" href="../../api/Hi.NcMech.Workpieces.Workpiece.html">Workpiece</a></li>
<li>The pointed Editor Panel is Management Tabs Panel</li>
</ul>
</li>
<li>Title Label</li>
</ul>
</li>
<li>Management Tabs Panel
<ul>
<li>Raw Shape Tab
<ul>
<li>Raw Geometry Source DropDown (Common Geometry and Meshed Geometry are EXCLUSIVE)
<ul>
<li>Common Geometry
Apply <a class="xref" href="../geom/geom-manage-control.html">Geometry Management Control</a></li>
<li>Meshed Geometry
Apply <a class="xref" href="../geom/meshed-geom-panel.html">Meshed Geometry Panel</a></li>
</ul>
</li>
</ul>
</li>
<li>Target Shape Tab
<ul>
<li>Geometry Management Control</li>
</ul>
</li>
<li>Anchor Tab
<ul>
<li>Geom To Fixture Tab
<ul>
<li><a class="xref" href="../geom/transformer-panel.html">Transformer Manage Panel</a></li>
</ul>
</li>
<li>Geom To Program-Zero Tab
<ul>
<li>Transformer Manage Panel</li>
</ul>
</li>
</ul>
</li>
<li>Runtime Tab
<ul>
<li>Initial Resolution Dropdown — the same powers-of-two ladder as the Mesh item above.</li>
</ul>
</li>
<li>Material Tab
Visible if <code>UserService.EnablePhysics</code> is true.
<ul>
<li>Workpiece Material File Selector
<ul>
<li>Browse Button
The initial directory is the project directory.</li>
<li>Browse Resource Button
The directory is the Default Resource directory.</li>
<li>Readonly File Path TextBox</li>
<li>Readonly Name TextBox (<a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Name">Name</a>)
<ul>
<li>ToolTip: <a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Note">Note</a></li>
</ul>
</li>
</ul>
</li>
<li>Cutting Parameter File Selector
<ul>
<li>Browse Button
The initial directory is the project directory.</li>
<li>Browse Resource Button
The directory is the Default Resource directory.</li>
<li>Readonly File Path TextBox</li>
<li>Readonly Name TextBox (<a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Name">Name</a>)
<ul>
<li>ToolTip: <a class="xref" href="../../api/Hi.Common.INameNote.html#Hi_Common_INameNote_Note">Note</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Viewer Panel
<ul>
<li>Viewer ToolBar
<ul>
<li><a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a></li>
<li>SetupDisplayee Options ToolBar
<ul>
<li>Options of <a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceEditorDisplayee.html">WorkpieceEditorDisplayee</a></li>
</ul>
</li>
</ul>
</li>
<li>RenderingCanvas
<ul>
<li>The <a class="xref" href="../../api/Hi.Disp.DispEngine.html">DispEngine</a>.<a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_Displayee">Displayee</a> is <a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceEditorDisplayee.html">WorkpieceEditorDisplayee</a> (Apply the model <a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceEditorDisplayeeConfig.html">WorkpieceEditorDisplayeeConfig</a>).</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="default-resource">Default Resource</h2>
<p>The default resources of Workpiece Material and Cutting Parameter exist in <code>Resource</code> folder under application folder (Not project folder). Both clients seed their file pickers at the matching <code>Resource</code> sub folder:</p>
<ul>
<li>&ldquo;Resource/WorkpieceMaterial&rdquo;</li>
<li>&ldquo;Resource/CuttingParameter&rdquo;</li>
</ul>
<h2 id="behavior">Behavior</h2>
<ul>
<li><p>A geometry slot's edit re-commits the swap-in <strong>before</strong> clearing the cache. The canvas draws the
runtime face's solids, and that face follows an authored edit only by rebuild, so a bare cache
clear would re-solidify a stale face. The re-commit is skipped when the slot is cleared to None:
None leaves the old object indexed, and re-committing would resurrect it.</p>
</li>
<li><p>Call <a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceService.html">WorkpieceService</a>.<a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceService.html#Hi_NcMech_Workpieces_WorkpieceService_ClearRawGeomCache">ClearRawGeomCache()</a> on Raw Shape set or changed.</p>
</li>
<li><p>Call <a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceService.html">WorkpieceService</a>.<a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceService.html#Hi_NcMech_Workpieces_WorkpieceService_ClearIdealGeomCache">ClearIdealGeomCache()</a> on Target Shape set or changed.</p>
</li>
<li><p>Both clients reach that one service by different routes: the web branch posts the controller's
cache-clear endpoints, and the WPF page calls
<a class="xref" href="../../api/Hi.NcMech.Workpieces.WorkpieceEditorDisplayee.html">WorkpieceEditorDisplayee</a>, which forwards to the same service.</p>
</li>
<li><p>Anchor edits re-commit their transformer and deliberately do <strong>not</strong> clear the geometry cache, so
a placement change forces no <code>CubeTreeFile</code> re-mesh.</p>
</li>
<li><p>The Mesh item's Initial Resolution feeds the runtime voxel simulation, not the setup canvas, so no
cache chain runs after a change.</p>
</li>
<li><p>The equipment canvas snaps to the isometric view when its displayee is bound to a rendering
connection; a shape edit on the branch re-commits and clears the cache so the next frame
re-solidifies, and leaves the view alone. The WPF page snaps its own canvas to isometric when a
Raw or Target shape is <em>set</em> to a different object, and whenever its meshed-geometry panel updates
its content — the assumption being that setting a shape changes the viewer more than changing its
content does, so only the setter event adjusts the view.</p>
</li>
<li><p>Keep <a href="../conventions/gui-file-path-assignment.html#portability">Portability</a> of the Material properties.</p>
</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>
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
<ul>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — builds the whole
<code>equipment/workpiece</code> branch: the root, the two geometry slots, the anchor group with its two
transformer slots, the Mesh item and the material group with its two resource items — including
each slot's re-commit-then-clear chain and the two allowed-kind whitelists.</li>
<li><code>wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue</code> — renders the root inline: the Raw
and Target type badges plus the intro.</li>
<li><code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> — the ItemType registry mapping the branch's
children to their panels.</li>
<li><code>wwwroot-src/src/components/controlTree/GeometrySlotPanel.vue</code> — the kind picker behind the raw
and target geometry slots.</li>
<li><code>wwwroot-src/src/components/controlTree/TransformerSlotPanel.vue</code> — the kind picker behind the two
anchor slots.</li>
<li><code>wwwroot-src/src/components/controlTree/WorkpieceRuntimePanel.vue</code> — the Mesh item's editor: the
Initial Resolution ladder and its hint.</li>
<li><code>wwwroot-src/src/components/controlTree/WorkpieceResourcePanel.vue</code> — the one panel both Material
children share, choosing its extension and resource subfolder from the node's role path.</li>
<li><code>wwwroot-src/src/components/widgets/FilePathInput.vue</code> — the file field those children use; its
project Browse entry is suppressed under <code>resourceOnly</code>, leaving the Resource-root pick.</li>
<li><code>wwwroot-src/src/components/mech/EquipmentSetupPanel.vue</code> — the canvas column and its Display
Options menu.</li>
<li><code>wwwroot-src/src/pages/GeneralSetupPage.vue</code> — the <code>/general-setup</code> page hosting the tree dock,
the content column and the canvas in nested splitters.</li>
<li><code>wwwroot-src/src/api/workpiece.ts</code> — typed client for <code>/api/Workpiece/*</code>: initialize, status, the
geometry re-commit and cache-clear calls, the two anchor transformer updates, <code>init-resolution</code>,
the two resource loads and the diff settings.</li>
<li><code>wwwroot-src/src/api/equipmentSetup.ts</code> — typed client for the shared canvas: the three
rendering-mode targets and the ten display flags, several of which are the workpiece's own
overlays.</li>
<li><code>Mech/WorkpieceController.cs</code> — the model REST surface at <code>/api/Workpiece</code>: geometry install and
cache clears, the anchor transformer updates, <code>init-resolution</code>, <code>workpiece-material/load</code>,
<code>cutting-parameter/load</code>, and the geometry-diff settings pair (visual radius and detection radius)
whose menu rides the <a class="xref" href="../execution/execution-extended-renderingcanvas-tool-bar.html">Execution Extended Tool Bar</a> rather
than this branch.</li>
<li><code>Mech/EquipmentSetupDisplayController.cs</code> — <code>/api/mech/equipment-setup-display</code>: binds the
equipment-setup displayee to a rendering connection, framing it isometrically at bind time, and
serves the rendering-mode and flag endpoints.</li>
<li><code>Disp/EquipmentSetupDisplayee.cs</code> — the merged fixture + workpiece setup scene the canvas draws.</li>
<li><code>Disp/EquipmentSetupDisplayeeConfig.cs</code> — its config: the fixture and workpiece display flags in
one place.</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>