Table of Contents

Class NcOptPieceClassifier

Namespace
Hi.NcOpt
Assembly
HiMech.dll

Classifies the SoftNc final-layer SyntaxPiece stream into the INcOptPiecePack list and the per-step StepOptContext dictionary that the StepFeedSolver consumes: maps each piece to its MachiningStep index segment, runs the tool-change state machine, evaluates the opt-ability criteria and seeds preserved packs with their constraining feedrate.

public static class NcOptPieceClassifier
Inheritance
NcOptPieceClassifier
Inherited Members

Remarks

Ported from HiNc NcOptProc.CreateLinePackList + OnToolChanging (frozen HardNc baseline). The HardNc NcFlagBitArray reads are replaced by reads of the SoftNc JSON sections (ToolChange, ToolHeightCompensation, CannedCycle, MotionState/MotionEvent, CompoundMotion, RadiusCompensation, Feedrate), and the HardNc GetStepIndexSegment FileLineIndex scan is replaced by a two-pointer walk over SentenceIndex / SentenceIndex that yields CONTIGUOUS segments (see GetStepIndexSegment(List<MachiningStep>, ref int, int)): every step index lands in exactly one piece's segment, so downstream step-keyed dictionaries built from the segments are hole-free over the whole step list.

Null-step tolerance mirrors the oracle exactly where the oracle has it: the HardNc step list is the raw unfiltered strip-pose projection and only the segment key selector is null-conditional (machiningStep?.SourceCommand?...) — a null entry behaves as a key-less (SentenceIndex == -1) step in the mapping. Everywhere else (opt-ability probes, distance walkers, the solver) steps are read directly, exactly as the oracle reads them; no extra null guards are added.

The preserved-pack seed precedence mirrors the HardNc chain exactly: blank/comment-only line → +Inf; else macro line (in-canned-cycle, G28 or G53.1, see NcFlagUtil.IsMacro) → the minimum RapidFeed_mmds over the seed option list (the OPTION rapid feed, not the machine rapid); else rapid (G00-modal) line → the machine rapid feedrate (the value HardNcLine.Feedrate_mmds reads on a rapid line, inherited from HardNcEnv); else the line's modal F.

