Table of Contents

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 HomeMc fills in only when no linear axis was ever configured there.
  • The Execution canvas's coordinate markers. The IsoCoordinate and HeidenhainCoordinate rendering 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 in wwwroot-src/src/api/controller.ts a 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 G54G59 and G59.1G59.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, the update rebind, and the marker-row selection.
  • wwwroot-src/src/api/controller.ts — the typed wrappers over all of it, kept as those endpoints' only client; getCncBrand and 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 the SEQ solve 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