Table of Contents

Workflow: Replay Acceptance over the HTTP API

Project Construction ends with a clean replay: the whole mission played, every message read by id, and the numeric cross-checks made. This page is that replay driven over the HTTP API, for a script or an agent with no canvas to look at: how to start a run that really is a fresh one, how to watch it while it plays and know it has ended, and the evidence that it cut what it should.

Sign-in, the reply envelope and the other conventions every route here follows are Driving the Web Service over HTTP.

flowchart LR
    Free["Instance free?"]
    Reset["Reset"]
    Start["Start<br>keep runId and instanceId"]
    Poll["Poll status, watch the run<br>same runId, same instanceId"]
    Evidence["Read the evidence"]
    Msgs["Inventory messages"]

    Free --> Reset --> Start --> Poll --> Evidence --> Msgs
    Msgs -->|fix, then replay| Free

1. Start from a free instance and a reset session

The service holds one project and one run for every client connected to it, so a replay begins by checking that the instance is free, and never ends by stopping a run it cannot prove is its own.

  1. GET /api/Execution/status. If executionStatus is Running or Paused, a run is live — do not start, reset or stop it. A reset stops a live run too. A review that is not null is a recorded play open for review (§7), possibly another client's: no program plays while it is open — the first program a run reaches ends it with Player-Run--Fault — and the Reset of step 2 closes it.
  2. POST /api/Execution/reset, before every start. Start does not reset. After a finished run, POST /api/Execution/start plays the mission again on top of the session that run left behind — the machined workpiece, its steps and its messages. Every Read On First Or Write record then sees steps already produced and writes instead of reading, so the stage-0 record overwrites the cached stock with the machined part, and every later run, reset or not, starts from stock that has already been cut until that file is deleted. The earlier run's touching steps also keep the no-contact warning, Play-Touch--None, from firing; what the second run reports instead is a Tool-Touch--None for each tool that cuts nothing, which points at tool lengths and offsets, not at the skipped reset. While another request plays an NC program on the session — a POST /api/SessionShell/PlayNcFile, for one, which executionStatus does not show as Running — the reset answers 409 with code: "Reset--Busy" and resets nothing: wait for that play to end, then reset.

A reset empties the step strip and the session's message lists, so read the evidence of the previous run (§4, §5) before resetting for the next one.

2. Start, and poll by run id

  1. POST /api/Execution/start, then check the body, not only the status code. An enabled Script command that does not compile refuses the start with HTTP 200, success: false and code: "script_compile_error"; a start while a run is live answers alreadyRunning: true and starts nothing, and the runId beside it is the live run's — the service cannot tell whether it is yours, so do not poll for it. Keep the runId a real start returns and the instanceId beside it, the service process the runId counts in. A real start also says whether it began on a fresh session: continuesSession: true, with a note saying so, means the Reset of §1 was skipped and the run plays on top of the session an earlier play left, so its evidence will hold that play's steps and messages too. Accept nothing from such a run: it is yours, so reset — which stops it — and start again. A start on a fresh session answers continuesSession: false.
  2. Poll GET /api/Execution/status until it reports that runId, under the same instanceId, with executionStatus either Finished or Ready. After a Reset, Ready is how a run that was stopped, or that ended in an exception, reports itself: a poller that waits for Finished alone never returns from a failed run. Without the Reset of §1, such a run inherits the previous run's Finished. The exception behind a Ready is in the service log, which GET /api/Project/logs returns for the current day. A higher runId means another caller has started a run since. A different instanceId means the service restarted and the run is gone: a new process counts its runs from 1 again.
  3. Read the evidence below before anything resets the session.

Watch it while it plays

A long play is worth watching while it runs, not only judging at its end. A wrong program zero, a tool length or offset set wrong, or stock in the wrong place does not stop a run: it keeps colliding, cuts air or cuts too deep for hours, and every program played on that stock afterwards is wasted. The requests below read the live run, change nothing, and are cheap enough to repeat every few minutes while the poller waits:

