Step Variables Branch
Step Variables is the third of the four roots of the Execution page's Control Tree, below Mission
and Program and above Step Colors: route /execution, Control-Tree path execution/step-variables.
It edits the project's step variables — each a C# formula over a step, saved in the project file and
registered with the step-property registry on every load and every edit — and lists, read-only, the
keys the session registered another way. How to use it is
Step Variables; this page is how it is built.
The Nodes
- The root, item type
StepVariableRoot, idexecution/step-variables. It is in the default set of expanded nodes; a layout a browser already stored keeps its own. - One node per variable of the project's list that has a key, item type
StepVariable, idexecution/step-variables/<key>. When a project file repeats a key, the second and later variables under it add/<occurrence>, counting the variables under the key from 0: the first keeps the bare id, the second is…/<key>/1. The label is the key as typed. - The keys registered at run are not nodes: a play registering keys does not reshape the tree. The root's panel lists them.
The branch's item types come from their own wave module, stepVariableItemTypes.ts, spread into the
registry like every other wave's. Unlike the Mission, Program and Tool House waves, which the host
and the panels also import, it is reached only through the registry module: the node ids the host
and the panels use live in stepVariableIds.ts, which imports nothing.
How the Branch Is Read
The whole branch comes from one GET api/execution/step-variables?lang=: the project's variables,
the keys registered at run (their names in the request's language), the unit options, and the
registry's generation the lists were read at. The root's builder keeps on the nodes what the panels
read, so the panels make no request of their own to show a node. Every read goes through one queue
into the one root object the page's first build makes — that build's own read included — so an
older answer never lands over a newer one, and a move of the registry's generation while the first
build runs is read right behind it. A Show Physics Options flip reads nothing here: on this page it
rebuilds the Mission branch in place, and the Step Colors branch reads its key options again once the
server has taken the flip. After the first read, three things start one:
- A structure change from a panel — an add, an apply, a rename, a delete.
- The registry's generation in the broadcast execution status — it moves when a play begins,
when a script, a CSV (a mapping or a CSV program's title row) or a training registers a key, when
the project's step variables and color trees are registered again after an edit, and when an
edit of a project color tree's names and hues alone is taken in place; either edit may be one
made in another tab. A branch read at the generation the status names is not read again; the
status's timestamp retries a read that failed. It is the step-property registry's count
(
LocalProjectService.StepPropertiesGeneration,stepPropertiesGenerationin the status), not the inspecting-slot generation: a batch of values written onto the steps — a mapping's columns, a training's estimates, a script's bulk write — moves the inspecting-slot generation once per batch (NotifyStepValuesWritten) — the scan that follows flags the strip for a recolor (RefreshDrawing) and raisesInspectingSlotsScanned, which the ClStrip hub broadcasts asUpdatedfor the Color Index Time Chart (theIndividualstrip chart) to read again — and leaves this one, so a batch alone never re-reads the branch; the branch sees a mapping or a training through the keys they register. Every move of the registry's generation but one moves the inspecting-slot generation with it: the in-place edit of a project color tree's names and hues moves the registry's generation alone, and no scan follows, the tree's leaf ids being the same (see Where the Written Values Live). - A language switch, for the registered keys' names. The unit labels are the same in every language.
A read that withdraws the selected variable moves the selection to the branch's root, never to
another variable under the same key, and so does a panel's structure change whose read did not grow
the node its selectId names (a rename or an add whose read failed). A read that fails keeps the
branch as it was —
dropping its nodes would unmount an open editor with its edit, and an empty list would offer a key
the project has to New — and the root's panel says the read failed, with a Refresh that
reads it again.
The Root's Panel
Two lists. Project: the variables, each row selecting its node, with the engine's verdict on it —
compiled, or the reason it is not registered: failed (the formula does not compile), keyTaken
(a built-in step property's key, or under MC.), keyRepeated (a key an earlier variable of the
list has; the first one counts). Registered at run: the session's other keys, with their source,
session (a script, a CSV mapping or a CSV program's title row — the registry does not tell those
apart) or training. A color tree is no step variable, and its key is in neither list — the
project's trees (StepColorTreeList(API))
and a script's
(RegisterStepColorTree(API)) alike.
New step variable checks the key on the spot — not empty, no comma or quote, no /, not . or
.., not a key the list holds — and puts the variable with the formula null, as an add only:
the request carries If-None-Match: *, so a key another tab added since this list was read is
refused with 412 on the key field instead of being overwritten. The new variable's node is
selected unless the user selected something while the request ran.
A Variable's Panel
Key, name, unit (every PhysicsUnit, shown as Step Properties prints it — mm/s, mm³, None
for none — and sent by its name; an unknown name a project file gave is kept as an option of its
own, shown as <name> (unknown unit)), format, and the formula in the shared text editor with the
step-variable completion context. 500 ms after a change the formula goes to compile-check; the
errors are listed under the editor with their line and column, and marked in the editor through
CodeMirror's lint state, which the editor component loads only for a consumer that passes
diagnostics.
- Apply sends
PUTwith the variable's key and occurrence. What is typed while the request is in flight is not part of it and stays; the errors of a refused formula are shown only while the formula is still the text refused. An applied rename re-reads the branch and selects the renamed node, unless the user selected something while it ran. - Delete asks (Delete Step Variable), removes this occurrence (
DELETE {key}?occurrence=), shows the answer'smessagesas notifications — the color trees that refer to the key among them — selects the root unless the user selected something meanwhile, and reads the branch again. The edit is dropped first, so the dirty-switch gate does not ask. - A refusal is shown on the field its code is about — the key, the unit or the format — and
under the editor otherwise, a formula's errors among them; a character a project file cannot hold
is shown on the key or the format when the server names one of them, and under the editor when it
names the name. Its text is the
apiErrorsbundle's for the code, with the response'sargsinterpolated; English shows the server's own message. - The dirty-switch gate asks before a selection change drops an unapplied edit — Cancel, Discard, Apply & Switch. A switch that does not go through the gate — live Follow, a read that withdraws the node — unmounts the editor, which keeps its edit as a draft in memory, per project and per node; the editor that next mounts on that node takes it back. A prompt still open when its editor unmounts answers Cancel, so the selection change waiting on it ends, refused, instead of waiting for good; a Mission row op never asks this editor — it asks only an open Mission editor's gate. An editor that unmounts while its Apply is in flight keeps nothing then: the Apply, when it answers, keeps what is left unapplied as the draft, under the renamed node if it renamed.
- A read while the panel is open hands it a new node under the same id, without a remount. With no edit pending the panel takes the server's row; with one, the edit stays, Discard returns to the server's row, and a row other than the one the edit began from is said: Apply would put this version over it.
- Preview is a button, and on only while a step is selected:
POST previewruns the formula as it stands on the selected step and times it on 100 steps from there — the strip's last 100 when fewer follow the step, the whole strip when it is shorter. The result is marked old when the selection moves or the physics is recomputed, including while the answer was on its way.
REST
api/execution/step-variables (Execution/StepVariableController.cs) edits the project's list in
memory, one edit at a time under the project's step-definition lock
(StepDefinitionEditLock(API), which the
refresh after an edit takes too); POST api/project/save writes it.
| Request | What it does |
|---|---|
GET ?lang= |
the project's variables with their state and errors and the occurrence of a repeated key, the keys registered at run (never a color tree's), the unit options, the generation |
PUT {key}?occurrence= |
adds, changes or renames (a body key other than the route's); compiles first and refuses a formula that does not compile, with its errors; If-None-Match: * makes it an add only |
DELETE {key}?occurrence= |
removes the variable the occurrence names, or every one under the key without it; when no variable is left under the key, messages carries StepVariable-Delete--ReferencedByColorTree, a warning that names, by key, the project's color trees whose terms or leaves' value keys refer to it and says they read it as no value until something registers the key again: the trees are read before the refresh, without asking the registry, so the warning comes even when a built-in property has the key, or a registration of the session, set aside while the variable held the key, takes it back |
POST compile-check |
the formula's errors, from a compilation that loads nothing |
POST preview |
the formula's value on a step, and the time one pass over the strip takes |
A refusal carries a code — StepVariableKeyEmpty, StepVariableKeySlash,
StepVariableKeyDotSegment, StepVariableKeyTaken, StepVariableKeyRepeated,
StepVariableInvalidCharacter, StepVariableBadFormat, StepVariableUnknownUnit,
StepVariableCompileFailed, StepVariableNotFound, StepVariableNoStep,
StepVariableStepNotBuiltYet, or the shared NoProjectLoaded with no project open (a 200 with
success: false on GET, a 400 on PUT, DELETE and preview) — and the args its message names;
StepVariableCompileFailed carries the formula's errors instead. Preview's two step refusals carry
stepIndex and stripCount: StepVariableStepNotBuiltYet answers the next index a built step
takes once a play began — the play stopped before building that step, as at a pause on a
collision, or is building it (running) — and StepVariableNoStep any other index without a built
step, a position the play has not filled, dropped at a stop or failed to build included.
Where the Written Values Live
A value written onto a step under a key — step["key"] = value in a script's step-built handler, a
CSV program's data columns, a CSV mapping's columns, a training's per-step estimates
(AngleOffset_deg, TrainingErrRatio), a script's bulk write — is not a field of the step. It is a
row of the step cache, the per-instance SQLite file the run writes every step's luggage and physics
briefs to, in two tables of that file (HiNc/SqliteUtils/SqliteStepStorage.cs); a step in memory
keeps no field for it: while the step's consignment is live the values ride on the consignment, and
after that the rows hold them (the runner keeps the last published step's as well, until the play
ends). The registry reads a written value ahead of a variable's function.
- Two tables.
StepVariableKeyhas one row per key a value was ever written under since the file was last reset —KeyId INTEGER PRIMARY KEY,Key TEXT NOT NULL UNIQUE— andStepVariableone row per (key, step):KeyId,StepIndex, aValuethat is never NULL,PRIMARY KEY (KeyId, StepIndex),WITHOUT ROWID. Both are cleared and reset with the file. The key table is mirrored in memory (IsStepVariableKeyWritten,WrittenStepVariableKeys), the one thing of these tables that is: a key never written costs no query — a read under it answers no row at once, and the scan below reads no row for it. A key's row is created inside the transaction of the first value written under it and reaches the mirror only after the commit; a delete alone creates none. - What a value is.
StepVariableValue.TryNormalizedecides. A number of any numeric type is stored as a double, a REAL — a value written as anintor afloatreads back as adouble, in memory and from the store alike — and a bool as 1 / 0; a string is a TEXT; a NaN, which the driver refuses to bind as a REAL, goes in as its eight raw bytes (a BLOB) and reads back bit for bit, so a handler's0.0 / 0.0never refuses a batch; null deletes the step's row under the key; a value of any other type is not stored, and the session says so once per key,StepVariable-Write--NotStored. - The write paths. During a play, a step-built handler's
step["key"] = valueand a CSV program's data columns (StateActRunner, through the same indexer) land on the step's live consignment (StepConsignment.WrittenValues, aWrittenStepValuesallocated on the first write), and the runner's writer takes them once and writes their rows in the same transaction as the step's luggage and briefs' rows (SqliteStepStorage.InsertStepConsignments): the handler's write costs no write of its own. A value written after the writer took the consignment's values, or onto a step whose consignment is gone — a script'sstep["key"] = valueafter the play — goes throughIMachiningService.WriteStepVariable: one row,INSERT OR REPLACE, a transaction of its own — a batch of one, which moves the inspecting-slot generation as any batch does (below), so a loop of such writes moves it once per write — and into the runner's copy of the last published step's values when the step is that one (a step object the strip no longer holds writes nothing). A CSV mapping (CsvNcStep.MapSingleByCsvFile), a training (MillingTraining) and a script's bulk write (SessionShell.WriteStepVariables(IEnumerable<StepVariableWrite>), which cuts the sequence into segments itself) write batches ofStepVariableWrite(StepIndex, Key, Value)through the host's batch write (IMachiningService.WriteStepVariables; the script's throughLocalProjectService.WriteStepVariablesFromScript, the same core, its stored keys noted as script-written), one transaction per batch ofStepVariableWrite.SegmentLength= 4,096 writes (the last batch the remainder; a mapping adds a step's columns whole and cuts the batch at the step that reaches 4,096); a batch the store refuses lands nothing and is reported asStepVariable-Write--Fault. Every batch that lands moves the inspecting-slot generation once (LocalProjectService.NotifyStepValuesWritten), and a scan follows 200 ms after the last move: the slots of a written key are refilled, and the Color Index Time Chart (theIndividualstrip chart) and the colours refresh, once per batch at most, not once per write. The step-property registry's generation — the one this branch watches — does not move with a write: a mapping moves it by registering its new columns once its batches are written (MappedStepColumns.Register), a training by registering its two keys. Every move of that generation but one moves the inspecting-slot generation with it (BumpStepPropertiesGeneration): a registered key may be the inspected one, or its definition. The exception is an edit of a project color tree's names and hues alone, taken in place (LocalProjectService.TryUpdateStepColorTreeAppearance): it moves the registry's generation only (BumpStepPropertiesGenerationOnly) and leaves the slots as they are, the tree's leaf ids being the same. - The read path.
step["key"]answers a pin the calling thread holds for the step first (the scan's and the step-file export's rows in hand), then the step's live consignment, then — for a step the host's strip holds, and no other — the runner's copy of the last published step's values (so a handler'spreStep["key"], for a key the handler wrote on that step, reads no row), then the host's store, one row read; a key never written reads null without a read, and so does a key the pin lacks on a step without a live consignment, since a pin holds every key the step has a row under. - The scan. The inspecting-slot scan (
LocalProjectService.ScanInspectingSlots) visits the strip 2,048 steps at a time. When the inspecting key is a written one — or the scan's probe found the key reading written values, a function or a project formula over a written key — it reads every key's rows of the segment in one range read (SqliteStepStorage.FindStepVariables(begin, end): a range search of the value table's primary key under each key of the key table, never a scan of the value table) and pins each step's whole row on its thread while the key's getter fills the slot; a step without a row at or below the writer's watermark (SqliteStepStorage.CommittedStepIndex, read before the rows) is pinned empty, one above it is left to its consignment or the store. So a chart or a colouring of a written key never reads one row per step;WriteStepFilepins the rows per segment the same way. - After a recompute. The step-physics recompute rewrites the briefs, never the written values:
a running sum a handler accumulated from the played physics reads as it was written. A recompute
that wrote names the keys a script or a CSV program stored a value under
(
LocalProjectService.ScriptWrittenStepVariableKeys: a handler's or a later indexer write, a script's bulk write, a CSV program's data columns — not a mapping's or a training's batches — those still in the key table), in one warning,StepPhysics-Recompute--WrittenValuesKept.
Source Code Path
See HiNC App Anatomy for git repository links.
Web Application
HiNC-2025-webservice (Quasar CLI SPA):
wwwroot-src/src/components/controlTree/stepVariableItemTypes.ts— the branch's two item types and the builder that reads the whole branch in one request and keeps a failed read's branch.wwwroot-src/src/components/controlTree/StepVariableRootPanel.vue— the two lists, the read-failed note, New step variable as an add only.wwwroot-src/src/components/controlTree/StepVariablePanel.vue— the editor: the fields, the compile-check, Apply, Delete, the refusals, the dirty-switch gate, the drafts, the preview.wwwroot-src/src/components/controlTree/stepVariableIds.ts,stepVariableRefusals.ts,stepVariableDrafts.ts— the node ids, the refusals' fields and the on-the-spot key checks, the drafts.wwwroot-src/src/components/controlTree/useControlTreeHost.ts—buildExecutionRoots(), the branch's read queue, the generation and language watches, and a panel's post-op selection that yields to the user's (StructureChangeRequest.yieldsToUser).wwwroot-src/src/api/stepVariables.ts— the client of the REST above;wwwroot-src/src/api/http.ts— the refusal codes' bundle keys.wwwroot-src/src/components/widgets/TextEditor.vue— thediagnosticsthe editor marks.Execution/StepVariableController.cs— the REST.
HiAPI Engine
HiNc/MachiningProcs/StepVariable.cs— one variable of the project file's list.HiNc/MachiningProcs/StepVariableCompiler.cs— the formulas' compilation, the compile-only check, the outcomes kept per formula text.HiNc/MachiningProcs/LocalProjectService.ProjectStepVariables.cs— the project's variables compiled, registered and refreshed after an edit.HiNc/MachiningProcs/LocalProjectService.ProjectStepColorTrees.cs—ReportStepColorTreesReferencing, the project's color trees that refer to a removed variable's key (StepVariable-Delete--ReferencedByColorTree).HiNc/SqliteUtils/SqliteStepStorage.cs— the step cache's two tables of written values,StepVariableKeyandStepVariable: the key mirror, the NaN as eight raw bytes, the batch write in one transaction, the range reads and the writer's watermark.HiMech/MachiningSteps/MachiningStep.cs— the indexerthis[string]: the read order (pin, live consignment, the runner's last published values, the store) and the write order (normalise, consignment, host).HiMech/MachiningSteps/WrittenStepValues.cs,HiMech/MachiningSteps/StepConsignment.cs— the values written onto a step while its consignment is live, taken once by the writer for the step's rows.HiMech/MachiningSteps/StepVariableWrite.cs— one write of a batch (StepIndex,Key,Value) andSegmentLength, 4,096;HiMech/MachiningSteps/StepVariableValue.cs— what a value normalises to.HiNc/MachiningProcs/SessionShell.cs—WriteStepVariablesandReadStepVariables, the script's bulk write and range read.HiNc/MachiningProcs/LocalProjectService.cs— the host:TryGetStepVariable,WriteStepVariableswithStepVariable-Write--NotStored,NotifyStepValuesWritten, the inspecting-slot scan,ScriptWrittenStepVariableKeysandStepPhysics-Recompute--WrittenValuesKept.
See Also
- Step Variables — the branch from the user's side
- Program Branch — the root above this one
- Step Colors Branch — the root below this one: the project's color trees, which share the variables' key space
- Execution Page — the page that hosts the four branches
- Step — step variables from a mission script