Legacy Controller Model (HardNcEnv)
HardNcEnv is the HardNc controller model: the one the project still carries as
MachiningProject.NcEnv and serializes as its NcEnv element, and the one the deprecating HardNc
pipeline plays. It is a model with no screen. The Legacy-Controller page at /controller/:tab? that
edited it was removed on 2026-09-11 — /controller/… now redirects to
/general-setup?tree=equipment/controller, following the route file's convention for retired
pages — and controller settings are edited on the General Setup page's
Controller Branch, the settings face of the SoftNc runner, which is the
live pipeline. What survives of the legacy face is its REST implementation,
Controller/ControllerController.cs, still mounted under /api/Controller/*, and its typed client
wwwroot-src/src/api/controller.ts, kept as those endpoints' only client; one wrapper in it,
getCncBrand, is still called from a live component. This page is what the model holds: how it
stands to the runner, what the live pipeline still reads from it, the settings that retire with it,
the ones the branch edits a runner-side copy of, and the one marker class both stores draw a work
coordinate with.
The Model, and the Runner Beside It
The project carries both models side by side and serializes both. HardNcEnv is
loaded from the project's own NcEnv element unconditionally; NcRunnerSuit — the
runner plus the project's per-case dependency list — is read from its own nested element. The legacy
element is a fallback source 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.
The legacy model is still read at play time. The legacy NC runner is constructed over a delegate
onto the project's NcEnv, so a replacement installed through the update endpoint — the removed
page's object-management chain was its caller, and nothing in the SPA calls it now — 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, EnableSoftNcRunner(API). 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 MachiningProject.NcEnv directly.
What the Live Pipeline Still Reads From It
Selecting the SoftNc pipeline does not silence the legacy model. Four reads still land on it with the switch on, so a value that only the legacy endpoints or the project XML can change still shows up on a runner play.
- Stroke limits, as a fallback. The per-step stroke check reads the runner's IStrokeLimitConfig when the switch is on and one is present, and falls back to the legacy stroke boxes otherwise — a CSV or NX-CL suit carries no stroke config, so a play on those falls through to the boxes. The pre-play audit that warns of an unlimited linear axis reads both sources end by end, the runner's first and the legacy box behind it, and stays silent for an axis either source guards.
- The home position, as a fallback. The chain is re-homed from the runner's home config; the
legacy
HomeMcfills in only when no linear axis was ever configured there. - The Execution canvas's coordinate markers. The
IsoCoordinateandHeidenhainCoordinaterendering flags on the Execution page draw from the legacy tables — see Two Faces of a Work Coordinate. - The Execution tool bar's brand. The Scene dropdown offers its Heidenhain-coordinate entry only
while the brand is Heidenhain, and the brand it asks for is the legacy model's, through
getCncBrand— the one wrapper inwwwroot-src/src/api/controller.tsa live component still calls.
The kinematics solver the service builds is mirrored onto NcEnv.XyzabcSolver as well, but that
slot is wiring the service maintains rather than a setting, and it is never serialized.
Settings That Retire With the Model
Four controls had no editor anywhere in the web application but the removed screen. None of them
gained one on the branch: three are legacy-only settings and retire with the model, and the fourth is an
action the runner path does not offer. Every one keeps its endpoint under /api/Controller/*, so
the value can still be read and written over REST or in the project XML — there is just no UI for
it.
Align P0 — not offered on the runner path
The removed Coordinate Table tab carried a third row action beside P0 and M0: Align P0, which moves the part so that program zero lands on the offset the row holds — the reproducing direction, program zero onto the work offset in the manual's terms. It did not write the coordinate; it wrote the fixture's geometry-to-table transformer, so the workpiece and fixture moved together and the runtime face followed at the next rebuild. The branch's Work Coordinates leaf carries P0 and M0 only, which write the row from the machine position — the opposite direction — and no alignment. The operation is not offered on the runner path.
The tab's undo history — Undo Align and Redo Align over two component-local stacks, the undo stack capped at 32 entries — was the component's own and went with it; the endpoint never kept an undo slot. It still answers with the assigned translation plus the transformer as it stood before and after the write, both serialized as XML, and the stateless revert endpoint still parses such a snapshot and assigns it as the fixture's transformer; nothing in the SPA posts to either now. The engine exposes the same operation as a script call, AlignWorkpieceProgramZeroToIso(API), which looks the row up in the legacy model's ISO coordinate table — an API for a script, not a replacement for the removed control.
Enable Shortest Rotary Path
EnableShortestRotary constructs true, and its only editor was the
removed Config tab's single toggle. 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 flag is
narrower than its name, because that same cycle is applied unconditionally on
Heidenhain — the brand branch runs it before the flag is consulted, so
clearing it changes nothing there.
The runner's dependency layer declares no counterpart: no brand parameter table, no generic config
and no branch leaf carries a shortest-rotary switch. What the runner pipeline has instead is the
Heidenhain M126 / M127 pair, modal codes read from the program text rather than from any
setting.
Max rotary speed per axis
MaxRotarySpeedABC_radds is the per-axis rotary speed ceiling the HardNc pipeline clamps feed and cycle time against; its editor was the removed Machine tab's Max Speed (rpm) column. On the runner pipeline the ceiling is not a setting of its own: the polar feed clamp reads it from the per-axis rapid-feedrate bucket, IRapidFeedrateConfig, and the legacy import funnels this field into that bucket at rpm × 360 deg/min, so the branch's Rapid Feedrates leaf edits the runner-side number. The legacy field itself retires with the model.
Heidenhain master-axis character
HeidenhainMasterAxisChar is a character face over the integer axis
direction the PLANE … SEQ solution family is resolved against; its editor was a second card the
removed Brand tab grew while the brand was Heidenhain, one select over A, B, C. The setter
takes only A, B or C and throws on anything else; the PUT endpoint upper-cases the first
character it is sent and answers 400 to anything but those three. On the runner pipeline the master
rotary is derived rather than configured — it is the first declared rotary axis — so the branch
has nothing to expose and no leaf for it.
What the Branch Edits a Runner-Side Copy Of
Naming what is not legacy-only 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 Brand Matrix for which of those leaves each brand grows. The branch edits the runner's copy of each; the legacy copy in HardNcEnv is reachable only through its endpoint and the project XML, and the two are not kept in step.
One flag sits outside both models. Set ideal offset dependent on tool house is a project-level
configuration flag, and the branch's Tool Offsets leaf and the legacy ideal-offset-dependent
endpoint read and write that one flag. The two tables stay separate: the legacy
set-ideal-offset-from-toolhouse endpoint — the removed Offset Table tab's Refresh from Tool
House — recomputes MillingToolOffsetTable from
MachiningToolHouse, while the branch's refresh recomputes the runner's own
tool-offset table.
Two Faces of a Work Coordinate
The work-coordinate marker is one displayee class serving two providers. The Execution canvas draws
it from IsoCoordinateTable — a
IsoCoordinateTable instance owned by the legacy model, which
constructs with G54 … G59 and G59.1 … G59.9 all at zero — and draws its Heidenhain datum
marker from the legacy datum tables the same way. The General Setup canvas builds the same
displayee over the active runner's effective IIsoCoordinateConfig
instead, which on most brands is the brand parameter table. Same marker, same code, two stores.
Which row the Execution marker draws is the displayee's own id, constructed as G54. Its only
writer is the legacy iso-coordinate-selection endpoint, which the removed tab's row selection
posted to and nothing in the SPA posts to now, so the Execution marker stays on G54. Whether it
is drawn is the IsoCoordinate rendering flag, toggled from the Execution page's Scene dropdown;
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. The General Setup marker's row is a user-config value of its own, with an
endpoint on the equipment-setup display controller.
Source Code Path
See HiNC App Anatomy for git repository links.
Web Application
HiNC-2025-webservice (Quasar CLI SPA):
Controller/ControllerController.cs— the model's REST face, still mounted: every legacy table and setting, the align endpoint that snapshots the transformer either side of the write, the stateless revert, theupdaterebind, and the marker-row selection.wwwroot-src/src/api/controller.ts— the typed wrappers over all of it, kept as those endpoints' only client;getCncBrandand the brand constants are what a live component still imports.wwwroot-src/src/components/execution/ExecutionExtendedToolBar.vue— that component: the Scene dropdown that toggles the coordinate flags, and asks the legacy model for its brand.wwwroot-src/src/api/renderingFlags.ts— the flag indices the Scene dropdown writes.Disp/ExecutionDisplayee.cs— the Execution canvas's displayee: the shipped flag set, and the work-coordinate and datum markers it builds over the legacy tables.Disp/IsoCoordinateEntryDisplayee.cs— the one marker class both canvases draw with.Disp/EquipmentSetupDisplayee.cs— the General Setup canvas's marker, over the runner's providers.wwwroot-src/src/router/routes.ts— the/controller/…redirect stub onto the Controller branch.
HiAPI Engine
HiUniNc/Numerical/HardNcEnv.cs— the model: the coordinate, datum and offset tables, the stroke boxes that construct infinite, the rapid rate and tooling time, the per-axis rotary speed ceiling, the shortest-rotary flag with its Heidenhain exclusion, and the master-axis character over its integer direction.HiUniNc/Numerical/HardNcLine.cs— the legacy consumers of two of those settings: the shortest-rotary path application and theSEQsolve that reads the master-axis direction.HiUniNc/Numerical/NcProc.cs— the legacy consumer of the rotary speed ceiling: feed and cycle time re-limited per rotary axis.HiUniNc/Numerical/MillingToolOffsetTable.cs— the offset table and the tool-house recompute the legacy refresh endpoint calls.HiMech/NcParsers/Dependencys/Generic/IsoCoordinateTable.cs— the coordinate table type and the fifteen G-code keys it constructs with, shared by name with the runner's brand-agnostic table.HiMech/Machining/MachiningEquipmentUtils/MachiningEquipmentUtil.cs— the alignment itself: the translation written into the fixture's geometry-to-table transformer.HiMech/NcParsers/SoftNcRunner.cs— the legacy import: which legacy fields are funnelled into which runner dependency, including the rotary speed conversion into the rapid-feedrate config.HiMech/NcParsers/LogicSyntaxs/PolarInterpolationUtil.cs— the rotary speed ceiling the runner pipeline reads, and why it is the rapid-rate bucket rather than a setting of its own.HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainPlaneTiltSyntax.cs— the derived master rotary on the runner pipeline: the first declared rotary axis, with no configured alternative.HiNc/MachiningProcs/MachiningProject.cs— 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.HiNc/MachiningProcs/LocalProjectService.cs— the legacy runner built over a delegate onto the project's model, the switch that selects which pipeline is active, and the stroke-limit and home-position fallbacks onto the legacy model.HiNc/Numerical/FilePlayers/HardNcRunner.cs— the legacy runner itself.HiNc/MachiningProcs/SessionShell.cs— the scripting face of the pipeline switch and of the alignment, and the optimisation route that passes the legacy model directly.
See Also
- Controller Branch — the SoftNcRunner-native controller branch: the live settings face, and the editor for everything named above as having a runner-side copy
- General Setup Page — the page that hosts that branch, and the rest of the equipment tree beside it