Datum Tables
Datum Tables is the pair of Heidenhain per-case ledgers the Controller branch grows together: the
preset rows a CYCL DEF 247 cycle selects, and the datum shift rows a CYCL DEF 7 cycle adds on top
of the selected preset. Both live on the General Setup page at /general-setup under the
Control-Tree ids equipment/controller/program-data/datum-presets and
equipment/controller/program-data/datum-shifts — reached as
?tree=equipment/controller/program-data/datum-presets and
?tree=equipment/controller/program-data/datum-shifts — and the tree labels them
Datum Presets (Q339) and Datum Shifts (D). One snapshot flag grows the pair, one component
serves both nodes, and one of the two tables is also the object the Work Coordinates leaf edits.
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.
Two Nodes, One Component
The branch builder pushes both nodes inside a single test on the snapshot's datum flag, so the two rows appear and disappear as a pair and never one without the other. That flag probes HeidenhainDatumTable over the runner's proxy-resolved dependency list, and only the Heidenhain preset carries it — which brand grows which leaf is Brand Matrix.
Both nodes carry the same item type, so the editor row mounts the same component for either
selection. The component takes its role from the last segment of the node id it is given: an id
ending in datum-shifts makes it the shift editor, and anything else makes it the preset editor.
The test is a suffix match with a preset default rather than an equality check over two known ids, so
the preset role is what an unrecognised id falls to.
Four things follow from that role, and nothing else in the panel differs between the two nodes.
| Decided by the role | Datum Presets (Q339) | Datum Shifts (D) |
|---|---|---|
| Which array of the one read is rendered | the preset rows | the shift rows |
| The index column's header | Q339 |
D |
| The description caption above the table | "Datum presets (CYCL DEF 247, Q339 = row). Rows 1–6 double as G54–G59." | "Datum shifts (CYCL DEF 7)." |
| The route segment its writes address | preset |
shift |
One request serves both nodes. The read returns a presence flag together with both arrays, and the panel keeps one and discards the other. Selecting the sibling node changes the node id, which changes the editor row's remount key, so the component remounts and re-fetches the same payload for the other half. Reading the pair therefore costs two identical requests, and neither node can show a value the other's request had already returned.
The preset and shift segments are the only two the panel ever sends; the endpoint's rejection of
a third is one of the departures recorded in
Editing Contract.
What Each Table Holds
Both tables are the same shape — an integer row number against an X / Y / Z translation — and both are seeded with rows 1 through 20 at zero when the table is constructed. What separates them is which klartext cycle reads which, and how the value is applied.
- Datum Presets (Q339) is the preset store.
CYCL DEF 247 DATUM SETTINGwithQ339=Nselects row N, and its translation becomes the block's active coordinate offset. The same parser claims the DIN/ISO spellingG247 Q339=+Nand stamps the identical cycle record, so both dialects reach one store. - Datum Shifts (D) is the shift store.
CYCL DEF 7with a#Nrow index reads row N, and the translation composes on top of the active preset rather than replacing it — the two land in separate entries of the block's transform chain.CYCL DEF 7written with direct X / Y / Z values instead of a#index reads no table row at all.
The two stores sit on one dependency and are two dictionaries of it, which is why one flag grows two nodes and one request answers for both.
Neither store is a parameter table. HeidenhainDatumTable does not derive from the controller parameter base every brand's machine settings hang off, and the Heidenhain preset carries it as an entry of its own beside the machine-parameter entry. So no datum row has a native parameter number, and none of these values appears in Native Parameters — unlike the Fanuc and Syntec work coordinates, which are parameter addresses and do.
Where the table is stored
The Heidenhain preset does not carry a datum table. It carries HeidenhainDatumTableProxy, a get-or-create placeholder that installs a bare new table into the project's per-case list when that list holds none of the type, and resolves to the project's own table when it does. The proxy holds no seed and serializes as an empty element, so a controller file saved from the branch root carries none of these values, and installing a Heidenhain runner file over a Heidenhain project leaves the rows already edited exactly where they are. The plane's storage model in full is Program Data Plane.
The Columns and the Axis Set
Each leaf renders one dense markup table: the row-number column, three value columns, and an unlabelled action column.
- The row-number cell is bold plain text and cannot be edited. Its header is the role's literal —
Q339orD. - The three value headers read
X (mm),Y (mm)andZ (mm). Those three and the index literal beside them are hard-coded rather than translated, so they read the same under any locale, while the description caption, both empty bodies and the action button's tooltip are translated. - The action column has no header text and holds one button per row.
The axis set is fixed at three and does not follow the machine. A datum row is a three-component
translation on both sides of the wire, so the columns are X, Y and Z whatever the machining chain
declares — a fourth or rotary axis has no column here and no stored datum component. That is the
opposite of the branch's Per-Axis Tables, whose rows are the
chain's own axis names. The mm in the header is a label: nothing on the path from the stored
translation to the cell converts anything.
Rows are whatever the read returned, in row-number order. A table built by the proxy opens with twenty rows of zeros in each node; a table deserialized from a project file carries the rows that file holds.
Editing a Row
Every value cell is the shared numeric field, so it commits on blur or on Enter and never per keystroke; its parsing, its bounds behaviour and the second commit that follows Enter are Numeric Input. No cell here passes a minimum or a maximum, so a negative offset is accepted and stored.
The write is the whole row. A commit assigns the new number into the local row and then sends that row's three components as they stand to the row's number; a failure restores the one component the handler captured and raises the branch's standard toast, though the request carried all three. That shape, the toast it ends in and what a failed write leaves behind are Editing Contract.
A cleared cell parses to null, and Infinity or NaN parse to a non-finite number; the handler
returns before the request in every one of those cases. Nothing is written and nothing is restored,
so the cell is left blank — or showing the literal Infinity — while the stored number stands
unchanged, until the panel is remounted by selecting another node and coming back.
The action button is labelled with the single character 0 and carries the tooltip “Reset to
zero”. It writes zeros into all three components of that row through a route of its own rather than
through the row setter, and it is not a delete: the row stays, at zero. It is also the one write on
these two leaves that is not optimistic — the request resolves first, and only then are the three
local cells set to zero. No confirmation guards it.
Neither leaf has an add control, a delete control, a draft or a save button, and the component declares no events at all, so no edit made here rebuilds the branch or marks anything dirty.
The Preset Table Is Also the Work-Coordinate Table
This is the pairing with Work Coordinates, and it holds for exactly one of the two tables.
HeidenhainDatumTable implements IIsoCoordinateConfig, and the work-coordinate endpoint reads and writes through the first implementer of that interface in the runner's proxy-resolved list. On the Heidenhain preset the datum proxy is the second entry of the dependency list, immediately after the brand marker, and no other entry that preset carries implements the interface — the Heidenhain machine-parameter table does not. So on Heidenhain the object behind Work Coordinates is this datum table, and the snapshot's coordinate-kind field reports the Heidenhain arm for that same reason.
The alias is narrow, and one-directional in coverage.
- Datum Presets (Q339) aliases. The ISO face maps
G54throughG59onto preset rows 1 through 6, in order, on both the read and the write, and enumerates no id outside that series — one for each of those six rows the table holds, which is all six unless a stored project table omits one of them. Editing theQ339row 3 cells changes what Work Coordinates shows forG56, and an edit made onG56changes row 3 — one instance, one cell, two faces. The row-zeroing button reaches the same cells. - Datum Shifts (D) does not alias. Nothing on the ISO face touches the shift store: the id enumeration reads the preset dictionary only, and both the offset getter and the offset setter resolve to preset rows. The shift table has exactly one editing surface, the leaf on this page.
- Preset rows 7 through 20 have no work-coordinate face either. The ISO face reserves six rows
for the
G54series and enumerates no id pastG59, so fourteen preset rows and all twenty shift rows are reachable from this page and from nowhere else on the branch.
The two faces are never on screen together — the editor row mounts one panel at a time — and each panel reads once on mount, so moving the selection between the two nodes is itself the refresh. What a mounted panel therefore does not see is Editing Contract.
What a brand switch does to the pair
The brand switch's carry option reads the outgoing runner's coordinate provider and writes the values into the incoming one, for the ids the incoming provider enumerates. On this table that means at most six values, landing in preset rows 1 through 6; a switch away from Heidenhain reads those same six rows out. No shift row is ever carried, and neither are preset rows 7 through 20.
A switch away from Heidenhain then sweeps the datum table itself, because no other brand preset proxies it — both stores go, and switching back materializes a fresh table of zeros rather than restoring them. The staged select, its confirmation and the rest of that operation are Brand Switch.
What the Rows Feed
At run time both stores are read and neither is written. No syntax in the parse pipeline writes either dictionary — every writer is one of the four surfaces named below — so playing a program cannot change a datum row.
The preset path. A CYCL DEF 247 block resolves its Q339 number — a literal, or the number a
Q-expression evaluated to — looks the row up, and writes a synthetic coordinate id naming that row
alongside the translation. The shared ISO coordinate syntax then keeps the offset alive on following
blocks by re-resolving that synthetic id against the same table, which is how a mid-program edit to a
row takes effect on the blocks after the declaration. A CYCL DEF 247 whose Q339 is not a number
resolves nothing, leaves the active datum unchanged, and reports the validation warning "CYCL DEF
247 without a literal Q339 preset number; the active datum is kept unchanged."
A successful preset selection clears the active shift. That is the one path in the cycle handler that does not carry the previous block's shift forward.
The shift path. A CYCL DEF 7 block carrying a # index reads that shift row and composes it as
a second, separate transform entry, so preset and shift add rather than replace. On blocks with no
cycle the shift is carried modally, and a numbered shift re-resolves from the table on every block,
exactly as the preset does. A CYCL DEF 7 written with direct values carries them forward verbatim
instead and needs no datum table at all; cancelling is a zero shift. A CYCL DEF 7 carrying neither
form reports "CYCL DEF 7 carries neither a #-table index nor direct X/Y/Z values; the active datum
shift is kept unchanged."
A G54 with axis words is not a table read. On the Heidenhain syntax list the datum-shift parser
claims a G54 that is followed by at least one axis value and routes it into the direct-shift flow,
which reads no row; a bare G54 is left alone and reaches the ISO path, where it resolves preset row
- So
G54means a table lookup or a literal shift depending on what follows it in the block.
A row number that resolves to nothing yields a zero translation rather than an error, on both paths.
And nothing is active at the first block: the Heidenhain preset's initializer seeds no coordinate
section at all, unlike the Fanuc family's G54 and the Siemens cancel frame, so a program that
issues no datum cycle and no G54 machines with no offset applied.
Where Non-Zero Rows Come From
Four surfaces put a value into these tables, and only the first is on this page.
- The two leaves themselves, per cell or per row.
- Work Coordinates, for preset rows 1 through 6 only, through the alias above.
- A brand switch onto Heidenhain with its carry option on, through that same ISO face and into the table the incoming preset resolves: at most six offsets, into preset rows 1 through 6 — Brand Switch.
- A project-load migration. Opening a project file that carries the superseded controller environment element and no per-case list populates the materialized per-case tables from that element, and the datum half runs only when the legacy brand element reads Heidenhain. It copies preset entries and shift entries by row number, overwriting a row it names and leaving every other row as seeded. The legacy work-coordinate table is replayed first, through the same ISO face that aliases onto preset rows 1 through 6, so where the two overlap the explicit datum copy is what stands.
Layout
- General Setup Control Tree — the left dock of
/general-setup- Controller Node Row
- Program Data Group Row —
equipment/controller/program-data- Datum Presets (Q339) Node Row —
equipment/controller/program-data/datum-presets - Datum Shifts (D) Node Row —
equipment/controller/program-data/datum-shifts
- Datum Presets (Q339) Node Row —
- Program Data Group Row —
- Controller Node Row
- Editor Row — the panel of whichever node is selected, one at a time
- Datum Presets (Q339) / Datum Shifts (D) Panel — one component, its role taken from the
selected id's last segment
- No-Runner Body — "No NC runner — load a project first."; the shared empty state, shown instead of everything below while the snapshot reports no runner
- Absent-Table Body — "No Heidenhain datum table on the active runner."; shown instead of everything below while the panel's own read reports no table
- Description Caption — grey, one line: "Datum presets (CYCL DEF 247, Q339 = row). Rows 1–6 double as G54–G59." or "Datum shifts (CYCL DEF 7)."
- Datum Table — dense, flat, bordered; no sort, no pagination, no column menu
- Header Row —
Q339orD, thenX (mm),Y (mm)andZ (mm), then an unlabelled action column - Datum Row, one per row the read returned, in row-number order
- Row Number Label — bold plain text, never editable
- Value Numeric Field, one per axis column — no minimum, no maximum, no unit suffix
- 0 Button — flat, dense, centred, tooltip “Reset to zero”
- Header Row —
- Toast — negative, three seconds, the panel's context followed by the server's own message
- Datum Presets (Q339) / Datum Shifts (D) Panel — one component, its role taken from the
selected id's last segment
The panel carries no heading, no toolbar, no add row, no delete button, no toggle, no save button and no dialog.
Source Code Path
See HiNC App Anatomy for git repository links.
Web Application
HiNC-2025-webservice (Quasar CLI SPA):
wwwroot-src/src/components/controlTree/SoftNcDatumTablePanel.vue— the one component behind both leaves: the role taken from the node id's suffix with its preset default, the four things that role decides, the literal column headers, the whole-row cell commit with its non-finite guard, and the non-optimistic row reset.wwwroot-src/src/components/controlTree/softNcItemTypes.ts— the branch builder: the two node ids and label keys pushed together inside one flag test, and the single item type both carry.wwwroot-src/src/components/controlTree/PrimarySlavePanel.vue— the editor row: the remount key that makes a move between the two nodes a re-fetch, and the events this panel declares none of.wwwroot-src/src/components/controlTree/SoftNcEmptyState.vue— the shared no-runner line the panel opens with.wwwroot-src/src/components/widgets/NumericInput.vue— the value cell: commit on blur or Enter, the empty-text-to-null parse, and the infinity and NaN literals the panel's handler then rejects.wwwroot-src/src/api/softNcRunner.ts— the read that returns both tables and its row shape, the two role values, the per-row setter and the reset call, and the snapshot's datum flag.wwwroot-src/src/composables/useSoftNcRunner.ts— the shared snapshot the first empty layer reads.wwwroot-src/src/api/http.ts— the envelope helper that turns a success-flagged failure body into a thrown error.wwwroot-src/src/components/controlTree/SoftNcWorkCoordinatesPanel.vue— the second face on the preset rows: the same six cells addressed by coordinate id, and the caption that names the mapping.wwwroot-src/src/i18n/en/softNc.ts— the two node labels, the two descriptions, the absent-table body and the three error contexts.wwwroot-src/src/i18n/en/common.ts— the reset button's tooltip.Mech/SoftNcRunnerController.cs— the REST surface: the datum flag's probe in the snapshot builder, the read that returns both dictionaries ordered by row number, the per-row writer and the zeroing route behind one shared table-segment guard, the work-coordinate routes that reach the same object through the ISO interface, and the brand switch's capture-swap-carry-sweep.
HiAPI Engine
HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTable.cs— the two dictionaries, their twenty seeded rows, the accessors both cycles read through, and the ISO face: theG54–G59map onto preset rows 1–6, the synthetic preset ids it also resolves, and the id enumeration that stops at six.HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainDatumTableProxy.cs— the seedless get-or-create placeholder the preset carries, and the bare table it installs into a project holding none.HiMech/NcParsers/Dependencys/IIsoCoordinateConfig.cs— the offset-provider contract whose first implementer the work-coordinate face resolves.HiMech/NcParsers/Dependencys/Heidenhain/HeidenhainParameterTable.cs— the other Heidenhain per-case table: it derives from the controller parameter base, which declares no coordinate contract, which is why the datum table is the preset's only implementer of one.HiMech/NcParsers/SoftNcRunner.cs— the Heidenhain preset's dependency list and the position of the datum proxy in it, the proxy resolution every read goes through, and the legacy per-case populate that fills both dictionaries by row number after replaying the legacy coordinate table.HiMech/NcParsers/ParsingSyntaxs/Heidenhain/CyclDefSyntaxs/HeidenhainDatumSettingSyntax.cs— theCYCL DEF 247parser and the DIN/ISOG247spelling it claims for the same cycle record.HiMech/NcParsers/ParsingSyntaxs/Heidenhain/CyclDefSyntaxs/HeidenhainDatumShiftSyntax.cs— theCYCL DEF 7parser: the#row index, the direct-value form, and the axis-word test that decides whether aG54becomes a direct shift or stays on the ISO path.HiMech/NcParsers/LogicSyntaxs/Heidenhain/HeidenhainCoordinateOffsetSyntax.cs— the resolver: the preset lookup and its synthetic id, the additive shift in its own transform entry, the modal carry that re-resolves a numbered shift from the table, the shift reset a successful preset selection performs, and the two validation warnings.HiMech/NcParsers/LogicSyntaxs/IsoCoordinateOffsetSyntax.cs— theG54-series path and the modal lookback that re-queries the table for the carried id.HiMech/NcParsers/LogicSyntaxs/CoordinateOffsetUtil.cs— the offset resolution and the translation composed onto the block's transform chain.HiMech/NcParsers/Syntaxs/HeidenhainSyntaxUtil.cs— the Heidenhain syntax list carrying both the ISO coordinate syntax and the datum-cycle syntax.HiMech/NcParsers/Initializers/StaticInitializer.cs— the Heidenhain initializer that seeds no coordinate section, against the Fanuc and Siemens ones that do.HiGeom/Geom/Vec3d.cs— the three-component translation a datum row stores, and the zero it defaults to.HiNc/MachiningProcs/MachiningProject.cs— the load path that wires the proxies and then runs the legacy per-case populate on a project carrying no per-case list.
See Also
- Program Data Plane — the plane these two leaves sit on, where their table is stored, and the per-case tables beside them a brand switch keeps or sweeps
- Work Coordinates — the second face on the preset rows: the same six cells
addressed as
G54–G59, on the one brand where two nodes edit one object