This commit is contained in:
2026-09-11 03:46:11 +08:00
parent 45b9f9ed5d
commit b7f55aa631
114 changed files with 3646 additions and 576 deletions
@@ -126,7 +126,14 @@ and <a class="xref" href="../../api/Hi.NcParsers.Dependencys.Siemens.SiemensLoop
<code>I</code> / <code>J</code> / <code>K</code> circle centres. <code>G90 C=IC(360/17)</code> is one incremental index inside an absolute
program. Direction resolution lives in <a class="xref" href="../../api/Hi.NcParsers.LogicSyntaxs.McAbcCyclicPathSyntax.html">McAbcCyclicPathSyntax</a>:
<code>ACP()</code> takes the <code>[anchor, anchor+360)</code> window, <code>ACN()</code> the <code>(anchor-360, anchor]</code> window, <code>DC()</code>
the shortest swing, with the exact 180° tie going negative.</li>
the shortest swing, with the exact 180° tie going negative. On a machine-coordinate block the
increment is a distance in the <em>machine</em> frame, because
<a class="xref" href="../../api/Hi.NcParsers.LogicSyntaxs.MachineCoordSelectSyntax.html">MachineCoordSelectSyntax</a> is ordered ahead of
<a class="xref" href="../../api/Hi.NcParsers.LogicSyntaxs.IncrementalResolveSyntax.html">IncrementalResolveSyntax</a> in all five brand presets — Fanuc,
Mazak, Syntec, Siemens and Heidenhain — so <code>G53 X=IC(10)</code> and <code>SUPA Y=IC(-10)</code> 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.</li>
<li><strong>Coded positions</strong><code>CAC</code> / <code>CIC</code> / <code>CDC</code> / <code>CACP</code> / <code>CACN</code> take a 1-based indexing position
number rather than a coordinate, resolved against <a class="xref" href="../../api/Hi.NcParsers.Dependencys.IIndexingPositionConfig.html">IIndexingPositionConfig</a>,
implemented by <a class="xref" href="../../api/Hi.NcParsers.Dependencys.Siemens.SiemensMachineDataTable.html">SiemensMachineDataTable</a> from the real machine
@@ -155,6 +162,37 @@ is the one spelling that states all of them at once — it takes the last progra
at the <code>CC</code> 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
<code>Arc-CircleCenter--OnStartPoint</code> and is degraded to a linear move to the endpoint.</li>
<li><strong>Klartext incremental axis words</strong> — the <code>I</code>-prefixed word (<code>IX+20</code>, <code>IY-15</code>, rotary <code>IC+90</code>) 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: <code>L X+60 IY-10</code> mixes an absolute X with an incremental Y and
stamps only the Y. One shared grab serves five statement heads — <code>L</code>, <code>C</code>, <code>CC</code>, <code>LN</code> and
<code>CYCL CALL POS</code> — each writing the value under the <em>plain</em> axis key of its own section plus a
<code>&quot;PositioningOverride&quot;: { &quot;Y&quot;: &quot;Incremental&quot; }</code> entry on the block root. That is the same per-word
section the Siemens <code>IC()</code> wrapper writes, so the existing resolve consumers convert it and no
second incremental mechanism exists. Coverage differs by head: <code>L</code>, <code>C</code> and <code>LN</code> take the whole
axis tag list, rotary included, while <code>CC</code> and <code>CYCL CALL POS</code> take <code>X</code> / <code>Y</code> / <code>Z</code> only — a <code>CC</code>
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 (<code>L IX5</code>), a <code>Q</code> reference
resolves behind the prefix (<code>L IZ+Q2</code>), and the glued post-processed run <code>LIX+20IY-15</code> parses.
Before these words were read the plain grab could not see the <code>X</code> inside <code>IX</code> — its prefix guard
demands a word boundary, a digit or whitespace before the tag, and <code>I</code> is a word character — so
<code>L X+60 IY-10</code> took the X and dropped the Y: a motion to the wrong place, not a missing one.</li>
<li><strong>What an incremental word is measured from</strong> depends on the statement. An <code>L</code> / <code>C</code> / <code>LN</code>
endpoint and a <code>CC</code> centre are distances from the last programmed tool position, so
<code>CC IX+0 IY+11</code> 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.
<code>CYCL CALL POS</code> has its own reference: an <code>IX</code> / <code>IY</code> / <code>IZ</code> word there is a distance from the
coordinates the <em>previous</em> <code>CYCL CALL POS</code> 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 <code>HeidenhainCyclCall--IncrementalNoReference</code> 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 <code>HeidenhainCyclCall--IncrementalAfterMove</code>, the control's stated
reference winning. With <code>M91</code> 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 — <code>IC+270</code> 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 <code>CYCL DEF 7</code> datum-shift <code>I</code>
words are a different mechanism: <code>CYCL DEF 7.2 IY+5</code> 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.</li>
<li><strong>Q-parameters</strong><a class="xref" href="../../api/Hi.NcParsers.EvaluationSyntaxs.Heidenhain.HeidenhainExpressionParser.html">HeidenhainExpressionParser</a> lexes
<code>Q</code> / <code>QR</code> / <code>QL</code> / <code>QS</code>, the <code>DIV</code> keyword of FN 4 and the prefix <code>SQRT</code> of FN 5, so <code>FQ1</code> reaches
the feedrate, <code>L X+Q2</code> reaches the program XYZ and <code>TOOL CALL SQ3</code> reaches the spindle speed.
@@ -120,8 +120,8 @@ count the thermal FEM builds, so trailing shank nodes no longer sit at 0 K after
<li><strong>Cutter geometry is validated up front.</strong>
<a class="xref" href="../../api/Hi.Milling.Cutters.MillingCutter.html#Hi_Milling_Cutters_MillingCutter_GetUpperBeamGeometryIssues_">GetUpperBeamGeometryIssues</a> collects upper-beam and shank
configuration problems as keyed messages — for example an extended-cylinder beam whose full length
sits below the flute height (<code>Cutter-UpperBeam--BelowFluteHeight</code>), which inverts the shank solid and
makes the shank thermal model unbuildable. They are reported once per tool at <code>BeginSession</code> and at
sits below the flute height (<code>Cutter-UpperBeam--ExtendedCylinderFullLengthTooShort</code>), which
inverts the shank solid and makes the shank thermal model unbuildable. They are reported once per tool at <code>BeginSession</code> and at
each tool change, instead of surfacing later as a null-reference cascade inside the thermal physics
with nothing naming the beam.</li>
<li><strong><code>RakeFaceCuttingPara3d</code> no longer throws on a six-field parameter string</strong> (the guard read the
@@ -134,9 +134,16 @@ the <code>LocalProfileMillingPara(Vec3d, Vec3d)</code> constructor maps (x,y,z)
physics was inactive at simulation time). The gather pass counts both against the eligible steps:
silent at zero, one summary warning at or below
<a class="xref" href="../../api/Hi.MillingForces.Training.MillingTraining.html#Hi_MillingForces_Training_MillingTraining_MissingEngagementAbortRatio">MissingEngagementAbortRatio</a> (default 0.25), and a
configuration error above it. A gather pass that produces no samples at all now reports immediately
rather than throwing inside the SVD solve, separating &ldquo;not one step touched the workpiece&rdquo; from
&ldquo;touched steps whose mapped force data yielded no usable shots&rdquo;.</li>
configuration error above it. <strong>The breakdown in that summary is the diagnosis</strong>: misses sitting on
the engagement level mean the play itself ran with physics inactive, while a scattered few on the
luggage level are the read-back path, where a segment that covers a step but lacks its row is
dropped and answers null once before the next read rebuilds it. Above the ratio the training
aborts instead of continuing on what is left, because a parameter file built from a fraction of
the play is indistinguishable from a sound one once it has been written — its coefficients are
read back later as a real effect. Set the ratio to <code>1</code> to never abort. A gather pass that produces
no samples at all now reports immediately rather than throwing inside the SVD solve, separating
&ldquo;not one step touched the workpiece&rdquo; from &ldquo;touched steps whose mapped force data yielded no usable
shots&rdquo;.</li>
<li><strong>New training knobs.</strong> <a class="xref" href="../../api/Hi.MillingForces.Training.MillingTraining.html#Hi_MillingForces_Training_MillingTraining_EnableDesignMatrixSolver">EnableDesignMatrixSolver</a>
(default false) solves the least squares on a thin QR of the design matrix instead of forming the
normal equations, which square the condition number;
@@ -111,10 +111,20 @@ per-fragment arc IJK recomputation — including R-to-IJK conversion — whereve
carries the centre. An arc whose centre lives on a preceding modal block is exempt: its fragments
keep the source block's arc words and share that upstream centre line unchanged, so only their
endpoint and <code>F</code> words move. Two cases where the frozen HardNc optimizer is silently wrong are
downgraded to a non-split rewrite rather than reproduced: a <code>G91</code> incremental block (the old
fragment rewrite always emitted absolute coordinates), and a klartext <code>C … DR±</code> arc whose modal
<code>CC</code> chain left one of the two in-plane axes unstated, so the block's own start point supplied
that component — since every fragment would re-derive the centre from its own start. A klartext
downgraded to a non-split rewrite rather than reproduced, each warned once per run. The first is
an incremental block, and it is now taken by both of its spellings: <code>G91</code> modal state, <strong>or</strong> a
block whose modal state is absolute while individual axis words carry a per-word incremental
override — Siemens <code>X=IC(10)</code> and the coded <code>CIC()</code>, klartext <code>IX+20</code> and <code>IC+90</code>. The frozen
fragment rewrite carries no <code>G90</code>/<code>G91</code> handling at all and always re-emits a coordinate as an
absolute program value, so it splits both shapes and is wrong on each in its own way: the <code>G91</code>
line comes back stating an absolute number the control still reads as a distance, and the
per-word line keeps its incremental source word, so every fragment re-applies the same distance
from its own start. Both now stop at <code>NcOpt--SplitionIncrementalUnsupported</code>. The per-word
spelling is also the only one that can reach klartext: that dialect has no modal <code>G90</code>/<code>G91</code>
word, so a stamped axis word is the only incremental form it produces. The second case is a
klartext <code>C … DR±</code> arc whose modal <code>CC</code> chain left one of the two in-plane axes unstated, so the
block's own start point supplied that component — since every fragment would re-derive the centre
from its own start; that one reports <code>NcOpt--SplitionStartPointCenterUnsupported</code>. A klartext
<code>C</code> block whose centre lands entirely on its own start point never reaches that guard: its radius
is zero, so play degrades it to a chord under a validation warning of its own.
<strong>Neither the controller brand nor klartext as such refuses a split</strong>: a klartext <code>L</code> block
@@ -126,10 +126,18 @@ they are the fastest way to find out what a run actually did.</p>
<td>a Siemens (T, D) pair had no <code>$TC_DP</code> row; the generic tool height was used</td>
</tr>
<tr>
<td><code>SiemensToolOffset--TcdpRowMissingGenericHeightNaN</code></td>
<td>a Siemens (T, D) pair had no <code>$TC_DP</code> row and the generic tool height was NaN too; the D activation resolved to offset 0</td>
</tr>
<tr>
<td><code>Comp-ToolHeight--001</code></td>
<td>a G43.4 H word could not be resolved</td>
</tr>
<tr>
<td><code>RadiusComp--Interference</code></td>
<td>a G41/G42 block whose compensated path runs against the programmed direction (an inner arc or groove smaller than the offset radius); a real control stops there with an interference alarm (Fanuc PS0041) while the simulation continues on the swapped-side path — on a tool-centre CAM path the radius offset row (D row) must hold the wear only, not the tool radius</td>
</tr>
<tr>
<td><code>Coord-WorkOffset--AdditionalZero</code></td>
<td>a Fanuc-family additional work coordinate system (<code>G54.1 Pn</code>, also written <code>G54 Pn</code>) was selected but no offset has been entered for it; the program runs on the machine origin</td>
</tr>
@@ -174,6 +182,10 @@ they are the fastest way to find out what a run actually did.</p>
<td>a CL motion resolved no tool offset</td>
</tr>
<tr>
<td><code>CsvStepDurationClamped</code></td>
<td>a replayed CSV row's recorded step gap exceeded the single-step duration limit and was capped at it; such a gap is normally the seam where two recordings were concatenated</td>
</tr>
<tr>
<td><code>Map-ShotGap--StepsSkipped</code></td>
<td>steps were skipped because their shot pairing window held no measured row</td>
</tr>