Class NcOptPieceClassifier
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,
HiNc/NcOpt/NcOptProc.cs lines 613-829). 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 (lines 127-134,
693-695) 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
(NcOptProc.cs:2521) and only the segment key selector is
null-conditional (machiningStep?.SourceCommand?...,
NcOptProc.cs:132) — 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 (e.g. NcOptProc.cs:739, 1211,
1236); no extra null guards are added.
The preserved-pack seed precedence mirrors the HardNc chain (lines
803-813) exactly: blank/comment-only line → +Inf; else macro line
(in-canned-cycle, G28 or G53.1, see NcFlagUtil.IsMacro lines
648-656) → 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,
HardNcLine.cs:206-211, 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 (NcOptProc.cs:748-793). Two
SoftNc-only guards downgrade such a piece to MonoPiecePack
with one diagnostic per run each, safer than the oracle's silent
wrongness: a G91 incremental-modal piece
(NcOpt--SplitionIncrementalUnsupported; the oracle has no
G90/G91 handling and its fragment rewrite always emits absolute
coordinates, NcOptProc.cs:1934-1935), and a piece whose
ProgramToMcTransform chain carries a Dynamic (RTCP) entry
(NcOpt--SplitionRtcpUnsupported; a Dynamic entry is a
block-endpoint snapshot whose per-step inverse is invalid at
interpolated points, see
HasDynamicEntry(JsonObject)). A third guard is
brand-shaped: under
Heidenhain the fragment rewrite would
splice ISO-shaped words into klartext
(NcOpt--SplitionKlartextUnsupported) — a klartext arc parses
through HeidenhainCircularMotionSyntax into the same
MotionEvent { Form: McArc, ArcCenter } the ISO path produces, so
the splition arc rewrite would emit I/J/K offset words into a
C … DR+ block that has no such vocabulary (its center comes from
the modal CC block), and the block's RND R…/radius words
would read as an R-format arc to delete.
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 (NcFlagUtil.cs:633-635: 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
(LogicSyntaxs.Heidenhain.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, string, 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, string targetBrand, out Dictionary<int, StepOptContext> stepIndexToStepOptContextDictionary, NcDiagnosticProgress conversionProgress, IProgress<IMessage> messageProgress, CancellationToken cancellationToken)
Parameters
piecesIReadOnlyList<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).
machiningStepsList<MachiningStep>The raw strip-pose step projection, UNFILTERED (a strip pos without a machining step contributes a null entry — the oracle's unfiltered read,
NcOptProc.cs:2521), ordered so that SentenceIndex is monotonic non-decreasing (key-less-1steps 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.stepIndexToSeedOptionSortedListSortedList<int, NcOptOption>Sorted list mapping step index to the seed NcOptOption effective from that step on (floor lookup semantics).
machiningToolHouseMachiningToolHouseThe machining tool house (int tool id keys).
isPhysicsComputableboolWhether the physics data required by the solver exists (HardNc equivalent:
millingPara != null).machineRapidFeedrate_mmdsdoubleThe 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).targetBrandstringThe session's CNC brand (CncBrandDependency values; defaults to Fanuc) — consulted only by the klartext splition guard, see the class remarks.
stepIndexToStepOptContextDictionaryDictionary<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.
conversionProgressNcDiagnosticProgressOptional diagnostic home for piece-anchored warnings (tool not found, splition guard downgrades).
messageProgressIProgress<IMessage>The message host for progress logging.
cancellationTokenCancellationTokenCancellation 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–NonMonotonicerror 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 (HardNcEnv.cs:252) and
IRapidFeedrateConfig
(ControllerParameterTableBase.cs:39). 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
(HardNcLine.cs:332-333).
public static double GetEffectiveMachineRapidFeedrate_mmds(double machineRapidFeedrate_mmds)
Parameters
machineRapidFeedrate_mmdsdoubleThe machine rapid feedrate in mm/s; NaN when unresolved (see GetMachineRapidFeedrate_mmds(MachiningSession)).
Returns
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.cs:757-762): a value > 0 refuses re-interpolation.
public static double GetLiteralArcTurnCount(string blockText)
Parameters
blockTextstringThe arc block's text; null/empty reads 0.
Returns
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
blockTextstringThe block text to test.
Returns
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
blockTextstringThe block text to test.
Returns
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
blockTextstring
Returns
IsIncrementalPositioning(JsonObject)
public static bool IsIncrementalPositioning(JsonObject json)
Parameters
jsonJsonObject
Returns
IsMacroPiece(JsonObject)
Whether the piece's modal positioning state is G91 incremental — its
Positioning section (written on every NC block by
PositioningSyntax, defaulting to absolute) carries
Mode == Incremental. 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 IsMacroPiece(JsonObject json)
Parameters
jsonJsonObjectThe piece's JSON object.
Returns
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
blockTextstringThe block text to test.
messageProgressIProgress<IMessage>The message host for parse-error logging.
Returns
Remarks
Ported from HiNc NcOptProc.IsSimpleFeedrateAssignableNcLine
(frozen HardNc baseline, lines 1994-2025) 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 (HardNcEnv.cs:252) and
IRapidFeedrateConfig
(ControllerParameterTableBase.cs:39).
public static double ResolveRapidFeedrate_mmds(double machineRapidFeedrate_mmds, List<MachiningStep> machiningSteps, IndexSegment stepIndexSegment)
Parameters
machineRapidFeedrate_mmdsdoubleThe machine rapid feedrate in mm/s; NaN when unresolved.
machiningStepsList<MachiningStep>All machining steps.
stepIndexSegmentIndexSegmentThe piece's step index segment.