Table of Contents

Namespace Hi.NcParsers.NcWriteback

Classes

EulerUtil

Decomposes a rotation matrix into euler angles for a configurable EulerSequence (default Zxz, the Fanuc G68.2 standard format) such that new Mat4d(third, θ3) * new Mat4d(second, θ2) * new Mat4d(first, θ1) reproduces the input — for ZXZ this is the exact composition IsoG68p2TiltSyntax performs when parsing G68.2 X_ Y_ Z_ I_ J_ K_, so a synthesized command round-trips to the same ProgramToMcTransform tilt entry.

The sequence is configurable because real controllers interpret tilted plane commands with controller-dependent conventions (Fanuc G68.2 P formats, Siemens CYCLE800, Heidenhain PLANE SPATIAL); pick the sequence the target controller expects. Note the replaying parse side (IsoG68p2TiltSyntax) currently reads ZXZ only — in-project replay parity holds for the ZXZ default.

Implementation: the input is conjugated by a signed axis-permutation rotation into the family's canonical frame (ZXZ for proper-euler sequences, XYZ for Tait-Bryan), decomposed analytically there (with the gimbal branches), and mapped back; every result is verified by recomposition before reporting success.

FanucNcSentenceComposer

INcSentenceComposer targeting the Fanuc dialect: composes whole NC lines from the brand-neutral JSON sections of finished SyntaxPieces — the source text (e.g. NX CLSF records) does not participate. One instance owns the modal state of one stream walk; wrap it in a NcSynthesisConverter for the stream-level conversion (dst pieces + src↔dst map).

Vocabulary map (sections → Fanuc words):

  • ToolChangeT… M06; the tool id also spells the G43.4 H… offset id.
  • SpindleSpeedS… M03/M04 or M05.
  • CoolantM08/M07/M09.
  • ProgramToMcTransform tilt entry (TransformSource, MSYS rotation) plus the TransformSource translation → G68.2 X_ Y_ Z_ I_ J_ K_ (euler angles via EulerUtil in the configurable TiltEulerSequence, default ZXZ), identity → G69.
  • ToolHeightCompensationG43.4 H… (RTCP), so the replaying runner rebuilds the same tool-height + pivot transform chain. Emitted before the first motion even for zero offsets — the pivot entry only folds while RTCP or a tilted plane is active.
  • Motion pieces → G00/G01/G02/G03 with X Y Z from ProgramXyz, rotary words from MachineCoordinateState (degrees, already shortest-cyclic), F from modal Feedrate; arcs from ClArcEvent with in-plane I/J/K center offsets and G17/G18/G19 plane words.

Modal words (motion G-code, F, plane, tilt, H) are suppressed while unchanged; coordinate words are suppressed per-axis when the change is below the emitted precision. Unsupported content is reported through NcDiagnosticProgress with the piece as anchor and the motion degrades conservatively (arc with an off-axis plane falls back to a linear move to the endpoint, loudly).

NcConversion

