Table of Contents

Script Commands

What Is a Script Command?

A script command is a C# statement executed by the HiNC scripting engine. Scripts directly reference members and methods of SessionShell, which serves as the global scope — no explicit object reference is needed.

// These are all SessionShell members used directly as globals
EnablePhysics = true;
MachiningResolution_mm = 0.125;
PlayNcFile("NC/file1.nc");
Message("Done");

Script Syntax Basics

Scripts use native C# syntax:

Feature Syntax
Statement terminator ;
End-of-line comment // comment
String interpolation $"Value is {variable}"
Positive infinity double.PositiveInfinity
Negative infinity double.NegativeInfinity
Bitwise OR (for flags) Fx|Fy|Fz

All standard C# language features (variables, loops, conditionals, LINQ, etc.) are available.


Execution Model

Session Lifecycle

  1. Scripts execute in order on the Task page
  2. A PacePlayer(API) controls playback — script commands like PlayNcFile(API) block until the NC program completes
  3. Player control commands (Pace()(API), Pause()(API), Reset()(API)) interact with the PacePlayer(API). The mission script runs on the player thread, so its own Reset() is refused with Reset--Refused and resets nothing, since the reset would end the session and clear the runtime under the script's own run — reset the runtime between two programs with ResetRuntime(API) instead
  4. ResetRuntime(API) clears the runtime state — the recorded steps, the meshed workpiece and the messages. Handlers on the session events survive it; they go when the session ends, at the player's Reset. Called inside the program being played — from the program's own embedded script, or by a script inside its own loop over RunNcFile(API) — it is refused with ResetRuntime--Refused, resets nothing, and the program plays on; PowerReset(API) is refused there the same way (PowerReset--Refused). Call either between two programs

A Script command that does not compile is reported on Shell as ScriptCommand-Compile--Failed, with every diagnostic, and runs nothing; one that throws — a cancellation of its own included — as ScriptCommand-Run--Fault, with the script's own message, once, whether the error comes before or after an await such as await Map(…). Neither ends the mission: it goes on with its next command. An exception from an action the script returns — the play that RunNcFile(API) hands back in return RunNcFile(…), for one — is the play's, not the script's: the mission stops there, with Player-Run--Fault. A Stop while the script runs is not a fault: the script ends without a report at its next call that looks at the Stop — a play, an act, an NC optimization or a mapping it awaits — and the rows below it do not run (see Starting and Stepping).

Event-Driven Execution

Events like SessionStepBuilt(API) fire during simulation and allow per-step logic:

SessionStepBuilt += (preStep, curStep) => {
    if (curStep != null)
        Message($"Step: ToolId={curStep.ToolId}");
};
PlayNcFile("NC/file1.nc");

A handler added this way belongs to the session: ResetRuntime leaves it in place, and it goes when the session ends, at the player's Reset.


Script Commands in NC Code

Script commands can be embedded in an NC program's own comments, so a setting changes at the point in the program where it applies. Two syntaxes have to agree for that to happen. The controller's comment syntax takes the comment out of the block first, and the comment character is the controller's own:

Controller What opens a comment
Fanuc, Mazak, Syntec ( ) — parentheses, and nothing else
Siemens ;, to the end of the line
Heidenhain //, ; or ( )

The script syntax then takes the script out of the comment's text, by two markers: @@ for a script that runs before the block's motion, @@^ for one that runs after it. The two markers are settings of that syntax rather than fixed characters: they come from the controller definition a project names, and a project that carries its own parser configuration keeps its own. On Siemens, where ; opens the comment:

T01 M06 ;@@MachiningResolution_mm=0.03125;
S1270 M03
G43 Z10. H01

On a Fanuc-family controller the same script goes inside the parentheses:

T01 M06 (@@MachiningResolution_mm=0.03125;)
S1270 M03
G43 Z10. H01

Three rules decide whether the script a block carries is the script that runs:

  • One script per marker, and each script goes last. A marker takes everything from itself to the end of the comment, so text written after a script but still inside that comment is read as part of it — a second @@ in one comment is swallowed into the first script.
  • @@ before @@^, in a comment that carries both. The after-the-block script is taken out of the comment first, so a comment that writes @@^ first has its @@ swallowed into it.
  • One parenthesised comment per line. Such a comment runs from the first ( to the last ) on the line, so a second comment is read as part of the first. On Heidenhain the parenthesised comment is taken out before the ; one, so a call written after ; loses its arguments to it — ;@@LineSelection("a", …); runs as LineSelection;, which does not compile. There, write a script that calls anything inside ( ) or after //.

