This repository has been archived on 2026-10-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
HiNC-2025-win-desktop-Deploy/App/wwwroot/HiAPI-docsite/anatomy/legacy-controller.html
T
2026-09-09 17:35:23 +08:00

576 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Legacy Controller Page | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Legacy Controller 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-ControllerPage">
<h1 id="legacy-controller-page">Legacy Controller Page</h1>
<p>The Legacy Controller page is the superseded controller face: the screen that edits
<a class="xref" href="../api/Hi.Numerical.HardNcEnv.html">HardNcEnv</a>, kept reachable while it still carries settings nothing else in the
application edits. It ships at the route <code>/controller/:tab?</code>, is reached from the app menu bar's
<strong>Page → Legacy-Controller</strong> entry, and owns no Control-Tree id of its own. Controller settings for
a project are edited on the General Setup page at <code>/general-setup</code> under the Control-Tree id
<code>equipment/controller</code> — <a class="xref" href="general-setup/controller/index.html">Controller Branch</a> — and the two surfaces edit
different models, so an edit on one is invisible to the other.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p>The <code>equipment/controller</code> id named above belongs to the Controller Branch rather than to this
page, and Anatomy makes no claim that it is stable across versions. The migration hop that keeps
an older 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-model-it-edits-and-the-one-it-does-not">The Model It Edits, and the One It Does Not</h2>
<p>The project carries both models side by side and serializes both. <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html">HardNcEnv</a> is
loaded from the project's own <code>NcEnv</code> element unconditionally; <a class="xref" href="../api/Hi.NcParsers.NcRunnerSuit.html">NcRunnerSuit</a> — the
runner plus the project's per-case dependency list — is read from its own nested element. The legacy
element is a <em>fallback source</em> for the suit rather than a shared store: a project file that carries
no runner element derives one from the legacy element at load time, and from then on the two drift
apart, because every later edit lands on one of them alone.</p>
<p>The legacy model is still read at play time. The legacy NC runner is constructed over a delegate
onto the project's <code>NcEnv</code>, so installing a replacement through this page's ⋮ menu propagates without
rewiring, and a project-level switch selects which of the two pipelines plays. That switch defaults
to the SoftNc pipeline and no screen in the web application changes it; it is a scripting property,
<a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_EnableSoftNcRunner_">EnableSoftNcRunner</a><small>(API)</small>. The legacy NC
optimisation route is the other live reader: it is taken whenever the switch is off or the session
holds no played SoftNc layers, and it is handed <code>MachiningProject.NcEnv</code> directly.</p>
<h2 id="what-only-this-screen-edits">What Only This Screen Edits</h2>
<p>Three settings on this screen have no editor anywhere else in the web application — neither on the
branch, nor on any other page.</p>
<h3 id="align-p0-and-its-undo-history">Align P0, and its undo history</h3>
<p>The Coordinate Table tab — that is the tab button's label; <strong>ISO Coordinate Table</strong> is the heading over the panel it opens — carries a third row action beside <strong>P0</strong> and <strong>M0</strong>: <strong>Align P0</strong>,
titled <em>&quot;Move workpiece+fixture so ProgramZero coincides with this ISO entry (mutates
Fixture.GeomToTableTransformer)&quot;</em>. It does not write the coordinate — it writes the <strong>fixture's
geometry-to-table transformer</strong>, moving the workpiece and fixture so that program zero lands on the
offset the row holds. The write goes to the authored setup equipment and reports the edit, so the
runtime face follows at the next rebuild. The branch's Work Coordinates leaf carries P0 and M0 and
no alignment.</p>
<p>The endpoint keeps no undo slot. It answers with the assigned translation plus the transformer as it
stood <strong>before</strong> and <strong>after</strong> the write, both serialized as XML, and the tab holds the history
itself: <strong>Undo Align</strong> and <strong>Redo Align</strong> buttons over two component-local stacks, the undo stack
capped at 32 entries and the oldest dropped past it. A third button sits beside them, <strong>Show on
Display</strong>, which belongs to the viewer rather than to the history: it is a bound toggle over the
<code>IsoCoordinate</code> rendering flag, carrying a <code>visibility</code> / <code>visibility_off</code> icon and writing through
the same flag endpoint the Scene dropdown writes. The two datum tabs carry the same button over
<code>HeidenhainCoordinate</code>. Each step posts a stashed snapshot back to a
stateless revert endpoint, which parses it and assigns the result as the fixture's transformer. Both
stacks are cleared whenever the tab's has-a-project prop changes, and the snapshot the component
believes is current is only what it last saw — a transformer changed from another surface is
overwritten wholesale by the next undo or redo rather than merged.</p>
<p>The engine exposes the same operation as a script call,
<a class="xref" href="../api/Hi.MachiningProcs.SessionShell.html#Hi_MachiningProcs_SessionShell_AlignWorkpieceProgramZeroToIso_">AlignWorkpieceProgramZeroToIso</a><small>(API)</small>; the
history is the screen's own.</p>
<h3 id="enable-shortest-rotary-path">Enable Shortest Rotary Path</h3>
<p>The <strong>Config</strong> tab's single toggle writes <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_EnableShortestRotary">EnableShortestRotary</a>, which
constructs <code>true</code>. Its banner reads <em>&quot;<strong>Shortest Rotary Path:</strong> optimises rotary axis motion to use
the shortest angular distance between positions.&quot;</em> On the legacy pipeline the flag gates one step:
each rotary axis of a block is cycled into the ±180° window around the previous block's value. The
toggle is narrower than it reads, because that same cycle is applied unconditionally on
<a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Heidenhain">Heidenhain</a> — the brand branch runs it before the flag is consulted, so
clearing the box changes nothing there.</p>
<p>The runner's dependency layer declares no counterpart: no brand parameter table, no generic config
and no branch leaf carries a shortest-rotary switch. The nearest thing the runner pipeline has is the
Heidenhain <code>M126</code> / <code>M127</code> pair, which is read from the program text rather than from any setting.</p>
<h3 id="heidenhain-master-axis-character">Heidenhain master-axis character</h3>
<p>The <strong>Brand</strong> tab grows a second card while the brand is Heidenhain, holding one select over <code>A</code>,
<code>B</code>, <code>C</code> labelled <strong>Master-axis character</strong>. It writes
<a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_HeidenhainMasterAxisChar">HeidenhainMasterAxisChar</a>, a character face over the integer axis
direction the <code>PLANE … SEQ</code> solution family is resolved against. Reads are normalised: anything that
is not <code>B</code> or <code>C</code> after trimming and upper-casing becomes <code>A</code>. On the runner pipeline the master
rotary is <strong>derived rather than configured</strong> — it is the first declared rotary axis — so the branch
has nothing to expose and no leaf for it.</p>
<h2 id="what-it-shares-with-the-branch">What It Shares With the Branch</h2>
<p>Naming what is <em>not</em> exclusive matters as much. The CNC brand, the stroke limits, the rapid feed,
the tool-change time, the tool offset table with its tool-house dependence, the work coordinates
with their P0 and M0 actions, and the Heidenhain datum preset and datum shift tables all have
editors on the branch — see
<a class="xref" href="general-setup/controller/brand-matrix.html">Brand Matrix</a> for which of those leaves each brand grows. The
<strong>Max Speed (rpm)</strong> column has a counterpart too: on the runner pipeline the rotary speed ceiling is
read from the rapid-feedrate config, and the legacy import funnels this field into it at rpm × 360
deg/min, so the branch's Rapid Feedrates leaf edits the runner-side number.</p>
<p>One control on this screen writes a value the branch also writes. <strong>Set ideal offset dependent on
tool house</strong> is a project-level configuration flag rather than a member of either NC model, and both
the Offset Table tab and the branch's Tool Offsets leaf read and write that one flag. The two
<em>tables</em> stay separate: this tab's <strong>Refresh from Tool House</strong> recomputes
<a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_MillingToolOffsetTable">MillingToolOffsetTable</a> from <a class="xref" href="../api/Hi.Machining.MachiningToolHouse.html">MachiningToolHouse</a>,
while the branch's refresh recomputes the runner's own tool-offset table.</p>
<h2 id="two-faces-of-a-work-coordinate">Two Faces of a Work Coordinate</h2>
<p>The work-coordinate marker is one displayee class serving two providers. This page's viewer draws it
from <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_IsoCoordinateTable">IsoCoordinateTable</a> — a
<a class="xref" href="../api/Hi.NcParsers.Dependencys.Generic.IsoCoordinateTable.html">IsoCoordinateTable</a> instance owned by the legacy model, which
constructs with <code>G54</code> … <code>G59</code> and <code>G59.1</code> … <code>G59.9</code> all at zero. The General Setup canvas builds the
same displayee over the active runner's effective
<a class="xref" href="../api/Hi.NcParsers.Dependencys.IIsoCoordinateConfig.html">IIsoCoordinateConfig</a> instead, which on most brands is the brand
parameter table. Same marker, same code, two stores.</p>
<p>Selecting a row on the ISO Coordinate Table tab writes the marker's id onto the shared Execution
displayee, so it decides which offset the marker draws at. It decides that whether or not the marker
is drawn: all three coordinate flags are off in the shipped rendering-flag set — as are the machine
tool and the cutter, leaving the workpiece, the fixture, the dimension bar and the cutter-location
strip as the four that arrive on — so on an untouched project the selection column moves something
invisible until <strong>Show on Display</strong> is pressed. The datum tabs also carry a
single-selection column, but nothing is sent when it changes — there the selection is a highlight and
nothing more.</p>
<h2 id="the-tabs">The Tabs</h2>
<p>The left pane is a tab strip over seven panels, addressable as the route's optional segment:
<code>coordinate-table</code>, <code>datum-preset</code>, <code>datum-shift</code>, <code>offset-table</code>, <code>machine</code>, <code>brand</code>, <code>config</code>. A
bare <code>/controller</code> canonicalises to <code>coordinate-table</code>. Two tab <strong>buttons</strong> are conditional — Datum
Preset and Datum Shift render only while the brand reads Heidenhain — but their panels are always
present in the template, so both segments stay valid URL targets under any brand. Switching the brand
away from Heidenhain while one of those two is active moves the selection back to Coordinate Table.</p>
<table>
<thead>
<tr>
<th>Tab</th>
<th>Edits</th>
</tr>
</thead>
<tbody>
<tr>
<td>Coordinate Table</td>
<td><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_IsoCoordinateTable">IsoCoordinateTable</a>, plus the P0 / M0 / Align P0 row actions</td>
</tr>
<tr>
<td>Datum Preset</td>
<td><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_HeidenhainDatumPresetTable">HeidenhainDatumPresetTable</a>, keyed <code>Q339</code></td>
</tr>
<tr>
<td>Datum Shift</td>
<td><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_HeidenhainDatumShiftTable">HeidenhainDatumShiftTable</a>, keyed <code>D</code></td>
</tr>
<tr>
<td>Offset Table</td>
<td><a class="xref" href="../api/Hi.Numerical.MillingToolOffsetTable.html">MillingToolOffsetTable</a> with row-level add, delete and key rename</td>
</tr>
<tr>
<td>Machine</td>
<td><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_RapidFeedrate_mmdmin">RapidFeedrate_mmdmin</a>, <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_ToolingTime">ToolingTime</a>, <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_StrokeLimitXyz_mm">StrokeLimitXyz_mm</a>, <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_StrokeLimitAbc_rad">StrokeLimitAbc_rad</a>, <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_MaxRotarySpeedABC_radds">MaxRotarySpeedABC_radds</a></td>
</tr>
<tr>
<td>Brand</td>
<td><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_CncBrand">CncBrand</a>, and the master-axis character on Heidenhain</td>
</tr>
<tr>
<td>Config</td>
<td><a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_EnableShortestRotary">EnableShortestRotary</a></td>
</tr>
</tbody>
</table>
<p>The brand select offers all five declared brands — <a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Fanuc">Fanuc</a>,
<a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Heidenhain">Heidenhain</a>, <a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Mazak">Mazak</a>,
<a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Siemens">Siemens</a> and <a class="xref" href="../api/Hi.Numerical.CncBrand.html#Hi_Numerical_CncBrand_Syntec">Syntec</a> — labelled with the
brand names verbatim rather than through the locale bundle, and warns above the select that
<em>&quot;Brand-specific settings may be lost when the brand changes.&quot;</em></p>
<p><strong>The Machine tab's axis rows are fixed, not chain-driven.</strong> It renders exactly <code>X</code>, <code>Y</code>, <code>Z</code> under
<strong>Linear Axis Stroke (mm)</strong> and exactly <code>A</code>, <code>B</code>, <code>C</code> under <strong>Rotary Axis Stroke (deg) &amp; Max Speed
(rpm)</strong>, because both row sets are literal in the template. Every write there sends the whole vector
— all six linear bounds, all six rotary bounds converted to radians, or all three speeds converted to
rad/s — rather than the one cell that changed. The branch's per-axis leaves take the opposite shape:
one row per machine-chain axis, one axis per write, described in
<a class="xref" href="general-setup/controller/machine/per-axis-tables.html">Per-Axis Tables</a>.</p>
<p>Both stroke limits construct as an infinite box, and the service serializes named floating-point
literals, so those bounds reach the browser as the tokens <code>Infinity</code> and <code>-Infinity</code> rather than as
numbers. The shared numeric field prints what it is handed and parses both words back, so an
unconfigured Machine tab opens showing those words rather than a blank and takes them typed in; the
rotary rows carry them through unconverted, because the degree conversion guards on a finite value.</p>
<p><strong>A stroke write does not survive its own re-read.</strong> The read and the write agree on the wire order:
the read emits the six bounds interleaved per axis, <code>[minX, maxX, minY, maxY, minZ, maxZ]</code>, and the
tab posts the same six back. The write then hands that vector to the box constructor, which reads its
arguments <em>grouped</em> — the whole minimum corner first, the whole maximum corner second. The vector is
de-interleaved on the way in, so only the first and the last number land where they were sent and the
four between them move to other rows; from the infinite default above, that is enough to leave the
middle axis reading a <code>Max</code> below its <code>Min</code> after any single edit. The rotary stroke endpoints share
the constructor and the behaviour; the three <strong>Max Speed</strong> values travel as a plain triple and are
unaffected. The regrouped box is not inert — with the stroke check on and no runner-side stroke
config resolving, the legacy play path validates every step against
<a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_StrokeLimitXyz_mm">StrokeLimitXyz_mm</a> and <a class="xref" href="../api/Hi.Numerical.HardNcEnv.html#Hi_Numerical_HardNcEnv_StrokeLimitAbc_rad">StrokeLimitAbc_rad</a>
as stored. The branch's per-axis leaves, writing one axis at a time, carry none of it.</p>
<h2 id="how-an-edit-commits">How an Edit Commits</h2>
<p>The tabs reuse the same numeric field the rest of the application uses, so the timing is that
widget's: commit on blur or on Enter, never per keystroke, with the bounds and parse behaviour set
out in <a class="xref" href="widget/numeric-input.html">Numeric Input</a>. Clearing a cell parses to null and every legacy
handler returns on null, so an emptied cell is not an edit — the one exception is the Offset Table's
tool-number cell, which forbids an empty value and shows a parse error in place instead. Selects,
toggles and row buttons commit on the click.</p>
<p>The optimistic write is the same capture-assign-await-restore shape the branch's panels use, and
<a class="xref" href="general-setup/controller/editing-contract.html">Editing Contract</a> is where that shape, its row-scoped
payloads and its rollback semantics are set out once. Four differences are this screen's own:</p>
<ul>
<li><strong>Three fields commit without a rollback.</strong> The Machine tab's <strong>Rapid Feedrate</strong> and <strong>Tooling
Time</strong> handlers assign the new value and await the write without capturing the old one, so a
refused write raises the toast and leaves the field showing the number the server rejected. The
Brand tab's <strong>Master-axis character</strong> select is the third and the least recoverable: its handler
captures nothing and restores nothing, and its two-way binding has already put the pick into the
tab's own state before the handler runs, so a refused write leaves the rejected character on screen
looking accepted. Every other value handler — the stroke and speed vectors, the coordinate and
datum cells, the offset row and its key rename, the brand select itself, the shortest-rotary toggle
(two-way bound as well, but reconstructing its old value from the new one) — restores the old value
on failure. Row actions are outside the shape entirely: <strong>P0</strong>, <strong>M0</strong>, the datum reset, an add and
a delete all write first and touch the local rows only once the request resolves.</li>
<li><strong>The REST surface answers with status codes, not a success envelope.</strong> A missing table, an
unknown index or an unexpected paste type is answered as <em>not found</em>, <em>bad request</em> or <em>conflict</em>.
Most of those answers are bare English sentences; four carry a code — no project loaded, no NcEnv
found, no NcEnv configuration and no fixture — which the client re-renders from the locale bundle
under any language but English. The branch's surface instead answers a missing dependency inside a
200 envelope; nothing here does.</li>
<li><strong>A refused load is silent on six of the seven tabs.</strong> Every write wrapper in this page's API
module goes through the plain-JSON helper, which throws on any non-2xx and returns the parsed body
otherwise, so a refused write always reaches a toast. Eight read wrappers deliberately bypass it
and answer a benign default on any non-2xx: an empty list for the coordinate table, both datum
tables and the offset table, <em>unknown</em> for the brand, <code>A</code> for the master-axis character, and <em>off</em>
for the tool-house dependence and shortest-rotary flags. A refused load therefore renders as an
empty or default tab, indistinguishable from one whose table really is empty, with no toast and no
console line. Only the Machine tab's five reads throw, so it is the one tab on which a refused load
shows.</li>
<li><strong>The tool-house dependence toggle writes before it flips.</strong> Turning it on awaits the flag write,
then flips the local toggle, then refreshes the offsets from the tool house and re-reads the table.
A failure in the refresh therefore leaves the flag committed on both sides rather than reverted.</li>
</ul>
<p>Every tab composes its failure toast the same way: negative, 3.5 seconds, the tab's own localized
context followed by the thrown error's own text, with a console line naming the component. That text
is built by the shared helper as <code>HTTP &lt;status&gt;: &lt;server message&gt;</code>, so the status code reaches the
user in the toast — where the branch's envelope failures surface the server's sentence alone.</p>
<p>Each tab fetches once on mount and again when its has-a-project prop turns true; the tab panels are
kept alive, so moving between tabs does not refetch. That prop is the indexed key rather than the
project store's own flag: Initialize answers <em>not found</em> where the project carries no <code>NcEnv</code>, so a
project can be open while every tab still reads as having none. Nothing pushes changes at a mounted
tab, and no tab re-reads after a cell edit — the Offset Table's two tool-house actions are the one
exception, because the server recomputes the table under them.</p>
<p>The four tables that render as data tables — the coordinate table, the two datum tables and the
offset table — are the only places in the application that use that component. None of them declares
a sortable column, and all four run unpaginated with every row shown.</p>
<h2 id="object-management-and-the-install-chain">Object Management and the Install Chain</h2>
<p>The left pane's head line carries the shared object-management ⋮ menu, the title <strong>Controller</strong>, and
a badge reading <em>ready</em> while a key is indexed and <em>no project</em> otherwise. The menu's entries are
Load, Save As, Copy, Paste and XML Mode; Load Resource is absent because the page passes no resource
directory. Load and Save As browse the server file system through the shared file-explorer dialog
filtered to <code>*.NcEnv / *.xml</code>, rooted at the project directory once a project is open; Save As
proposes the name <code>NcEnv.xml</code>; Paste is checked against the expected type
<code>Hi.Numerical.HardNcEnv, HiUniNc</code>.</p>
<p>Load, Paste and an XML apply swap only the indexed object, so the page then installs it: it posts the
indexed key to the install endpoint <strong>before</strong> re-running Initialize, because Initialize re-indexes
from the project and would otherwise resurrect the object that was replaced. A failed install stops
the chain with a toast. A successful one re-reads the brand, which is what re-gates the two Heidenhain
tabs, refreshes the rendering flags, and raises an informational toast naming the installed type. The
XML dialog's Apply raises the load event as well as its own, and the page listens only to the load
event, so the chain runs once rather than twice.</p>
<p>Initialize mints a fresh index key on every call, and the page registers each one for cleanup and
drops the key it replaced, so repeated installs do not accumulate entries.</p>
<h2 id="the-viewer">The Viewer</h2>
<p>The right pane is a rendering canvas with its own toolbar: the shared view toolbar, a <strong>Scene ▾</strong>
dropdown, and a badge reading <em>rendering</em> or <em>disconnected</em>. The canvas binds the <strong>shared Execution
displayee</strong> rather than a viewer of its own, so what is toggled here is what the Execution page
shows.</p>
<p>The Scene dropdown groups its checkboxes as <strong>Solid</strong> — Machine, Tool, Workpiece, Fixture —
<strong>Coordinate</strong> — Program Zero, ISO Coordinate, Heidenhain Coordinate — and <strong>Display Aids</strong> —
Dimension Bar, Color Scale Bar. The Heidenhain Coordinate row is listed only while the brand reads
Heidenhain. Three tabs carry a <strong>Show on Display</strong> button of their own that flips the same shared
flags: the coordinate tab flips ISO Coordinate, and the two datum tabs both flip Heidenhain
Coordinate.</p>
<p>The Heidenhain marker is gated twice over, and the second gate is never satisfied. The displayee is
added to the scene only while its flag is set <em>and</em> the model's brand is Heidenhain; it then returns
without drawing until an active datum number or datum-shift argument has been assigned to it, and no
code path in the web service assigns either. So the marker stays absent whatever the flag reads.
Where it does draw, it resolves its offset through the legacy datum tables — the ones this page's two
datum tabs edit — and not through the runner's.</p>
<h2 id="layout">Layout</h2>
<ul>
<li><strong>Legacy Controller</strong> Page — <code>/controller/:tab?</code>, a two-pane splitter, left pane 55 % and draggable
between 25 % and 75 %
<ul>
<li>Management Pane
<ul>
<li>Head Line
<ul>
<li><a class="xref" href="widget/object-management-menu-button.html">Object Management Menu Button</a> (<code>⋮</code>) — Load, Save
As, Copy, Paste, XML Mode</li>
<li><strong>Controller</strong> Title</li>
<li>Status Badge — <em>ready</em> / <em>no project</em></li>
</ul>
</li>
<li>Tab Strip — <strong>Coordinate Table</strong>, <strong>Datum Preset</strong>, <strong>Datum Shift</strong>, <strong>Offset Table</strong>,
<strong>Machine</strong>, <strong>Brand</strong>, <strong>Config</strong>; the two datum buttons rendered only under Heidenhain</li>
<li>Tab Panels — kept alive; one shown at a time
<ul>
<li><strong>ISO Coordinate Table</strong> Panel
<ul>
<li>Toolbar — <strong>Undo Align</strong>, <strong>Redo Align</strong>, <strong>Show on Display</strong></li>
<li>Table — columns <strong>Index</strong>, <strong>X</strong>, <strong>Y</strong>, <strong>Z</strong>, <strong>Actions</strong>, with a single-selection column
<ul>
<li>Row Actions — <strong>P0</strong>, <strong>M0</strong>, <strong>Align P0</strong></li>
</ul>
</li>
</ul>
</li>
<li><strong>Datum Preset Table</strong> Panel — <strong>Show on Display</strong>; columns <strong>Q339</strong>, <strong>X</strong>, <strong>Y</strong>, <strong>Z</strong>,
<strong>Actions</strong>, the action being a reset-to-zero button</li>
<li><strong>Datum Shift Table</strong> Panel — the same shape, keyed <strong>D</strong></li>
<li><strong>Tool Offset Table</strong> Panel
<ul>
<li>Toolbar — <strong>Set ideal offset dependent on tool house</strong> toggle, and <strong>Refresh from Tool
House</strong> while it is on</li>
<li>Table — columns <strong>Tool #</strong>, <strong>Ideal Radius</strong>, <strong>Radial Wear</strong>, <strong>Ideal Height</strong>, <strong>Axial
Wear</strong>, and a delete button; the tool number is editable and the delete button present only
while the dependence is off</li>
<li><strong>Add</strong> Button — below the table, shown only while the dependence is off</li>
</ul>
</li>
<li><strong>Machine Configuration</strong> Panel
<ul>
<li><strong>General</strong> Card — <strong>Rapid Feedrate (mm/min)</strong> and <strong>Tooling Time (sec)</strong> numeric fields</li>
<li><strong>Linear Axis Stroke (mm)</strong> Card — <strong>Axis</strong> / <strong>Min</strong> / <strong>Max</strong> over fixed rows X, Y, Z</li>
<li><strong>Rotary Axis Stroke (deg) &amp; Max Speed (rpm)</strong> Card — <strong>Axis</strong> / <strong>Min (deg)</strong> /
<strong>Max (deg)</strong> / <strong>Max Speed (rpm)</strong> over fixed rows A, B, C</li>
</ul>
</li>
<li><strong>CNC Brand</strong> Panel
<ul>
<li><strong>Selection</strong> Card — the brand select over the five brands, above the brand-change warning
banner</li>
<li><strong>Heidenhain Settings</strong> Card — <strong>Master-axis character</strong> select over A / B / C; shown only
under Heidenhain</li>
</ul>
</li>
<li><strong>Configuration</strong> Panel
<ul>
<li><strong>Options</strong> Card — <strong>Enable Shortest Rotary Path</strong> toggle above its explanatory banner</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Viewer Pane
<ul>
<li>Viewer Toolbar
<ul>
<li><a class="xref" href="widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a></li>
<li><strong>Scene ▾</strong> Dropdown — <strong>Solid</strong>, <strong>Coordinate</strong> and <strong>Display Aids</strong> groups</li>
<li>Connection Badge — <em>rendering</em> / <em>disconnected</em></li>
</ul>
</li>
<li>Rendering Canvas — bound to the shared Execution displayee</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/pages/ControllerPage.vue</code> — the page shell: the splitter, the seven tab panels and
the Heidenhain gate over two of the buttons, the install-then-initialize chain, the rendering-flag
snapshot the per-tab toggles read, and the tab reset on leaving Heidenhain.</li>
<li><code>wwwroot-src/src/components/controller/CoordinateTableTab.vue</code> — the coordinate table: the per-cell
whole-row write, the row selection that names the marker, the P0 / M0 actions, and Align P0 with
its two client-side history stacks and their cap.</li>
<li><code>wwwroot-src/src/components/controller/DatumPresetTab.vue</code> and
<code>wwwroot-src/src/components/controller/DatumShiftTab.vue</code> — the two Heidenhain tables: the <code>Q339</code>
and <code>D</code> key columns, the reset-to-zero action, and the selection that is a highlight only.</li>
<li><code>wwwroot-src/src/components/controller/OffsetTableTab.vue</code> — the offset editor: row add, delete and
key rename with its duplicate guard, and the two-step tool-house dependence toggle.</li>
<li><code>wwwroot-src/src/components/controller/MachineTab.vue</code> — the fixed X/Y/Z and A/B/C row sets, the
whole-vector writes, the degree and rpm conversions with their infinity guards, and the two
handlers that commit without capturing a rollback value.</li>
<li><code>wwwroot-src/src/components/controller/BrandTab.vue</code> — the five-brand select with its captured
rollback, the change warning, and the Heidenhain master-axis card: its read normalisation, its
two-way binding and its write with no rollback.</li>
<li><code>wwwroot-src/src/components/controller/ConfigTab.vue</code> — the shortest-rotary toggle, its banner and
its reconstructed rollback.</li>
<li><code>wwwroot-src/src/components/controller/ControllerExtendedToolBar.vue</code> — the <strong>Scene</strong> dropdown: the
three flag groups and the brand test that hides the Heidenhain row.</li>
<li><code>wwwroot-src/src/api/controller.ts</code> — the typed wrappers over every endpoint above, the brand
constants and their literal labels, the align / revert payloads, the documented stroke-vector order,
and the eight read wrappers that answer a default instead of raising.</li>
<li><code>wwwroot-src/src/api/http.ts</code> — the plain-JSON helper every write goes through, its coded-error
keys, and the success-envelope helper this module does not use.</li>
<li><code>wwwroot-src/src/api/renderingFlags.ts</code> — the flag indices the Scene dropdown and the per-tab
toggles write.</li>
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> — the numeric cell: commit on blur or Enter,
the empty-to-null parse the handlers reject, the generic text path that prints the infinity tokens,
and the parse branch that takes them back.</li>
<li><code>wwwroot-src/src/components/widgets/ObjectManagementMenuButton.vue</code> — the ⋮ menu, its server file
browser and extension filter, the paste type check, and the load event an XML apply also raises.</li>
<li><code>wwwroot-src/src/components/RenderingCanvas.vue</code> and
<code>wwwroot-src/src/components/RenderingCanvasToolBar.vue</code> — the viewer pane's canvas and its shared
view toolbar.</li>
<li><code>wwwroot-src/src/composables/useRouteTabs.ts</code> — the tab-to-URL sync, the seven valid segments and
the default the bare route canonicalises to.</li>
<li><code>wwwroot-src/src/composables/useCleanupHub.ts</code> — the registration that drops each superseded index
key.</li>
<li><code>wwwroot-src/src/router/routes.ts</code> — the <code>/controller/:tab?</code> route beside <code>/general-setup</code>.</li>
<li><code>wwwroot-src/src/components/AppMenuBar.vue</code> — the <strong>Page</strong> dropdown carrying the
<strong>Legacy-Controller</strong> entry.</li>
<li><code>wwwroot-src/src/stores/project.ts</code> — the project flag the page watches in order to re-run
Initialize; the indexed key that call returns is what the seven tabs and the status badge read.</li>
<li><code>wwwroot-src/src/layouts/MainLayout.vue</code> — the keep-alive that holds the page across navigation and
rebuilds it on a project epoch change.</li>
<li><code>wwwroot-src/src/i18n/en/controller.ts</code> — every title, tab label, column header, banner and error
context quoted above.</li>
<li><code>wwwroot-src/src/i18n/en/menu.ts</code> — the <strong>Page</strong> and <strong>Legacy-Controller</strong> menu strings.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared column, action and status strings the tabs reuse.</li>
<li><code>Controller/ControllerController.cs</code> — the page's whole REST surface: the index-and-install pair,
the per-property readers and writers, the interleaved stroke reads beside the grouped stroke
writes, the four coded error payloads, the row-level offset CRUD, the align endpoint that snapshots
the transformer either side of the write, the stateless revert, and the display binding that
attaches the shared Execution displayee.</li>
<li><code>Common/ApiError.cs</code> — the four coded payloads named above.</li>
<li><code>Program.cs</code> — the named-floating-point-literal serializer the infinite bounds travel under.</li>
<li><code>Widget/ObjectManagementController.cs</code> — the server half of the ⋮ menu, including the paste that
rejects an object the expected-type string does not admit.</li>
<li><code>Disp/ExecutionDisplayee.cs</code> — the displayee this page's canvas binds: the two coordinate flags,
the extra brand test on the Heidenhain one, and the legacy tables both markers are built over.</li>
<li><code>Disp/IsoCoordinateEntryDisplayee.cs</code> — the one marker class serving both faces, over whichever
coordinate provider it is handed.</li>
<li><code>Disp/HeidenhainCoordinateEntryDisplayee.cs</code> — the Heidenhain marker: the brand guard, the datum
number and shift argument it draws nothing without, and the legacy tables it resolves through.</li>
<li><code>Common/ProjectDisplayeeService.cs</code> — where that single shared displayee is created.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiUniNc/Numerical/HardNcEnv.cs</code> — the model this page edits: the coordinate, datum and offset
tables, the stroke boxes that construct infinite, the rapid rate and tooling time, the
shortest-rotary flag with its Heidenhain exclusion, and the master-axis character over its integer
direction.</li>
<li><code>HiGeom/Geom/Box3d.cs</code> — the box the two stroke limits are, its infinite construction, and the
six-argument constructor whose grouped argument order the interleaved stroke writes are handed to.</li>
<li><code>HiUniNc/Numerical/HardNcLine.cs</code> — the legacy consumers of two of those settings: the
shortest-rotary path application and the <code>SEQ</code> solve that reads the master-axis direction.</li>
<li><code>HiUniNc/Numerical/MillingToolOffsetTable.cs</code> — the offset table and the tool-house recompute the
Refresh button calls.</li>
<li><code>HiMech/NcParsers/Dependencys/Generic/IsoCoordinateTable.cs</code> — the coordinate table type and the
fifteen G-code keys it constructs with, shared by name with the runner's brand-agnostic table.</li>
<li><code>HiMech/Machining/MachiningEquipmentUtils/MachiningEquipmentUtil.cs</code> — the alignment itself: the
translation written into the fixture's geometry-to-table transformer.</li>
<li><code>HiMech/NcParsers/SoftNcRunner.cs</code> — the legacy import: which legacy fields are funnelled into
which runner dependency, including the rotary speed conversion into the rapid-feedrate config.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/PolarInterpolationUtil.cs</code> — the rotary speed ceiling the runner
pipeline reads, and why it is the rapid-rate bucket rather than a setting of its own.</li>
<li><code>HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainPlaneTiltSyntax.cs</code> — the derived master rotary
on the runner pipeline: the first declared rotary axis, with no configured alternative.</li>
<li><code>HiNc/MachiningProcs/MachiningProject.cs</code> — where both models hang off the project, and the load
path that derives a runner from the legacy element only when no runner element is present.</li>
<li><code>HiNc/MachiningProcs/LocalProjectService.cs</code> — the legacy runner built over a delegate onto the
project's model, the switch that selects which pipeline is active, and the per-step stroke check
that falls back to the legacy boxes when no runner-side stroke config resolves.</li>
<li><code>HiNc/Numerical/FilePlayers/HardNcRunner.cs</code> — the legacy runner itself.</li>
<li><code>HiNc/MachiningProcs/SessionShell.cs</code> — the scripting face of the pipeline switch and of the
alignment, and the optimisation route that passes the legacy model directly.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="general-setup/controller/index.html">Controller Branch</a> — the SoftNcRunner-native controller branch that supersedes this screen,
and where controller settings for a project are edited</li>
<li><a class="xref" href="general-setup/index.html">General Setup Page</a> — the page that hosts the branch above, and the rest of the equipment
tree beside it</li>
<li><a href="../manual/utilities/legacy-controller.html">Legacy Controller (manual)</a> — the end-user task: the settings this screen still
owns alone, and which face to edit for everything else</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>