P2 splition emission: an opt-able piece whose seed options enable interpolation (and which is not radius-compensating nor a multi-turn arc) is emitted as a SplitionPiecePack — its ctor seeds the per-step StepAdjustment list — mirroring the oracle's SplitionLinePack emission. A SoftNc-only guard downgrades such a piece to MonoPiecePack with one diagnostic per run, safer than the oracle's silent wrongness: an incremental piece — G91 modal, or any word stamped Incremental in the block-root PositioningOverride (Siemens IC(), klartext IX+20) (NcOpt--SplitionIncrementalUnsupported; the oracle has no G90/G91 handling and its fragment rewrite always emits absolute coordinates, and the fragment composer's tag patcher cannot even see the axis letter inside IX, so it would insert a second absolute word beside the surviving incremental one). A Dynamic (RTCP) ProgramToMcTransform chain is NOT a guard: the fragment engine rebuilds the Dynamic entries at each step's own ABC (GetComposedTransformAtAbc(JsonObject, vec3d_t, vec3d_t, IMachineKinematics, double)), keeping the per-step inverse valid at interpolated points — the SoftNc dual of the oracle's per-step XyzabcSolver.McToPn re-solve (HardNcLine.GetProgramXyz). The second guard is a Heidenhain klartext arc whose center comes from its own start point (NcOpt--SplitionStartPointCenterUnsupported): a C … DR± block with no CC ever given, or with a CC that left an in-plane axis unstated, fills the center from the current position (HeidenhainCircularMotionSyntax stamps StartPoint), so on a re-parse every fragment would fill it from ITS start and the split could not share one circle. Neither the brand nor klartext as such is a guard: a klartext linear L block splits like an ISO one (the fragment rewrite only touches endpoint/rotary/F words, all in the L vocabulary), and a klartext arc with a CC pole splits into fragments that keep the block's words around the unchanged CC line — the plan builder skips the I/J/K rebuild for a ModalCircleCenter arc. IsArc follows the oracle's MODAL semantics (NcFlagUtil.IsOnArcCommand, the G02/G03 Group-01 latch); the one-shot arc-data presence is carried separately as HasArcMotionEvent.

Scope notes still in effect. The Heidenhain L/LN veto of the HardNc IsRapid predicate (an L/LN line is never rapid, even under FMAX) is deliberately NOT reproduced: the SoftNc Logic bundle normalizes a klartext L block onto the shared ISO vocabulary and consumes the L marker, stamping G00 for FMAX and G01 otherwise (HeidenhainMotionModeSyntax), so no piece-level probe for "this was an L statement" survives — and porting the veto on top of that normalization would classify an FMAX rapid as a feed move, which is wrong in the SoftNc model rather than merely different. Reproducing it would require carrying the klartext statement kind into the sections; that is a deliberate open question, not an oversight. G95 feed-per-revolution is warned once per run (NcOpt--FeedPerRevNotSupported) while its raw value is still treated as mm/min.

Methods

CreatePiecePackList(IReadOnlyList<SyntaxPiece>, List<MachiningStep>, SortedList<int, NcOptOption>, MachiningToolHouse, bool, double, out Dictionary<int, StepOptContext>, NcDiagnosticProgress, IProgress<IMessage>, CancellationToken)

Creates the piece pack list with the step segments built, and prepares the StepOptContext seats of every step of the opt-able pieces (only those steps get an entry).

public static List<INcOptPiecePack> CreatePiecePackList(IReadOnlyList<SyntaxPiece> pieces, List<MachiningStep> machiningSteps, SortedList<int, NcOptOption> stepIndexToSeedOptionSortedList, MachiningToolHouse machiningToolHouse, bool isPhysicsComputable, double machineRapidFeedrate_mmds, out Dictionary<int, StepOptContext> stepIndexToStepOptContextDictionary, NcDiagnosticProgress conversionProgress, IProgress<IMessage> messageProgress, CancellationToken cancellationToken)

Parameters

pieces IReadOnlyList<SyntaxPiece>

Final-layer syntax pieces in execution order, already filtered of the init seed (pieces without a source file path). SentenceIndex must be strictly increasing (gaps allowed); the precondition is validated and a violation aborts the classification (see returns).

machiningSteps List<MachiningStep>

The raw strip-pose step projection, UNFILTERED (a strip pos without a machining step contributes a null entry — the oracle's unfiltered read), ordered so that SentenceIndex is monotonic non-decreasing (key-less -1 steps may be interspersed). Step indices used in the outputs are positions in this list, which equal StepIndex (the strip pos index) exactly because the list is unfiltered.

stepIndexToSeedOptionSortedList SortedList<int, NcOptOption>

Sorted list mapping step index to the seed NcOptOption effective from that step on (floor lookup semantics).

machiningToolHouse MachiningToolHouse

The machining tool house (int tool id keys).

isPhysicsComputable bool

Whether the physics data required by the solver exists (HardNc equivalent: millingPara != null).

machineRapidFeedrate_mmds double

The machine rapid traverse feedrate in mm/s (HardNc equivalent: HardNcEnv.RapidFeedrate_mmds), see GetMachineRapidFeedrate_mmds(MachiningSession). NaN when no rapid-feedrate config is reachable; rapid pieces then fall back per piece, see ResolveRapidFeedrate_mmds(double, List<MachiningStep>, IndexSegment).

stepIndexToStepOptContextDictionary Dictionary<int, StepOptContext>

Output: one StepOptContext per step of every opt-able piece, keyed by step index, with the ExtendedIndexSegment already fixed up to inclusive-begin / exclusive-end.

conversionProgress NcDiagnosticProgress

Optional diagnostic home for piece-anchored warnings (tool not found, splition guard downgrades).

messageProgress IProgress<IMessage>

The message host for progress logging.

cancellationToken CancellationToken

Cancellation token to cancel the operation.

Returns

List<INcOptPiecePack>

One INcOptPiecePack per piece, in the same order — the piece segments are contiguous and cover every step index (see GetStepIndexSegment(List<MachiningStep>, ref int, int)). Null when canceled, or when the piece SentenceIndex stream is not strictly increasing (e.g. a legacy counter-less preset replaying a subprogram): a NcOpt-Pieces–NonMonotonic error is emitted on both the message channel and the diagnostic home and the optimization aborts gracefully (the orchestrator treats null as the cancel path).

GetEffectiveMachineRapidFeedrate_mmds(double)

The machine rapid feedrate to seed modal-F state with, in mm/s: machineRapidFeedrate_mmds when resolved (non-NaN), otherwise the 20000 mm/min sentinel — the shared default of both HardNcEnv.RapidFeedrate_mmds and IRapidFeedrateConfig. This is the SoftNc dual of the HardNc chain seed: HardNcEnv.RefNcLineOnInit seeds BOTH the rapid and the machining modal-F registers with the env rapid.

public static double GetEffectiveMachineRapidFeedrate_mmds(double machineRapidFeedrate_mmds)

Parameters

machineRapidFeedrate_mmds double

The machine rapid feedrate in mm/s; NaN when unresolved (see GetMachineRapidFeedrate_mmds(MachiningSession)).

Returns

double

GetLiteralArcTurnCount(string)

The literal L (turn count) word on an arc block's text, comment masked, or 0 when absent/non-numeric — the SoftNc stand-in for the oracle's ArcNcArg.L multi-turn splition veto (frozen NcOptProc): a value > 0 refuses re-interpolation.

public static double GetLiteralArcTurnCount(string blockText)

Parameters

blockText string

The arc block's text; null/empty reads 0.

Returns

double

Remarks

Deliberately NOT the parsed MotionEvent.AdditionalCircleNum. That field is derived, not literal: CircularMotionSyntaxUtil.IsClosedOnPlane makes it 1 for an L-less closed full circle (the renderer needs the extra turn) and a pitch helix can carry one too — arcs the oracle splits happily, because its veto reads the L word alone. Probing the source text keeps the two sides on the same criterion. Only reached under a G02/G03 modal, so a Heidenhain L motion word never lands here (and it is followed by a space, not a number, so it would read as absent anyway).

GetLiteralFeedrateTagKind(string)

Classifies the literal F token of the block text (outside comments) through the patcher's own value grammar, ProbeTagValue(string, string, string, IReadOnlyList<(int Start, int Length)>, IReadOnlyList<string>) (number | variable/bracket expression | keyword) — so this probe and a subsequent NcPatchWriter F edit can never disagree on token presence or editability. The modal-carried Feedrate section cannot answer this — it is present on every block once any F has appeared — so the writeback path uses this text probe to know when a preserved line keeps its own F token, when deleting the F token is meaningful at all, and when an F edit would be refused (Unpatchable: e.g. F#500). Fanuc-family comment spans and the # variable prefix are assumed (P1 scope; the writer is brand-aware, the probe follows in P2).

public static NcTagValueKind GetLiteralFeedrateTagKind(string blockText)

Parameters

blockText string

The block text to test.

Returns

NcTagValueKind

HasLiteralFeedrateTag(string)

Whether the block text literally carries an F word (outside comments), numeric or not — see GetLiteralFeedrateTagKind(string).

public static bool HasLiteralFeedrateTag(string blockText)

Parameters

blockText string

The block text to test.

Returns

bool

HasStartPointArcCenter(JsonObject)

Whether the piece's one-shot arc event took its own start point as the center — its MotionEvent carries CenterSource == StartPoint, the stamp HeidenhainCircularMotionSyntax writes on a klartext C block whose CC is missing or leaves an in-plane component unstated. This is the probe behind the NcOpt–SplitionStartPointCenterUnsupported guard (see class remarks); a missing event or stamp (block-word centers, the modal CC center) reads false.

public static bool HasStartPointArcCenter(JsonObject motionEvent)

Parameters

motionEvent JsonObject

The piece's MotionEvent section; may be null.

Returns

bool

IsBlankOrCommentOnly(string)

Whether the block text is blank or consists of comments only (Fanuc family comment shapes: head-% line and paren spans). Such a piece never constrains its neighbors, so its preserved seed feedrate is positive infinity.

public static bool IsBlankOrCommentOnly(string blockText)

Parameters

blockText string

Returns

bool

IsIncrementalPositioning(JsonObject)

Whether any coordinate word of the piece is incremental — its Positioning section (written on every NC block by PositioningSyntax, defaulting to absolute) carries Mode == Incremental, or its block-root PositioningOverride section stamps at least one word Incremental / CodedIncremental (Siemens IC() / CIC(), klartext IX+20; the coded value is listed for completeness — the write stage rewrites it to its plain counterpart before any piece reaches the optimizer). A missing section (e.g. a CL-sourced piece) counts as absolute. This is the probe behind the NcOpt–SplitionIncrementalUnsupported guard (see class remarks).

public static bool IsIncrementalPositioning(JsonObject json)

Parameters

json JsonObject

The piece's JSON object.

Returns

bool

IsMacroPiece(JsonObject)

Whether the piece is a macro line — a drilling-cycle body, a reference return, a tool-center alignment or a swivel — whose preserved seed feedrate is the minimum option rapid feed rather than the line's modal F. Mirrors the HardNc NcFlagUtil.IsMacro predicate over the SoftNc sections: inside a canned cycle (CannedCycle.Term other than G80, a missing section counting as G80 per the init seed), a G28 reference return (CompoundMotion.Term), a G53.1 tool-center alignment or a Siemens CYCLE800 swivel — the last two both being one-shot MotionEvent terms (G53p1RotaryPositionSyntax, SiemensCycle800TiltSyntax).

public static bool IsMacroPiece(JsonObject json)

Parameters

json JsonObject

The piece's JSON object.

Returns

bool

IsSimpleFeedrateAssignable(string, IProgress<IMessage>)

Whether an F word can simply be inserted into (or restated on) the block: the block carries a G01/G02/G03 (or Heidenhain L/LN) token, or — excluding head-% blocks — it consists solely of axis words (X/Y/Z and A/B/C both present) with nothing else left after grabbing them.

public static bool IsSimpleFeedrateAssignable(string blockText, IProgress<IMessage> messageProgress)

Parameters

blockText string

The block text to test.

messageProgress IProgress<IMessage>

The message host for parse-error logging.

Returns

bool

Remarks

Ported from HiNc NcOptProc.IsSimpleFeedrateAssignableNcLine (frozen HardNc baseline) over BlockText, including the original's requirement that both an XYZ and an ABC word exist on the bare-coordinate branch.

ResolveRapidFeedrate_mmds(double, List<MachiningStep>, IndexSegment)

Resolves the effective rapid feedrate of a rapid (G00-modal) piece in mm/s: machineRapidFeedrate_mmds when resolved (non-NaN); otherwise the maximum played step feedrate over the piece's segment (the SoftNc player runs rapids at the configured per-axis rapid rates, so the effective step feedrate approximates the machine rapid); last resort the 20000 mm/min sentinel — the shared default of both HardNcEnv.RapidFeedrate_mmds and IRapidFeedrateConfig.

public static double ResolveRapidFeedrate_mmds(double machineRapidFeedrate_mmds, List<MachiningStep> machiningSteps, IndexSegment stepIndexSegment)

Parameters

machineRapidFeedrate_mmds double

The machine rapid feedrate in mm/s; NaN when unresolved.

machiningSteps List<MachiningStep>

All machining steps.

stepIndexSegment IndexSegment

The piece's step index segment.

Returns

double