Table of Contents

Brand NC language coverage

The SoftNc pipeline is the default NC engine, and this is where most of the release went.

Siemens SINUMERIK

Real .mpf / .spf programs replay end to end, not an ISO subset. At 3.1.175 a program that declared its tool as T="NAME", shifted with SUPA, computed with R-parameters, called L-subprograms or looped with WHILE was not interpreted past that point.

  • Modal vocabularySUPA / G153 suppress all frames for one block; T="NAME" string tool calls with D cutting-edge offsets resolved through SiemensToolOffsetTable ($TC_DP lengths and radius plus additive wear); G70 / G71 units; path smoothing (G60x / G64x, FNORM / SOFT / FFWON / COMP* / UPATH, CYCLE832); MSG() and STOPRE; CR= and TURN= arcs. Tail comments became quote-aware, so a ; inside MSG("A;B") no longer truncates the block, and the preset stops misreading L and G74 as Fanuc-family codes.
  • EvaluationSiemensExpressionParser feeds the shared expression engine, so Z=R63+150 and X=SIN(R10)*20 drive motion. SiemensRParameterTable holds R0–R999 as per-case project data, DEF REAL/INT declarations lower into assignments, and $P_UIFR[n,axis,TR] binds both ways to SiemensFrameTable. Any other $-variable is recorded with an unsupported note rather than dropped.
  • Five axisSiemensProgrammableFrameSyntax simulates TRANS / ATRANS / ROT / AROT (with RPL=) and the solid-angle forms ROTS / AROTS into the tilt-transform chain in Sinumerik RPY order (ROTS / AROTS behave as ROT / AROT: at most two angles, first-named axis first); SiemensTraoriSyntax makes TRAORI a real RTCP mode, the sibling of ISO G43.4, with TRAFOOF handing the offset back; SiemensCycle800TiltSyntax decodes CYCLE800's MODE bits for all four swivel modes. SCALE / MIRROR / CROTS are recognized and reported (SiemensFrame--Unsupported), not simulated.
  • Calls — L-prefixed and named subprogram calls resolve against SubProgramFolderConfig ({name}.SPF, then .MPF, then the bare name) and inline with their P repetition count; M17 / RET pop a frame; REPEAT re-runs a labelled slice; MCALL CYCLE81/82/83/85 maps onto the shared canned-cycle machinery; PROC headers and labels are claimed whole.
  • Control flowGOTOF / GOTOB, IF / ELSE / ENDIF, and WHILE / FOR / REPEAT-UNTIL / LOOP. Runaway programs are bounded rather than hanging the session: SiemensGotoIterationDependency caps jumps per (file, label) and SiemensLoopIterationDependency caps iterations per (file, loop-entry line); over the cap the construct warns and falls through.
  • Per-word coordinate functionsAC() / IC() / DC() / ACP() / ACN(), including on I / J / K circle centres. G90 C=IC(360/17) is one incremental index inside an absolute program. Direction resolution lives in McAbcCyclicPathSyntax: ACP() takes the [anchor, anchor+360) window, ACN() the (anchor-360, anchor] window, DC() the shortest swing, with the exact 180° tie going negative. On a machine-coordinate block the increment is a distance in the machine frame, because MachineCoordSelectSyntax is ordered ahead of IncrementalResolveSyntax in all five brand presets — Fanuc, Mazak, Syntec, Siemens and Heidenhain — so G53 X=IC(10) and SUPA Y=IC(-10) add the raw word to the previous machine position rather than being re-based through the program frame first. That order is not something a saved-file patch can express: a project saved before 3.2.21 keeps the old order for that block shape until its pipeline is re-created from the brand preset.
  • Coded positionsCAC / CIC / CDC / CACP / CACN take a 1-based indexing position number rather than a coordinate, resolved against IIndexingPositionConfig, implemented by SiemensMachineDataTable from the real machine data (the MD30500 axis assignment, the MD10910 / MD10930 position tables, the equidistant MD30501–30503 definition).
  • G74 / G75 fixed-point return is claimed as a whole block, so its dummy axis values no longer mint a rapid to the coordinates written in the block and its F never reaches the modal feedrate.
  • OEM auxiliary M-codes — the preset declares M12 / M13 / M22 / M23 and M330 / M331 note-only, so each occurrence voices DeclaredMCode--UnmodeledEffects instead of an unknown-code warning, without inventing simulated effects. A machine's own table overrides a declaration when the real effects are known.

Heidenhain