Look at Request Healthy
Still running, still yours GET /api/Execution/status executionStatus is Running with the runId and the instanceId your start returned
Progress GET /api/execution/cl-strip/range count grows between two looks
Alarms GET /api/Execution/messages?minSeverity=Message&tail=0&idPrefix=<prefix> for Collision-- and Play-RapidCut, and with minSeverity=Error for Stroke (which leaves out StrokeLimit--Unconfigured, a one-time set-up warning at the start) every list's matched stays 0
New warnings GET /api/Execution/messages?minSeverity=Warning&tail=1000 no id you have not accounted for (§5)
Where the play is the strip chart of §4 with widthHint=0, dispBegin a stretch before count, dispEnd=<count> and inspectingKey FileNo, LineNo, ToolId, Cl.X, Cl.Y, Cl.Z, read at its last value that is not NaN — count includes steps still being computed, which answer NaN the file and line move on; the tool is the one the program calls, where the program cuts
Cutting, and how hard the same chart over the last few thousand steps, with IsTouched, CuttingDepth_mm, MaxAbsForce_N, StressRatio and MaxSpindlePowerRatio — the last three are physics: with the project's physics off, no physics licence, or Show Physics Options off they answer NaN or an empty items in contact where the program cuts; depths within the tool's length of cut — near the step-down only where no wall is engaged, since a wall, a corner or a flank pass engages up to the whole length of cut (§6); stress and spindle-power ratios below 1

The strip chart's Individual aspect answers the project's inspecting key alone (HiNC 3.2.46 and later): post the key first — keep the key the project holds (inspectingKey of GET /api/execution/strip-chart-item-config without a key), then POST /api/execution/inspecting-key with {"inspectingKey": "FileNo"}, whose reply's currentKey must be the key — then read. An answer with pending: true is not ready: the host is still filling that key's values for every step, so read again after a moment until it is false; items empty with pending: true means the key is not the project's (another client posted its own). Posting a key changes the project's own setting, which a save keeps, so when the reads are over read it again and, while it is still the key you posted, post back the key the project held before, the one you kept; a key another user has set since is theirs to keep. With tail=0 each list answers its total and the matched count for the filter without returning the messages, so an alarm check costs one small reply per prefix. The canvas of the app, looked at during the play without selecting a step, shows the tool and the stock as cut so far; the geometry difference is built only when the play ends.

Stop a play only for a set-up error that spoils what follows — alarms that keep coming, no contact where the program should cut, depths or loads far beyond the pass, a tool somewhere the program never goes. Then stop it (POST /api/Execution/stop stops whatever run is current, so only a run you know is yours), fix the set-up, and replay from §1. A defect that does not spoil what follows — a warning already understood, a single touch on a retract — is written down and the play left to finish; it is judged with the rest of the evidence in §4 and §5.

If the poller gives up, let it give up: a timeout is not a reason to call POST /api/Execution/stop. It takes no run id and stops whatever run is current, which on a shared instance may not be yours, and a stopped run ends without the rows below the point it reached — no Post-Execution output, no end-of-stage record.

3. Coarse first, then fine — and clear the stock cache in between

The first plays of a new project check datums, not surfaces, so they run at a coarse Initial Resolution (PUT /api/Workpiece/init-resolution) and a coarse Machining Resolution. Moving to the fine resolution for the acceptance run has one trap. The stage-0 record above the first program caches the stock as it was meshed at the Initial Resolution of that time, and every later run reads that file back — so after changing the Initial Resolution, the stock geometry or its allowance, the next run starts from the old stock and nothing says so.

Delete the stage-0 file before the next start: DELETE /api/Mission/commands/{path}/recordmeshedgeom/file, with {path} the record's entry path (GET …/recordmeshedgeom/file-status reports whether the file exists). An end-of-stage file you mean to resume from was written at the old width as well; delete it or play its stage again. A change to the Machining Resolution command needs no deletion — it governs the cuts below it, not the cached stock. A Mission That Resumes is the layout these records belong to; in the app, the record's own Reset deletes its file.

4. A finished run is not a passing run

Finished says the mission reached its end. It does not say that anything was cut. A session starts with no tool in the spindle unless the project saves one with its equipment — no route of this API sets one, so a project built through it never does — and from then on only a tool-change command or a Manual Tooling entry mounts a tool. A program that never mounts one — written for a machine whose operator changes tools by hand, with no T… M06 in it; a tool word the runner could not resolve; an empty tool-name table — plays every line, produces no machining steps at all, and still ends Finished. In the step list it leaves one warning, Step-Tool--NotEquipped, at the first feed move made with the spindle empty, naming that move's line and file — once per Reset. The end-of-play warning Play-Touch--None is raised only when steps exist and none of them touched the stock, so a play with zero steps never reaches it; and the NC messages often carry no trace of it at all — a G43 H1 compensates from the table whether or not a tool is in the spindle, and a G43 with no H runs on the modal H (H0 until one is written) without a word, as the control does.

