Controller Brand
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 /general-setup under the Control-Tree id
equipment/controller/machine/brand, and it is the only leaf whose write changes which of the
branch's other nodes exist. The tree row reads Controller Brand; the select inside the panel is
labelled Controller brand.
Important
Anatomy makes no claim that the id named here is stable across versions. The migration hop that
keeps an older ?tree= value resolving is described in
Tree Ids and Routes.
What the Brand Actually Selects
The brand marker itself is a plain string carried by CncBrandDependency, 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 preset: a whole SoftNcRunner 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.
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 F word lands — on
Heidenhain after the rightmost DR+/DR- and RL/RR/R0, so the element order a TNC enforces
holds — and the shape of the source note the optimizer embeds, a ; 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. The piece classifier does not read it: neither of its re-interpolation guards is keyed
on the brand — one keys on where the arc's centre came from, the other on the piece's own
incremental words. So a klartext arc centred on a
modal CC line splits like any other, provided the chain supplies both in-plane centre coordinates
and the C block states its own words absolutely; an incomplete chain
(NcOpt--SplitionStartPointCenterUnsupported) and an incremental word on the block itself
(NcOpt--SplitionIncrementalUnsupported) are each optimized as whole lines, and
Heidenhain Support states both. An arc whose centre falls
entirely on its start point never reaches the centre guard: with no radius it is not an arc at all,
so it warns Arc-CircleCenter--OnStartPoint and is degraded to a chord as the program is read. The
incremental guard is not conditioned on the block being an arc, so a degraded chord that still
carries an I word trips it like any other line.
Each of the five properties returns a new instance per read, 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.
The option list this panel offers is the client-side constant: Fanuc, Siemens, Heidenhain, Syntec, Mazak, in that order. Brand names never translate.
What Names the Current Brand, and What Names the Target
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, nothing left on the panel names the brand in force — only the confirmation dialog names both at once.
| Where it is read | What it names |
|---|---|
| The Controller brand select, before a pick | the brand in force, from the shared snapshot |
| The Controller brand select, after a pick | the target — the staged brand, not yet applied |
The orange banner's {brand} |
the target |
The dialog's {brand} |
the brand in force, or the literal word current when the snapshot carries no brand marker |
The dialog's {next} |
the target |
The success toast's {brand} |
the target |
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 F position and a
parenthesized source note — and the negative-radius validation warning stays off.
Staging, and the Two Buttons
The panel is one of the branch's two draft panels: the pick stages, it does not write.
- Apply brand is enabled only while the staged value is non-empty and differs from the brand the snapshot reports. It carries a spinner and both buttons disable while a request is in flight.
- Revert drops the staging by re-reading the snapshot's brand, and is enabled under the same condition.
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 — cannot be issued from this node, 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.
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.
The Confirmation Flow
Apply brand opens a confirm dialog before any request is sent. Its title is Switch controller brand, 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 OK and Cancel in English. Cancel 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:
- Unticked — "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."
- Ticked — "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."
While a different brand is staged, an orange banner stands above the buttons with the same warning in the target's voice: "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."
Both sentences generalise, in opposite directions, and the exact reading is What Survives below. The sweep is narrower than the old brand's program-data tables are removed: 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 every apply, including the ones where nothing at all is swept.
What the request can be refused with
The action refuses before it touches anything in three cases, and each reaches the same toast:
| Refusal | Answer |
|---|---|
| No project loaded | not found, with the coded no-project payload — the one refusal here that a non-English locale re-renders |
| An NC program is playing | conflict, rather than swapping the parser under a running session |
| A brand token outside the five | bad request, naming the token it was given |
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.
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.
What the operator sees afterwards
Success raises an informational toast, "Controller brand switched to {brand}", naming the target. Failure raises a negative toast composed as “Switch controller brand” 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.
What the Switch Does
The action runs four steps in a fixed order, and the order is what decides the outcome.
- Capture. The outgoing work-coordinate provider — the first IIsoCoordinateConfig 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.
- Swap. The preset is assigned through SoftNcRunner, 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.
- Carry. The held offsets are written into the new 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.
- Sweep. 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.
What Is Carried
The carry moves only the work-coordinate XYZ offsets. No other table's values cross, and nothing else on either plane is copied.
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:
| Provider | Brands | Coordinate ids it exposes |
|---|---|---|
| FanucParameterTable | Fanuc, Mazak | G54–G59 and G54.1 P1–P48, all seeded |
| SyntecParameterTable | Syntec | the same set, through the same address map — spelled Pr rather than # |
| SiemensFrameTable | Siemens | G54–G57, plus the extended series G505–G599 |
| HeidenhainDatumTable | Heidenhain | G54–G59, aliased onto preset rows 1–6 |
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.
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.
What Survives and What Is Swept
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.
| Per-case table | Proxied by | Behaviour on a switch |
|---|---|---|
| ToolOffsetTable | all five | survives every switch |
| RetainedCommonVariableTable | Fanuc, Syntec, Mazak | survives among those three; swept into Siemens or Heidenhain |
| FanucParameterTable | Fanuc, Mazak | survives between those two; swept otherwise |
| SyntecParameterTable | Syntec | swept on leaving Syntec |
| SiemensMachineDataTable | Siemens | swept on leaving Siemens |
| SiemensFrameTable | Siemens | swept on leaving Siemens |
| SiemensRParameterTable | Siemens | swept on leaving Siemens |
| SiemensToolOffsetTable | Siemens | swept on leaving Siemens, tool-name map included |
| HeidenhainDatumTable | Heidenhain | swept on leaving Heidenhain |
| HeidenhainParameterTable | Heidenhain | swept on leaving Heidenhain |
| HeidenhainQParameterTable | Heidenhain | swept on leaving Heidenhain, and exposed by no node or endpoint in the branch |
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.
Beside the per-case tables, the preset's plain 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:
- ToolingMcConfig — the tool-change position, back to its three-axis default, where X and Y are left unset and Z returns to zero.
- GenericBlockSkipConfig — 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.
- SubProgramFolderConfig — the subprogram lookup folders,
back to an internal folder of
NCand no external folder. - The macro iteration guards, back to the target preset's own guard set.
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.
What the Tree Does Afterwards
The panel derives its rebuild scope from its own id by stripping the fixed /machine/brand 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 own request for the runner snapshot,
independent of the shared one the panels gate on.
The shared snapshot is re-read either way, in the call's finally block, together with the runner's index key. Three things follow:
- The selection does not move. 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.
- A failure that reached the server after the swap leaves the tree describing the previous shape. 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 Apply brand 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.
- A failed snapshot re-read empties the branch's panels. That read resets the shared snapshot to its empty value and raises its own toast, “Load controller settings” followed by the server's message; every leaf panel then renders the shared no-runner line even where the tree still lists the leaves.
Layout
- General Setup Control Tree — the left dock of
/general-setup- Controller Node Row
- Machine / Controller Group Row
- Controller Brand Node Row —
equipment/controller/machine/brand
- Controller Brand Node Row —
- Machine / Controller Group Row
- Controller Node Row
- Editor Row — Controller Brand Panel
- Shared Empty Line — "No NC runner — load a project first.", in place of everything below it while the shared snapshot reports no runner
- Controller brand Select — dense, outlined; five options in list order, Fanuc, Siemens, Heidenhain, Syntec, Mazak; no clear button and no search field
- Carry work-coordinate XYZ (G54…) into the new brand's table CheckBox — dense, ticked when the panel mounts
- Warning Banner — orange, dense, rounded; present only while a different brand is staged
- Button Row
- Apply brand Button — primary, disabled unless a different brand is staged, spinner while the request is in flight
- Revert Button — flat, same enablement
- Confirm Dialog — title Switch controller brand, one of the two message variants, Cancel and OK
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.
Source Code Path
See HiNC App Anatomy for git repository links.
Web Application
HiNC-2025-webservice (Quasar CLI SPA):
wwwroot-src/src/components/controlTree/SoftNcBrandPanel.vue— 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.wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue— the shared no-runner line this panel renders in place of its body.wwwroot-src/src/composables/useSoftNcRunner.ts— 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.wwwroot-src/src/api/softNcRunner.ts— the five-brand option constant, the snapshot shape, and the typed brand-switch request carrying the target and the carry flag.wwwroot-src/src/api/http.ts— 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.wwwroot-src/src/components/controlTree/softNcItemTypes.ts— 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.wwwroot-src/src/components/controlTree/useControlTreeHost.ts— the branch root the scope resolves to, and the rebuild that expands it and re-resolves the selection.wwwroot-src/src/components/controlTree/SoftNcRunnerRootPanel.vue— 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.wwwroot-src/src/i18n/en/softNc.ts— the tree label, the select label, the checkbox caption, the banner sentence, the button captions and the two toast contexts.wwwroot-src/src/i18n/en/dialog.ts— the dialog title, the two message variants and the current fallback used when the snapshot carries no brand marker.Mech/SoftNcRunnerController.cs— 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.Common/ApiError.cs— the coded no-project payload, the one refusal here a non-English locale can re-render.
HiAPI Engine
HiMech/NcParsers/SoftNcRunner.cs— 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.HiMech/NcParsers/NcRunnerSuit.cs— the suit that holds the runner beside the project's own per-case dependency list, and the proxy wiring the swap triggers.HiMech/NcParsers/ControllerPresetWriter.cs— the brand token list and preset factory that mirror the REST action's switch, and the controller-file extension a saved runner takes.HiMech/NcParsers/Dependencys/CncBrandDependency.cs— the brand marker itself: five tokens and one free-form string property, with no behaviour of its own; its readers live elsewhere.HiMech/NcOpt/SoftNcOptProc.cs— where NC optimization reads the marker back out of the session's effective dependency list, for the writeback grammar.HiMech/NcOpt/NcOptPieceClassifier.cs— the re-interpolation guards, none of them keyed on the brand; the two a klartext arc can reach key on the centre's provenance and on the piece's own incremental words instead.HiMech/NcParsers/Keywords/Generic/ArcCenterSource.cs— the two centre-provenance stamps: the one the arc guard refuses, and the one that tells the writeback a split arc's fragments share theCCline unchanged. An arc whose own block states its centre carries neither.HiMech/NcParsers/NcWriteback/NcPatchWriter.cs— the writeback grammar the marker selects: the variable prefix, the comment spans, the Heidenhain keyword set, the pre-feed word patterns that place an insertedF, and the trailing-comment shape the embedded source note takes.HiMech/NcParsers/PostLogicSyntaxs/RadiusCompensationSyntax.cs— the negative-radius validation warning raised only where the marker reads Heidenhain, in a syntax all five presets carry.HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs— the work-coordinate provider contract the carry reads and writes through, including the id enumeration that decides what crosses.HiMech/NcParsers/Dependencys/IsoCoordinateAddressMap.cs— 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.HiMech/NcParsers/Dependencys/Siemens/SiemensFrameTable.cs— the frame table's own allocation, G54–G57 plus the extended G505–G599 series, which is what a carry into Siemens accepts.HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs— the datum table's preset rows and the six of them aliased onto G54–G59.HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs— 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.HiMech/NcParsers/Dependencys/Generic/ToolOffsetTableProxy.cs— the proxy every preset carries, and therefore the reason the generic tool-offset table survives every switch.HiMech/NcParsers/Dependencys/Generic/ToolingMcConfig.cs— the tool-change position's preset default, one of the runner-owned values every apply resets.HiMech/NcParsers/Dependencys/Generic/GenericBlockSkipConfig.cs— the block-skip layers' preset default, and the config the Heidenhain preset does not carry.HiMech/NcParsers/Dependencys/Generic/SubProgramFolderConfig.cs— the subprogram folders' preset defaults.HiNc/MachiningProcs/LocalProjectService.cs— 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.
See Also
- Machine and Controller Plane — the plane this leaf opens, and the storage column that says which of its other leaves this control resets
- Brand Matrix — which nodes each brand grows, so what the branch looks like after the switch is a lookup rather than a surprise
- Editing Contract — the commit and error rules the rest of the branch follows, and which this panel departs from on purpose