Both dialects play on one preset, HeidenhainNcRunner.

  • Klartext motion and setup — the L statement and its axis words, FMAX, M91 as a one-shot machine-coordinate move, TOOL CALL wired to tool change and spindle speed with the table height and DL. Datum handling follows TNC semantics: CYCL DEF 247 sets the preset and CYCL DEF 7 is an additive shift on top of it, composing as separate transform-chain entries instead of replacing each other, resolved against HeidenhainDatumTable. Arcs (CC pole plus C statement, DR- = CW, closed arc = full circle), RL / RR / R0 radius compensation, the M126 / M127 rotary-wrap state, M140 MB retract, and CYCL DEF 32 TOLERANCE. HeidenhainLnOrientationSyntax resolves an LN vector block into rotary axes through the same RTCP machinery the rotary-word programs use: T present plus M128 / FUNCTION TCPM keeps the tool on T; T absent plus RTCP holds the tool on the surface normal N; RTCP inactive ignores T (Orientation-Vector--IgnoredNoTcpm), as the control does. A C block never states its own centre: each in-plane component comes from the CC block's own axis word, else from the previous CC section's same axis, else from the arc's own start point. A bare CC is the one spelling that states all of them at once — it takes the last programmed position, read at the CC block, and replaces the modal centre rather than inheriting it. A centre that lands on the arc's own start point leaves the block with no radius, so it warns Arc-CircleCenter--OnStartPoint and is degraded to a linear move to the endpoint.
  • Klartext incremental axis words — the I-prefixed word (IX+20, IY-15, rotary IC+90) is the same axis word as its absolute twin, read as a distance from the last programmed position. It is per word rather than per block: L X+60 IY-10 mixes an absolute X with an incremental Y and stamps only the Y. One shared grab serves five statement heads — L, C, CC, LN and CYCL CALL POS — each writing the value under the plain axis key of its own section plus a "PositioningOverride": { "Y": "Incremental" } entry on the block root. That is the same per-word section the Siemens IC() wrapper writes, so the existing resolve consumers convert it and no second incremental mechanism exists. Coverage differs by head: L, C and LN take the whole axis tag list, rotary included, while CC and CYCL CALL POS take X / Y / Z only — a CC never positions a rotary axis, and the stamp is keyed by axis on the block root, where a rotary letter would read as a rotary word of the block. The sign is optional (L IX5), a Q reference resolves behind the prefix (L IZ+Q2), and the glued post-processed run LIX+20IY-15 parses. Before these words were read the plain grab could not see the X inside IX — its prefix guard demands a word boundary, a digit or whitespace before the tag, and I is a word character — so L X+60 IY-10 took the X and dropped the Y: a motion to the wrong place, not a missing one.
  • What an incremental word is measured from depends on the statement. An L / C / LN endpoint and a CC centre are distances from the last programmed tool position, so CC IX+0 IY+11 is a stated centre resolved against that position — never against the previous centre — and it is no longer the spelling whose coordinates the parser could not read. CYCL CALL POS has its own reference: an IX / IY / IZ word there is a distance from the coordinates the previous CYCL CALL POS programmed, which is the control's own rule behind its error 1A0-0108. An axis that no earlier call programmed has no reference: the control refuses the call, and the simulation warns HeidenhainCyclCall--IncrementalNoReference and falls back to the last programmed position. A tool move between the two calls makes the two candidate references differ, and that divergence warns HeidenhainCyclCall--IncrementalAfterMove, the control's stated reference winning. With M91 on the block the increment is a machine-frame distance, per the ordering note under Siemens above. A rotary increment is a signed traverse the wrap pass keeps verbatim — IC+270 from 75° stays 345° instead of folding into the ±180° shortest-path window, while an absolute rotary word on the same block still folds. The CYCL DEF 7 datum-shift I words are a different mechanism: CYCL DEF 7.2 IY+5 shifts by the value on top of the shift last valid, stays under prefixed keys in the cycle's own record, and writes no per-word stamp at all.
  • Q-parametersHeidenhainExpressionParser lexes Q / QR / QL / QS, the DIV keyword of FN 4 and the prefix SQRT of FN 5, so FQ1 reaches the feedrate, L X+Q2 reaches the program XYZ and TOOL CALL SQ3 reaches the spindle speed. HeidenhainQParameterTable holds Q0–Q99 free and QR0–QR499 permanent parameters as per-case project data. Unimplemented opcodes (FN 14 / 16 / 18…) are claimed and reported rather than half-read, so an FN 18 SYSREAD target stays vacant instead of taking a fabricated value. FN 9–12 conditional jumps execute, with a (file, label)-keyed iteration cap.
  • Tilt and RTCPPLANE (SPATIAL fully composed with SEQ / COORD ROT / TABLE ROT and STAY / MOVE / TURN positioning; VECTOR structurally captured; EULER / POINTS / RELATIV / AXIAL / PROJECTED consumed and warned with the previous tilt retained, so a PLANE AXIAL B+45 B word can never be mistaken for a rotary axis command), FUNCTION TCPM, and real M128 / M129 tool-centre-point control. A centre-referenced FUNCTION TCPM REFPNT (TIP-CENTER / CENTER-CENTER) is recorded and reported (Orientation-RefPoint--CntNotSimulated) rather than silently read as TIP-TIP.
  • Cycles and callsCYCL DEF 2xx bodies with their Q parameters mirrored into the block assignments, cycles 200 / 232 / 251 / 252 / 253 mapped onto the shared G81 / G82 / G83 slots, CYCL CALL / CYCL CALL POS / M99 / M89 splitting call-once from modal firing, CALL LBL inlining up to LBL 0, CALL LBL n REP m as a section repeat, and CALL PGM resolved by file name. The multi-line tilde continuation form is joined at segmentation (JoinTildeContinuations, on by default).
  • Post-processed spellings — some post-processors write a whole klartext body with no separators at all, which used to yield zero motion. Glued line shapes (LX-26.3Y+43.1, FMAXM03M08, …R0FMAX) and the detached feed spelling (F 20000) now parse, with M140, M128 and PLANE MOVE widening their own F capture so a retract or feed-limit value cannot leak into the modal feedrate. BLK FORM is recorded as a brand-neutral stock declaration without replacing the project workpiece setup, and the klartext STOP word joins M00 / M01.
  • DIN/ISO dialect% tape header, N block numbers, T + M06, absolute I / J / K arc centres with the modal pole carried forward, the ISO label family (G98 L<n> definitions and the head-anchored L<n>,<m> call mapping the comma count onto REP), G247 Q339 stamping the same datum preset as CYCL DEF 247, G54 with axis words read as a datum-shift declaration, and G70 / G71.