Give such a program its tool before the cuts, as the operator does. First choice: a Manual Tooling entry above its Program File — POST /api/Mission/list-command/entries with {"commandType": "manualtooling", "insertIndex": <the Program File's index>}, then PUT /api/Mission/commands/{path}/fields/toolId with the tool number as the body. It mounts the tool and plays nothing, so the file keeps its lines and its file number (Mounting the tool the operator loads by hand). Otherwise put the change at the start of the copy the project plays (Project Construction §1) — T1 M06. An NC Code entry holding the change above the Program File mounts the tool too, but it is played as a file of its own, so it takes a file number and every program after it moves up one: a controller log mapped by FileNo (Sensor Mapping) has to follow. A program with no G43 needs the tool's length as well — a G43 H for the tool in the copy, or the length folded into the work offset's Z the way a shop without length compensation keeps it (Program Zero Alignment). Without it the tip runs a whole tool length below every programmed Z, and the play either touches nothing, ending with Play-Touch--None, or plunges that far into the stock.

A zero-step run also hides behind the resume layout. With no step produced, every Read On First Or Write record below the program still reads, so an end-of-stage file left by an earlier good run is loaded back — and the canvas and the geometry difference show that earlier result as if this run had made it.

So accept a replay on evidence:

Check Request Pass
Steps exist GET /api/execution/cl-strip/range count > 0
Something was cut GET /api/execution/strip-chart?aspect=Individual&inspectingKey=IsTouched&xValueCategory=IndexByStep&widthHint=1&dispBegin=0&dispEnd=<count> the largest value in items[0].max is 1
Every program ran GET /api/NcProgram/files every file — and every subprogram, listed under its caller's children — has an invocation with a non-zero executedLineCount
The difference was built GET /api/Workpiece/diff-settings hasDiffOverlay is true (and detectionRadius is no longer null)
Outputs written GET /api/Execution/messages?kinds=shell&idPrefix=StepFile-&tail=1000 (and idPrefix=ShotFile-), then GET /api/file-explorer/list?rootName=ProjectDirectory&relativePath=<the template's folder> a StepFile-Create--Start (ShotFile-Create--Start) row, Creating file[…], for every file expected, between the writer's …-Write--Start and …-Write--Finish; no …-Write--NoFile; every file listed, with a size above 0
Messages GET /api/Execution/messages every id accounted for (§5)

GET /api/Workpiece/diff-settings answers { hasWorkpiece, diffVisualRadius, detectionRadius, hasDiffOverlay }, and { hasWorkpiece: false } with no project loaded. hasDiffOverlay says an overlay is attached to the live meshed tree right now — not that deviations were found, and not that a comparison was requested — and detectionRadius is null until one exists, so a null radius is the same answer as a false flag. A Reset clears both.

A Post-Execution writes only what has played above it. A writer of it that opened no file — the command sits above the programs, or the run has no steps — ends on StepFile-Write--NoFile or ShotFile-Write--NoFile, a warning, instead of …-Write--Finish. The path in a Creating file[…] row is the template expanded, relative to the project folder unless the template is absolute; a folder that was never created answers the file-explorer list with 404.

Pass dispBegin and dispEnd explicitly: without them the strip chart covers only the window currently on display, not the run. widthHint=1 returns at most two points: the first covers the range without its last step, the second the last step alone. Take the range's peak as the largest value in items[0].max (and its floor as the smallest in items[0].min); widthHint=0 returns one point per step instead. Post the key first (§2): the request answers IsTouched only while it is the project's inspecting key, and pending: true until the host has filled its values.

For a line whose effect you doubt, GET /api/NcProgram/syntax-piece?fileIndex=<i>&lineIndex=<n> returns the sentence as the runner parsed it, together with the steps it produced — the direct way to see which command an address word was attached to. Under a review (§7) it answers the block recorded at that file and line — the first one recorded there — in the same JSON, with the steps rebuilt from it; a query by sentenceIndex alone finds nothing.

5. Inventory the messages

GET /api/Execution/messages returns one section per list — shell, nc, step and ncManip — and filters them all the same way:

  • minSeverity=Warning&head=50 returns the first matches, which is where the cause of a cascade sits; the default takes them from the end.
  • tail is capped at 1000, and tail=0 returns counts only — the quick way to size each list.
  • idPrefix narrows to one family of ids; kinds names the lists to read.
  • sinceIndex pages one list at a time, so kinds must then name exactly one.

A Collision--Detected's stepIndex is the index its pose's step takes, and it is reported the moment the collision is found, before that step is built; a run that pauses on failures pauses right there. The licence and lease refusals said before a step is granted (MachiningStep--LicenseCapReached and its kin, which hold the run), Player-Run--Fault and Step-Task--Fault (a step whose building failed) are anchored to their own step the same way. So is a stroke-limit report — Stroke-Limit--Over, or StrokeLimit-Xyz--Over and StrokeLimit-Abc--Over on the legacy HardNc runner — on the step whose pose went past the limit (for a motion that built no step, the index that step would have taken); it names no sentence, so its sentenceIndex is -1. After a stop and a start without a reset, the replay's steps take the numbers on from there, so the same index can belong to a later step. To tell, ask GET /api/NcProgram/sentence-of-step?stepIndex=<stepIndex>: the step is the message's only when the answer's sentenceIndex equals the message's. Without a sentenceIndex, an index below stripCount while locked is a position the pipeline is still filling (ask again); the next index (notBuiltYet) while running is the step the run is building; notBuiltYet otherwise is a step the run stopped or paused before building; an index below stripCount with nothing locked is a step a stop dropped or whose building failed. The other step messages — a script's, a tool change's, a line end's, an empty spindle's — are anchored to the next built step by design and are not compared; a message whose sentenceIndex is -1 has nothing to compare. The comparison holds within one numbering of the sentences: a reset clears the messages, while an EndSession, a script's PowerReset() or a change of the NC runner keeps them and numbers the next sentences from zero again. Under a review (§7) sentence-of-step reads a recorded step as it reads a live one, but the result keeps no messages: the lists hold only the review's own, and there is nothing to compare.

What to stop on — only declared informational messages, plus the expected messages of constructs left as delivered — is Project Construction §5.

6. Figures for the cross-checks

The strip-chart request of §4 returns the peak of any step property over any step range — the largest value in items[0].max across the points it returns — once that property is posted as the inspecting key (§2). Take the keys from GET /api/execution/step-properties — CuttingDepth_mm for the depth-of-cut check, MaxAbsForce_N for a force peak. The depth peak of a roughing group should match its step-down; that of a finishing program is not its allowance, since a wall or a corner can engage the whole length of cut — check a finishing job's Z datum with the distance test of Building the Workpiece from a Reference Mesh §1 instead. A run at a machining resolution coarser than the allowance says whether the program touches the stock and where; take its forces and depths only from a run at a resolution finer than the allowance. To narrow the range to one file, take the invocation's firstSentenceIndex and lastSentenceIndex from GET /api/NcProgram/files and turn each into steps with GET /api/NcProgram/steps-of-sentence?sentenceIndex=<n>. Under a review (§7) the invocations carry no sentence range and steps-of-sentence answers found: false: a line's steps are in its line-marks entry and its syntax-piece answer instead.

The simulated time at the end of step i is its end time code: xValueCategory=IndexByTime&widthHint=0&dispBegin=<i>&dispEnd=<i+1> returns it in xs, in seconds. The difference across a file's step range is the time to compare with the post-processor's estimate, with the limits Project Construction §6 sets on that comparison.

7. A recorded play, reviewed over the API

A play recorded with Record Play Result can be read again without playing it (Recording the play and reviewing it). POST /api/LocalProjectService/OpenNcResultReview?relResultDirectory=<folder> opens the review — the folder relative to the project folder, empty for the newest completed result — and POST /api/LocalProjectService/CloseNcResultReview closes it. Both need a loaded project but no session, and answer 200 when the review opened or closed and 409 when it did not, with the reason among the body's messages; a folder that holds no result file answers 500. Opening ends a recording still open and resets the run first, as the Reset of §1 does, so read a live run's evidence before it; a Reset, a Close and a project switch close the review.

While it is open, GET /api/Execution/status carries review — the folder, the program's name, when it was recorded, its step, position and block counts, whether the recording was completed and the play ran to its end, and what became of its finished stock (wctState, and wctColoured: whether the loaded stock is coloured and picked by the recorded steps, false for a result recorded without the stock's node map, which shows it plain) and of its luggage archive (luggageState: Loaded when the folder's luggage.db — the recorded steps' force arrays and engagements — is open, None when the folder holds none, Incomplete, Mismatch or Failed when the review did not read it) — and null when none is open. The strip, the steps and the strip charts are the recorded ones, and so are the two Sim cycle charts while luggageState is Loaded: GET /api/execution/cycle-line/force and GET /api/execution/cycle-line/sim-spindle-moment answer from the archive as during the play. What the result does not hold answers ready with no data rather than pending: the CWE panel's POST /api/Execution/step-volume/{connectionId} in every review, and the two Sim cycle charts without an archive or for a step it does not hold, carry reviewing: true, which the app shows as Not in the play result, and the steps have no step-variable values. The Program requests answer from the result file: files lists one root per recorded source file, without a caller, a sentence range or an executed-line count (-1); line-marks marks a line where a pass recorded a block, with its steps; syntax-piece (§4), sentence-of-step (§5) and steps-of-sentence (§6) answer as said there. The result keeps no messages and no executed-line counts, so the checks of §4 that read them are a live run's.

See Also