An embedded script compiles against the same references and imports as a Script command, and like a Script command it needs no licence feature of its own. A script that does not compile is reported on its step as Script-Compile--Error and one that throws as Script-Run--Exception; neither stops the play. A Stop pressed while an embedded script runs is seen at the next program or act the script plays and ends the script there, without a report; a script whose block the Stop caught in progress does not run. An embedded script cannot reset the run it plays in: @@ResetRuntime(); and @@PowerReset(); are refused with ResetRuntime--Refused and PowerReset--Refused, reset nothing, and the program plays on. Put such a reset between two programs, in a Script command or a script line between their plays.

Note

Packages 3.2.7 to 3.2.31 ran embedded scripts only under the NcComposition licence feature. Without it the first one reported Script--NotLicensed once and every further one — in that play and in the plays after it — was skipped silently. A program whose embedded scripts stopped taking effect after a move to one of those packages runs them again on a later one; nothing in the program has to change.

Warning

;@ was the marker of the previous runner, and ;# was its after-the-block marker. On the runner that plays NC today they are ordinary comment text and the command does not run. For ;@ the build warns CsScript-Marker--Legacy once per NC file, naming the file and the first line where ;@ stands before a call or an assignment; a ;# command goes unreported. Written bare on a Fanuc-family controller, where ; opens no comment at all, the text is not even a comment — the build also warns UnparsedText--Remaining.


File Path Templates

Commands that output files support the [NcName] token, which is replaced with each NC file name:

PlayNcFile("NC/file1.nc");
PlayNcFile("NC/file2.nc");
WriteShotFiles("Output/[NcName].shot.csv", 1);
// Produces: Output/file1.nc.shot.csv, Output/file2.nc.shot.csv

All file paths are relative to the project directory unless an absolute path is given.


Important Warnings

The following operations can corrupt simulation state or produce incorrect results:

  • Do not save the project during simulation. System-internal configuration (e.g., training-specific resolution overrides) may overwrite your settings.
  • Do not reset the player during milling coefficient training. Close the project instead of pressing the reset button to avoid unexpected errors.
  • Do not modify resolution, tool, or controller settings during training. Changing these mid-training invalidates the results.
  • Do not play, reset or end the session inside a SessionStepBuilt(API) handler. A handler runs as each step is finished, while the play is still producing the next ones. Called from one, PlayAct(API), PlayToolingTeleport(API), PlayClTeleport(API), ResetRuntime(API), EndSession(API) and the player's Reset(API) throw: the exception is reported on that step as Step--Exception, and the step is left without a result. PlayNcFile and the other program plays are refused as a second play (Play--Busy under a mission; Play--BusyAnotherThread when the play runs on another thread, such as a host's or a web request's). Closing the project is not refused, but stops the play and waits for it to end, while the play may be waiting for that handler: the two can wait on each other. Do such work after the play: a script line after PlayNcFile runs once every step's handler has returned.
  • Do not play an act from another thread while a run is live. PlayAct(API), PlayToolingTeleport(API) or PlayClTeleport(API) called from a host's thread or a web request, and LocalProjectService's Act(API) called by a host, play nothing while the player is running or paused — a mission, or a job such as the step-physics recompute — and report Play--BusyAct; a mission script's own call, on the player thread, and an NC-embedded script's are let through. While another thread plays NC on the session, an act from any other thread plays nothing and reports Play--BusyActAnotherThread. Make the call in the mission script, or after the run.
  • Do not combine UpdateNcOptOption in SessionStepBuilt(API) with NC-embedded optimization commands. Parallel computation may cause undefined behavior.
  • Do not write NaN into a per-step NcOptOption. A NaN feed-per-tooth boundary is refused when the step is solved: that step keeps its simulated feed rate and is reported as an error naming the option values, and the rest of the run is optimized normally. See When a Step Cannot Be Solved.

Global Variables

Global provides a key-value dictionary for sharing data across scripts:

Global["material"] = "Steel";
var material = Global["material"];

Full API Reference

For the complete list of available commands, properties, and events, see:

See Also