Fanuc and ISO common

  • Polar coordinate interpolationG12.1 / G13.1 on the SoftNc pipeline. Before this a polar section parsed silently wrong: the X word (a diameter) and the C word (a hypothetical Cartesian axis in mm) were consumed as ordinary XYZ and rotary degrees. ProgramRxczSyntax halves X from diameter, resolves G90/G91, writes the polar and derived Cartesian positions and the machine C angle, and classifies motion into polar linear and polar arc — the latter emitting ActMcPolarSpiralContour, which keeps spiral geometry in central polar coordinates and stays continuous across ±180°. G41 / G42 compensation is resolved on the hypothetical plane, a C-axis speed clamp applies, and YA / ZB axis pairs are supported. PolarGCodeCheckSyntax scans for incompatible G codes before the mode syntaxes consume them.
  • Custom Macro B#var assignment with range-routed stores (#1#33 local per macro frame, #100#499 volatile cleared on M02/M30, #500#999 retained and persisted in the project, #3000#3999 system-control), boolean and logical operators, IF[..]GOTO n, IF[..]THEN <stmt>, WHILE[..]DO m / END m with a bounded-loop watchdog, and position and tool-offset system variables. M98 P_ L_, M198 external call, M99 return and M99 P{seq} early return; G65 one-shot macro call with A–Z → #1#26 argument binding, and G66 / G67 modal macro.
  • Additional work coordinate systemsG54.1 Pn, also written G54 Pn, resolves against the extended table (P1–P48). A selected row nobody has entered reports Coord-WorkOffset--AdditionalZero. G59.1G59.9 have a provider on the Fanuc, Mazak and Syntec presets. See Work Coordinates and ISO core.
  • Fanuc / Syntec unit codes and G05. G71 is the metric spelling of G21; G70 is the inch spelling of G20 and reports Unit--InchNotSupported rather than silently switching units. G05.1 Q1 / Q0 is AICC / Nano Smoothing (recognized). Bare G05 P{n} is a different feature (HPCC) and is consumed without changing programmed coordinates, reported under the Hpcc--* ids.
  • Canned-cycle repeats do not take rotary words on SoftNc. The G73–G89 family expands XYZ / R / Q / F / P / K strokes. An A / B / C word on a repeat is not taken: a rotary-only repeat does not fire the cycle, a mixed block drops the rotary word, and under G68.2 the hole follows the table. Not finished on this line, see Adoption status on the release-note page. The HardNc fallback indexes those blocks the way a control does.

