Per-Axis Tables
Four leaves of the Controller branch's machine plane are one table each, keyed on the machine's
axes: Machine Limits (Stroke), Rapid Feedrates, Home / G28 Reference and Tool-Change
Position. They live on the General Setup page at /general-setup under the Control-Tree ids
equipment/controller/machine/limits, equipment/controller/machine/rapid,
equipment/controller/machine/home and equipment/controller/machine/tool-change; none of them is
gated by a snapshot flag, so every brand grows all four. Two components serve them — one panel
behind the first three, discriminating on the id's last segment, and a second behind Tool-Change
Position — and the differences between those two components are what this page is mostly about.
Important
Anatomy makes no claim that any id named here is stable across versions. The migration hop that
keeps an older ?tree= value resolving is described in
Tree Ids and Routes.
Where the Rows Come From
Every row on all four leaves is one axis of the runner's machine-axis configuration, read as AxisNames through the proxy-resolved dependency list. On all five brand presets that configuration is the brand parameter table itself: ControllerParameterTableBase declares IMachineAxisConfig alongside IStrokeLimitConfig, IRapidFeedrateConfig and IHomeMcConfig, so one object supplies both the row set and three of the four value sets. Which brand carries which table is Brand Matrix.
The table's axis set is a single parameter row: the per-axis integer bucket under the brand's axis-type number, whose keys are the axis names and whose values are the AxisType — linear, rotary or spindle. Adding an axis therefore means adding a key to that one row, and exactly one surface on this branch reaches it.
- Neither panel here has an add-row or delete-row control. Both render a fixed header over one
<tr>per axis and nothing else. - The four per-axis routes behind them write one axis' value and nothing more: the caller names an axis, and the endpoint writes into the stroke-limit, rapid-rate, home or tool-change store under that name. None of them touches the axis-type row, so none of them changes which rows the next read returns.
- Parameters (Native) edits the axis-type row itself, and
is the one place on the branch that widens the axis set: its Add / Set footer takes a
free-text axis name, so the section Axis (integer), the brand's axis-type number, a name and a
value of
0linear,1rotary or2spindle adds an axis, which then appears on all four leaves. Its grid cannot — the per-axis columns are the union of the axis names already present, so a cell can be written only for an axis that exists — and its delete button removes a whole parameter row, every axis cell of one number at once, rather than one axis.
Axes arrive from the machining chain. Whenever the active runner suit or the chain changes, the project service re-binds the suit and calls ConfigureByMachiningChain(API), which walks the chain's axis codes and their transformers: a rotating transformer writes the axis as rotary — and, on a brand parameter table, writes its reference position as 0 deg and its rapid rate as 36000 deg/min — any other transformer writes the axis as linear, and every chain axis whose reference position is still unset is seeded to 0. That is the mechanism behind the description above the Home / G28 Reference table, "Axes without a value are seeded to 0 when a machine tool attaches."
The stamp adds rows and never removes one. The generic axis configuration carried by the CSV and CL runner suits is cleared before the walk; a brand parameter table is not, and keeps every axis it already held. An axis dropped from the chain therefore keeps its rows on all four leaves, and no surface removes it individually. The three-axis default table every brand preset seeds is why a project with no machine tool attached still shows X, Y and Z.
Two values are the exception, and only on a rotary chain axis of a brand table: that branch of the walk is unconditional, so it rewrites the axis' reference position to 0 deg and its rapid rate to 36000 deg/min whether or not one was stored. The walk re-runs on every suit re-bind, so an edited Home Position or Rapid Rate on a rotary axis is back at those two numbers once the chain or the active suit changes. A linear axis' values, and every value on an axis the chain does not carry, are left untouched.
The caption below the first three tables records the row half of that: "Axis rows follow the Machine Tool chain; they cannot be added here." Where the chain itself is edited is Machine Tool.
The Value Columns
Each leaf renders the axis column, its own value column or columns, and a unit column.
| Leaf | Value columns | Empty cell means |
|---|---|---|
| Machine Limits (Stroke) | + Limit, − Limit | no limit set for that axis and side |
| Rapid Feedrates | Rapid Rate | never empty — see below |
| Home / G28 Reference | Home Position | no reference position stored |
| Tool-Change Position | Stays put, Position | see Stays Put |
The stroke-limit and home readers return an optional value and hand null straight through, so an unset cell is blank. The rapid reader does not: it returns the stored rate, or — when the axis has no rate of its own — the base class's fixed default, 20000 mm/min for a linear axis and 36000 deg/min for a rotary one. So a Rapid Rate cell is always a number, and a number in it is not proof that the axis has a row.
A table cloned fresh from a brand's preset seed decides what the three leaves then open on, and all four brand defaults agree in shape: a linear X, Y and Z, a reference position of 0 on each, and a rapid rate per axis — and no stroke limit at all. Machine Limits therefore opens entirely blank on every brand, while Rapid Feedrates and Home / G28 Reference open populated. Whether a brand switch clones a fresh table or keeps the project's own is Machine and Controller Plane.
The Unit Column
The unit column's header is the translated label Unit; its cell text is not translated and does not come from the server. Each panel computes it in the browser from the row's rotary flag, using literal strings:
| Leaf | Linear axis | Rotary axis |
|---|---|---|
| Machine Limits (Stroke) | mm |
deg |
| Rapid Feedrates | mm/min |
deg/min |
| Home / G28 Reference | mm |
deg |
| Tool-Change Position | mm |
deg |
The flag itself is server-side, and it is the axis type rather than a separate setting: an axis stored as rotary or as spindle reports rotary, so a spindle-mode axis is labelled in degrees.
The unit is a label, not a conversion. The base class writes a linear and a rotary rapid rate into the same per-axis cell and reads them back through the same lookup, differing only in the default returned when the cell is absent; the stroke-limit and home accessors are equally unit-blind. Nothing on these three leaves converts, so their numbers are the raw stored numbers, and the same values appear in Parameters (Native) under the brand's own parameter number:
| Role | Fanuc, Mazak | Syntec | Siemens | Heidenhain |
|---|---|---|---|---|
| Axis type — the row set | 1006 | 1006 | 30300 | 400 |
| Reference position (home) | 1240 | 1240 | 34010 | 410 |
| Positive stroke limit | 1300 | 1300 | 36100 | 420 |
| Negative stroke limit | 1320 | 1320 | 36110 | 430 |
| Rapid rate | 1420 | 1420 | 32000 | 1010 |
Mazak shares the Fanuc column because the Mazak preset carries the Fanuc parameter-table proxy; Siemens spends its rapid-rate role on the max-axis-velocity machine datum.
Tool-Change Position is the exception on both counts: its values live on ToolingMcConfig, a plain runner-owned dependency rather than a parameter table, so they appear in no native form at all and a brand apply returns them to the preset defaults — see Machine and Controller Plane.
Stays Put, and the Sentinel Behind It
IToolingMcConfig stores one number per axis, and the contract gives one number a second meaning: NaN means the axis stays where it is during a tool change. The read splits that back into two fields — a stored NaN becomes the Stays put flag with no position, anything else becomes the position — and the panel renders the flag as a checkbox and the position as a numeric field disabled while the box is ticked.
Writing goes the same way round. Ticking the box sends the stay flag and the reader writes NaN. Unticking it sends a position, and because the panel clears its own cell when the box is ticked, the position it sends on the way back is 0 — so unticking parks the axis at machine zero rather than restoring what was there before.
A third state exists and is not the sentinel: an axis with no entry in the map at all. Its box is unticked and its Position cell blank, because the reader reports no position and no stay. At run time the two are indistinguishable — the tool-change motion overlays only those axes that carry a number and are not NaN onto the current pose, and leaves every other axis alone. So the machine stays put either way; the tick mark distinguishes only how that was recorded.
The overlay has a vocabulary of its own, narrower than the table. X, Y and Z always take part; every other axis takes part only while the machine declares it rotary. A linear axis outside those three is stored, shown and editable on this leaf, and never moved.
The preset default is exactly this mixture: X and Y carry the sentinel, Z carries 0, and no rotary axis carries an entry, which is why a rotary axis usually opens unticked and blank.
Above the table sits Tool-change mechanism time, a single field in seconds bound to ToolingTime. It becomes the duration of the tool change step the runner emits, and it is the changer mechanism alone — the axis travel to and from the position is timed separately from the positions in the table. It is 0 on every brand preset, and it is written into the runner file only when non-zero.
One Reader, Two Readings of Its Presence Flag
All four reads answer the same shape: a present flag plus one row per axis. present reports
whether the backing configuration resolved — stroke limit, rapid feedrate, home or tool change — and
it is computed independently of the rows. Each reader builds the row list from the axis set
whether or not that configuration resolved, and fills the value cells from an optional reference, so
a missing configuration yields blank limit and home cells, a 0 rapid rate, and rows all the same.
The two components read the answer differently, and this is the sharpest divergence between them.
- The axis-table panel discards
presententirely. Its typed reader parses the flag; the panel assigns only the rows. Its second empty layer keys on the row count instead: with no axes it shows "No machine axes yet — these rows are driven by the Machine Tool chain (MechBuilder). Attach a machine tool to edit per-axis values.", and with axes it renders the table whatever the flag said. - The tool-change panel reads
presentand, when it is false, shows "No tool-change config on the active runner."
What that costs: a runner that has axes but no stroke-limit, rapid-feedrate or home configuration renders a full, editable table of blank or zero cells, and every commit fails with a toast naming the missing dependency instead of the panel saying the table is absent. On the five shipped brand presets the state cannot arise, because the one object that supplies the axis set supplies all three configurations too. The reachable case is the mirror one, and it reaches the two panels differently: deleting the axis-type parameter row from Parameters (Native) empties the axis set, and the three axis-table leaves fall to the no-axes line while Tool-Change Position keeps its rows, because its reader falls back to the tool-change configuration's own axis keys when the axis set is empty. Rows recovered that way are reported as linear whatever they are.
The tool-change flag itself is never false on a shipped preset: all five carry the tool-change configuration as a plain entry, so its empty line is reachable only on a hand-built or file-loaded runner.
Above both layers sits the shared no-runner guard every leaf of the branch carries; it, and the commit, rollback and toast rules the four leaves share with the rest of the branch, are Editing Contract.
Editing a Cell
Numeric cells are the shared numeric field, so a value commits on blur or on Enter and never per keystroke — its full contract is Numeric Input. The Stays put checkbox commits on the click. Three details govern how these four leaves commit:
- The writes are per cell, not per row. Editing + Limit sends that side alone, and the endpoint leaves a side it was not sent unchanged. The branch's row-shaped editors do not agree on this: a tool-offset or work-coordinate edit resends the whole row, while Parameters (Native) writes one axis cell at a time as these four do.
- Clearing a cell writes nothing. An emptied field parses to null and the handler returns before the request, so the box is left blank on screen while the stored number stands, and the number returns when the panel is remounted by selecting another node and coming back. There is consequently no way to unset a stroke limit or a reference position from these panels; the only surface that removes one is the native leaf's whole-row delete.
- No cell is bounded. None of the four tables passes a minimum or a maximum to its fields, so a negative limit or a negative rapid rate is accepted and stored. The mechanism time is the one bounded field on the four leaves, at zero or above.
What the Values Do When a Program Plays
The stroke limits are checked at each played step against the chain's current machine pose while the session's stroke-limit check is on; a position past a configured limit is reported as a stroke-limit validation error anchored to that step, and pauses the player when pause-on-failure is set. The check walks a fixed axis vocabulary — X, Y and Z from the machine point, A, B and C from its orientation — so a limit stored against any other axis name is kept and shown here but never tested. Rapid-traverse timing has the same shape and the same six names: a G00 move is timed axis-by-axis at each axis' rate and takes the slowest, falling back to the same 20000 mm/min and 36000 deg/min defaults when no configuration answers.
The reference positions reach further, but only at the start. The initializer that sets the machine pose at the first block writes every declared axis, reading each axis' stored reference and falling back to 0 for one that has none. The G28 reference return reads the same store through a narrower window: X, Y and Z for its linear stage, and A, B or C for its rotary stage — and a rotary letter only while the machine declares that axis rotary and a reference position is stored for it, an unconfigured one being reported as a validation error instead. So an axis outside the six canonical names still carries its stored reference into the pose at the first block, while its stroke limit, its rapid rate and its G28 return are all inert.
Layout
- General Setup Control Tree — the left dock of
/general-setup- Controller Node Row
- Machine / Controller Group Row
- Machine Limits (Stroke) Node Row —
equipment/controller/machine/limits - Rapid Feedrates Node Row —
equipment/controller/machine/rapid - Home / G28 Reference Node Row —
equipment/controller/machine/home - Tool-Change Position Node Row —
equipment/controller/machine/tool-change
- Machine Limits (Stroke) Node Row —
- Machine / Controller Group Row
- Controller Node Row
- Editor Row — the panel of whichever node is selected
- Machine Limits (Stroke) / Rapid Feedrates / Home / G28 Reference Panel — one
component, its role taken from the selected id's last segment
- Description Caption — "Machine travel limits per axis (empty = no limit set). Motions beyond a limit raise a stroke-limit validation error.", "G00 rapid traverse rate per axis." or "G28 first reference (home) machine coordinate per axis. Axes without a value are seeded to 0 when a machine tool attaches."
- Axis Table — dense, flat, bordered
- Header Row — Axis, then + Limit and − Limit, or Rapid Rate, or Home Position, then Unit
- Axis Row, one per axis
- Axis Name Label — bold, not editable
- Value Numeric Field, one per value column
- Unit Label —
mm/deg, ormm/min/deg/minon Rapid Feedrates
- Footer Caption — "Axis rows follow the Machine Tool chain; they cannot be added here."
- No-Axes Block — replaces the description, the table and the footer caption when the axis set is empty: a precision-manufacturing icon beside "No machine axes yet — these rows are driven by the Machine Tool chain (MechBuilder). Attach a machine tool to edit per-axis values."
- Tool-Change Position Panel
- Tool-change mechanism time Numeric Field — suffixed
s, minimum 0 - Axis Table — dense, flat, bordered
- Header Row — Axis, Stays put, Position, Unit
- Axis Row, one per axis
- Axis Name Label — bold, not editable
- Stays put CheckBox
- Position Numeric Field — disabled while Stays put is ticked
- Unit Label —
mm/deg
- Footer Caption — “Stays put” leaves the axis where it is during a tool change.
- No-Config Block — replaces the field, the table and the caption: "No tool-change config on the active runner."
- Tool-change mechanism time Numeric Field — suffixed
- Shared Empty State — replaces either panel's whole body while the snapshot reports no runner: "No NC runner — load a project first."
- Machine Limits (Stroke) / Rapid Feedrates / Home / G28 Reference Panel — one
component, its role taken from the selected id's last segment
Source Code Path
See HiNC App Anatomy for git repository links.
Web Application
HiNC-2025-webservice (Quasar CLI SPA):
wwwroot-src/src/components/controlTree/SoftNcAxisTablePanel.vue— the panel behind the first three leaves: the role taken from the node id's last segment, the value columns and description per role, the unit strings computed from the row's rotary flag, the row-count empty layer that discards the read's presence flag, and the per-cell commit that returns on a cleared field.wwwroot-src/src/components/controlTree/SoftNcToolChangePanel.vue— the tool-change panel: the mechanism-time field, the Stays put checkbox that clears the position and sends 0 on the way back, the disabled position cell, and the presence-flag empty layer.wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue— the shared no-runner line both panels open with.wwwroot-src/src/components/widgets/NumericInput.vue— the numeric cell: commit on blur or Enter, and the empty-text-to-null parse the panels reject.wwwroot-src/src/components/controlTree/softNcItemTypes.ts— the four node ids, their labels and label keys, and the two item types that bind them to the two panels.wwwroot-src/src/api/softNcRunner.ts— the four readers and their setters: the row shapes, the presence flag both panels receive, the per-side stroke-limit payload, and the stay-or-position tool-change payload.wwwroot-src/src/composables/useSoftNcRunner.ts— the shared runner snapshot the first empty layer reads.wwwroot-src/src/i18n/en/softNc.ts— the four node labels, the three descriptions, the column headers, the two footer captions and the two empty-state lines quoted above.wwwroot-src/src/i18n/en/common.ts— the shared Axis and Unit column headers.wwwroot-src/src/components/controlTree/SoftNcNativeParamsPanel.vue— the neighbouring leaf that edits the same parameter rows by number: the per-cell axis writes, the grid columns unioned from the existing axis names, the delete that drops a whole row, and the Add / Set footer whose free-text axis field is what widens the axis-type row.Mech/SoftNcRunnerController.cs— the REST surface: the chain-driven axis helper every reader builds its rows from, the four readers with their independently computed presence flags, the four per-axis writers that name an axis but never declare one, the native per-axis-integer route that does, the tool-change fallback to the tool-change configuration's own axis keys, the NaN sentinel write, and the rotary lookup the rapid write re-derives server-side.
HiAPI Engine
HiMech/NcParsers/Dependencys/ControllerParameterTableBase.cs— the one object behind three of the four leaves: the axis set as the keys of the axis-type row, the reference-position, rapid-rate and stroke-limit accessors over the per-axis buckets, the fixed rapid defaults, and the rotary configuration helper the chain walk calls, which writes the axis type, the reference position and the rapid rate together with no guard on the two values.HiMech/NcParsers/Dependencys/IMachineAxisConfig.cs— the axis contract and the axis-type enum whose rotary and spindle members both make a row rotary.HiMech/NcParsers/Dependencys/IStrokeLimitConfig.cs— the limit accessors, and the check itself with its fixed X/Y/Z and A/B/C vocabulary and its validation report.HiMech/NcParsers/Dependencys/IRapidFeedrateConfig.cs— the linear and rotary rate accessors.HiMech/NcParsers/Dependencys/IHomeMcConfig.cs— the reference-position accessors.HiMech/NcParsers/Dependencys/IToolingMcConfig.cs— the tool-change contract: the NaN sentinel and the mechanism time.HiMech/NcParsers/Dependencys/Generic/ToolingMcConfig.cs— the sole implementation: the per-axis map, the preset default of X and Y staying and Z at 0, and the serialization that omits a zero mechanism time.HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTable.cs,HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTable.cs,HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTable.cs,HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs— the parameter numbers in the table above and the three-axis default each brand opens on.HiMech/NcParsers/Dependencys/Fanuc/FanucParameterTableProxy.cs,HiMech/NcParsers/Dependencys/Syntec/SyntecParameterTableProxy.cs,HiMech/NcParsers/Dependencys/Siemens/SiemensMachineDataTableProxy.cs,HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTableProxy.cs— the placeholders the brand presets actually carry: each clones its three-axis seed into the project's own dependency list when that list holds none, and resolves to the project's table when it does.HiMech/NcParsers/SoftNcRunner.cs— the chain walk that stamps axis codes, hands a rotary axis to the unconditional rotary helper on a brand table, and fills a missing reference position; and the five brand presets that decide which parameter table and which tool-change configuration a brand carries.HiNc/MachiningProcs/LocalProjectService.cs— where the chain walk is triggered from, why the generic axis configuration is cleared first and a brand table is not, and the per-step stroke-limit check that reads the limits through the proxy-resolved list.HiMech/MachiningProcs/MachiningSession.cs— the play loop that runs that check once per step and pauses on failure.HiMech/NcParsers/Semantics/LinearMotionUtil.cs— rapid-traverse timing: per-axis rates, the slowest axis, and the same six axis names the stroke check uses.HiMech/NcParsers/Initializers/HomeMcInitializer.cs— the reference positions written for every declared axis at the first block.HiMech/NcParsers/LogicSyntaxs/ReferenceReturnSyntax.cs— the G28 return: the three linear names it resolves a final position for, the three rotary letters it tests against the declared axes, and the validation error a rotary letter the machine has not declared raises.HiMech/NcParsers/LogicSyntaxs/ToolChangeMotionSyntax.cs— the overlay: X, Y and Z plus the declared rotary axes, of which only the ones carrying a non-NaN position move.HiMech/NcParsers/Semantics/ToolChangeSemantic.cs— the tool-change step that carries the mechanism time as its duration.
See Also
- Machine and Controller Plane — the plane these four leaves sit on, and which half of the project each of their values is stored in
- Brand Matrix — why all four are ungated, and which table each brand resolves them through
- Editing Contract — the fetch, commit, rollback and empty-layer rules these panels share with the rest of the branch, and where they are recorded as departing from them