Result of one Convert(IEnumerable<SyntaxPiece>, NcDstPieceFactory, NcDiagnosticProgress) run: the destination SyntaxPiece stream (execution order; each piece's BlockText holds the produced NC text) plus the src↔dst NcPieceMap. Text output is stage two — ToLines(IEnumerable<SyntaxPiece>) over DstPieces. Retained by the session (see MachiningSession.NcConversions) so a GUI can walk the mapping (src line ↔ dst line) after the conversion.

NcDstPieceFactory

Builds the destination SyntaxPieces of an Convert(IEnumerable<SyntaxPiece>, NcDstPieceFactory, NcDiagnosticProgress) run, stamping their identity: DstFilePath on the Sentence, a DstFileIndex/line-index char segment (line index advances per produced line), and a fresh SentenceIndex drawn from AllocateSentenceIndex.

In-session callers pass the session's Allocate() (and a FileIndexCounterDependency value) so destination identities stay unique against every played piece; out-of-session callers use CreateFallback(IReadOnlyCollection<SyntaxPiece>, string), which continues numbering after the source stream's maxima.

NcOptMixedPatchConverter

Mixed-mode INcPieceConverter for the NC-optimization output path: per-piece dispatch between the 1:1 patch semantics of NcPatchWriter (whose per-piece logic it reuses through ApplyRegisteredEdits(SyntaxPiece, NcDiagnosticProgress, ref HashSet<int>) — the public NcPatchWriter 1:1 contract stays untouched) and the 1:n splition fragment rewrite: a source piece whose SentenceIndex is keyed in SplitionPlansBySentenceIndex emits one destination piece per EMITTED fragment of its NcOptSplitionPlan, each produced by replaying the plan's recorded text operations onto a fresh copy of the source BlockText.

NcPatchUtil

Positional text-edit primitives for NcPatchWriter (the patch-mode INcPieceConverter): anchored token edits on a verbatim BlockText draft.

This is the SoftNc dual of HardNcUtil.SetTagNumber/AddBySeq with the two known defects fixed while keeping every other semantic:

Faithfully kept from HardNc: NaN deletes the token; an absent tag is inserted at the conventional word position (ConventionalTagOrder: X,Y,Z,I,J,K,A,B,C,F,S — X scans forward for the first present successor and lands before it, other tags scan backward for the nearest present predecessor and land after it, otherwise the token is appended at the end of the code region); formatted values trim trailing zeros but keep the decimal point (10.000010.), which also guards the value against no-decimal-point integer shrinking on replay.

Tag matching mirrors the parser's lexing exactly (GrabTagValue(ref string, IEnumerable<string>, string): case-insensitive, RegexFlagPrefix guard, and the full value grammar number|variable|bracket-expression) so that patch and parse agree on what a token is. A tag whose value is a variable or bracket expression is refused, never rewritten.

NcPatchWriter

Patch-mode INcPieceConverter: re-emits each piece's verbatim BlockText, applying only the anchored token edits registered in EditsBySentenceIndex. An untouched piece is reproduced byte-identically; an edited piece changes only in the addressed token spans — this is the conservative text-regeneration strategy the NC optimizer output path requires (same dialect as the source; dispatch rule per INcPieceConverter: source dialect must equal TargetBrand). Stage one of the two-stage writeback pattern: each source piece converts to exactly one destination piece (1:1 NcPieceMap) whose block text is the patched draft; stage two is ToLines(IEnumerable<SyntaxPiece>), which also splits a multi-line block back into its original lines.

The converter reproduces the executed piece stream as-is (expansion semantics): a sentence replayed by a loop or subprogram call appears once per execution, exactly like the final pipeline layer. Line-sequence-equal round-trips therefore hold for linear programs (byte-level fidelity — newline style, EOF newline, encoding — is owned by the serialization layer that writes the lines); for call/loop programs the output is the expanded execution sequence, not the source file.

Edits inside comments never happen: CommentSpanProvider (default GetFanucCommentSpans(string)) masks comment spans, so a literal like (F100 TEST) is never mistaken for an F word. A tag whose value is a variable or bracket expression is refused with Writeback-Patch--VariableValue instead of rewritten.

NcPieceConverterExtensions

Composition helpers over INcPieceConverter.

NcPieceMap

Bidirectional src↔dst piece mapping built by an INcPieceConverter: one source piece converts to n destination pieces (synthesis may spell several NC lines per source piece), while each destination piece derives from exactly one source piece. Keys are SentenceIndex values — the session-global execution-order identity already used for cross-process alignment — so a GUI can navigate src line ↔ dst line in both directions.

Register(SyntaxPiece, SyntaxPiece) also stamps a SourceRefSectionName section ({SentenceIndex, FileIndex, LineIndex} of the source piece) into the destination piece's JsonObject — the durable half of the mapping that survives piece serialization. The NC-optimization output path shares this exact mechanism for its own dst streams.

NcPieceTextUtil

Stage two of the two-stage NC writeback pattern — the reverse of an ISegmenter: walks a destination SyntaxPiece stream and emits its BlockTexts as NC text lines, splitting a multi-line block back into its original lines. Byte-level fidelity (newline style, EOF newline, encoding) is owned by the serialization layer that writes the lines.

NcSynthesisConverter

Synthesis-mode INcPieceConverter — the brand-agnostic stream stage of the no-source-text path: walks the source pieces in execution order, asks a fresh INcSentenceComposer for the lines each piece spells, and materializes them as destination pieces with the src↔dst map (each composed line becomes one destination piece, mapped n:1 back to the source piece that authored it; the composer's header rides on the first content piece, its trailer on the last walked piece). All dialect knowledge lives in the composer — targeting another brand means another INcSentenceComposer, not another converter.

NcTagEdit

A single anchored tag edit for NcPatchWriter: set the tag to the value (NaN deletes the token). A null format resolves to the writer's per-tag default (FeedrateFormat for F, CoordinateFormat otherwise).

NcWritebackSessionUtil

Shared vocabulary and identity allocation of the session writeback paths (MachiningSession.ConvertClToNcFiles and the NC optimization writeback in SoftNcOptProc): the output path template keywords and the per-output-file NcDstPieceFactory construction whose destination identities draw from the session counters, with a counter-less legacy fallback.

Structs

EulerSequence

An euler rotation sequence: three axis rotations applied in FirstSecondThird order, composing to R(Third)·R(Second)·R(First) in this codebase's row-vector matrix convention (mirroring how IsoG68p2TiltSyntax composes G68.2 I/J/K). Adjacent axes must differ; the twelve valid sequences split into the proper-euler family (First == Third, e.g. ZXZ) and the Tait-Bryan family (all three axes distinct, e.g. XYZ roll-pitch-yaw).

Interfaces

INcPieceConverter

Stage one of the two-stage NC writeback pattern: converts a finished source SyntaxPiece stream (the last layer of a SoftNc pipeline run) into a new destination SyntaxPiece stream whose BlockText holds the produced NC text, together with the src↔dst NcPieceMap. Stage two — serializing the destination stream to text lines — is the shared ToLines(IEnumerable<SyntaxPiece>) (a reversed segmenter), so every converter (cross-dialect synthesis, same-dialect patch, and the future optimization output) reuses one text stage and one mapping mechanism.

Two converter modes share this contract:

  • Patch mode (same dialect as the source) — uses BlockText as the verbatim draft and performs anchored token edits only (feedrate/coordinate rewrites). Zero information loss for untouched text; src↔dst map is 1:1. See NcPatchWriter.
  • Synthesis mode (cross dialect, e.g. NX CLSF → Fanuc) — composes whole lines from the brand-neutral JSON sections (MotionEvent/Feedrate/ProgramXyz/ ProgramToMcTransform/…). The source text does not participate; one src piece maps to n dst pieces. See NcSynthesisConverter, which delegates the per-sentence data→text step to an INcSentenceComposer.

Mode dispatch rule: a piece is patch-eligible when its source dialect (the pipeline's CncBrandDependency) equals TargetBrand; otherwise only synthesis applies.

INcSentenceComposer

The dedicated data→text seam of the writeback layer: picks data from a piece's brand-neutral JSON sections and composes new NC sentence text — no original NC text participates. This is the step that varies per target dialect, factored out of the stream-level INcPieceConverter (see NcSynthesisConverter) so every no-source-text path shares one contract:

  • CL → NC conversion — there is no original NC to reference; every line is composed from data (FanucNcSentenceComposer).
  • Future manipulation paths whose lines have no source-text counterpart at all. (NC optimization splition was originally listed here, but P2 shipped it on the patch side instead: a re-interpolated fragment derives from its source line, so it starts from that line's verbatim text and rewrites anchored tokens — see NcOptMixedPatchConverter. Composing those lines from data would have dropped whatever else the source line carried alongside the motion, such as an M code or an inline comment.)
  • Future manipulation paths that fabricate blocks without a source-text counterpart.

The source-text counterpart is the patch path (NcPatchUtil / NcPatchWriter): when an original text exists it stays the draft and only anchored tokens are edited. A future mixed stream can dispatch per piece — patch when a reference text is available, compose otherwise; if that dispatch must run on JSON alone (without reaching the source piece), the reference text can be carried as a JSON section alongside SourceRefSectionName (design affordance, not yet needed — the in-memory map already reaches the source BlockText).

A composer instance owns the modal state of one stream walk (emitted tilt / H / F / plane latches, header bookkeeping): create a fresh instance per conversion run, feed pieces in execution order, and flush with ComposeTrailerLines(NcDiagnosticProgress) after the stream drains.

Enums

EulerAxis

Rotation axis selector for EulerSequence.

NcPatchTagResult

Outcome of a SetTagNumber(ref string, string, double, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>, bool, bool) edit.

NcTagValueKind

Kind of value a tag literally carries in a block text, as classified by the patcher's own value grammar (TagValuePattern). Probing through this enum guarantees a caller agrees with SetTagNumber(ref string, string, double, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>, bool, bool) on what an editable token is.