Cross-brand

  • A shared DwellSyntax consumes G4 and G04 with the dialect held on the instance: Fanuc-family X / U seconds, P milliseconds, S spindle revolutions; the Siemens instance reads F seconds and S revolutions. Capturing the G04 spelling fixes a real defect — the un-captured spelling fell through to the flag and axis syntaxes, where a Fanuc G04 X0.5 dwell time became a ghost X motion word.
  • Machine-declared M-codesIMCodeDeclarationConfig and MCodeEffects let a machine state what its own OEM codes do (composite spindle+coolant codes, a tool-change trigger, turret T-word semantics), and MCodeExpansionSyntax expands a declared code into the canonical ISO flags the shared consumers already understand.
  • Custom spindle M-codes drive the spindle direction through a machine-level ISpindleControlConfig; ISO M03 / M04 / M05 remain the built-in fallback. An S greater than zero with no direction ever issued assumes clockwise and emits SpindleDirection--AssumedCw, so the physics gate no longer silently produces zero mechanics for a whole file.
  • Tool changes synthesize their axis travel. ToolChangeMotionSyntax overlays the per-axis tooling position from IToolingMcConfig onto the current pose (a NaN or missing axis stays put) and stamps a one-item rapid compound motion, and ToolChangeSemantic moves behind CompoundMotionSemantic so the tooling step lands at the tooling point. Programs that retract on their own overlay to a zero-length move and emit nothing extra.
  • Per-brand pivot gates. PivotTransformationSyntax reverts to the ISO/Fanuc vocabulary (G43.4 plus the G68.x family), SiemensPivotTransformationSyntax gates TRAORI / CYCLE800, and HeidenhainPivotTransformationSyntax gates the M128 / PLANE vocabulary. All three compose the identical entry through the shared PivotTransformUtil. Exactly one brand gate belongs in a pipeline list — never register two.
  • Controller presets are writable and shipped. A controller resource file is one serialized SoftNcRunner — the whole pipeline that decides how a brand's NC code is interpreted. ControllerPresetWriter serializes the built-in brand presets (CreateBrandPreset, WriteBrandPresetFile, WriteAllBrandPresetFiles) under Resource/Controller/ with the .Controller extension, and HiNc-Resource ships all five so the load browser starts populated. The static brand properties remain the source of truth; the files are regenerable snapshots. Writing needs no XFactory registration — reading one back does, because the loader drops unregistered pipeline entries silently rather than failing the load.
  • Saved pipelines back-fill their system-wired dependencies on load. A project saved before a system-wired dependency existed never self-healed by round-tripping, because re-saving stamped a fresh API version on the same incomplete list. The SoftNcRunner XML constructor now appends the missing ones after the legacy version patches. A runner rehydrated from an older file still keeps the syntax list it was saved with, though — take the regenerated preset, or a fresh NcRunnerSuit built from it, rather than expecting an old file to grow new syntaxes.
  • Machine-coordinate and tilted-plane failures report. G53 and G53.1 record their source G-code on the parsed block and emit Coord-MachCoord--005 / --006 / --007 on paths that used to fail silently. A G68.2 tilted plane the machine cannot reach emits Coord-Tilt--001 / --002, with a tool-axis-only IK retry that avoids a spurious warning on a machine with fewer than three rotary axes.
  • Session-global sentence indexing. SyntaxPiece.SentenceIndex used to be assigned by two independent sequences, so indices collided as soon as a call was inlined mid-stream. The new SentenceIndexCounterDependency supplies every index at one chokepoint, so values are session-globally unique and strictly increasing in execution order, including nested and repeated calls.
  • Inline plays can loop and jump, and playing a file no longer holds it open. Inline NC-code plays stamp their pieces with the command title as a pseudo-path, and every control-flow re-segmentation re-read the host file by that path — the existence check always failed, so loops fell through without looping. RunNc now registers the raw lines on NcLineSourceDependency and LabelScanUtil reads memory first, disk second.
  • M02 / M30 / END PGM is a reset edge. SoftNc models program-end as the edge between that block and the next, so a concatenated multi-program file does not keep TCP, tilt, CRC or a canned cycle alive into the next program. Each modal owner writes an explicit cancel section on the successor (G49 / G69 / G40 / G80); the end-of-program block itself still runs under the modal state it executed with. HardNc still carries G43.4 across M02 — see Results that change on upgrade.