This commit is contained in:
2026-09-15 01:12:50 +08:00
parent 2db889ad89
commit 1a75503105
99 changed files with 4168 additions and 726 deletions
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Class IncrementalResolveSyntax | HiAPI-C# 2025 ">
<meta name="description" content="Resolves G91 incremental axis values to absolute in-place within and its sub-sections. Reads written by . Per-word override: a block-root section (written by for the Siemens per-word coordinate functions, and by the Heidenhain L / C / CC / CYCL CALL POS parsers for the klartext I-prefixed words — see ; the CYCL CALL POS words never reach this syntax, resolves them on the spot ahead of it) beats the modal term for the listed axes on this block only: an entry converts that word even under G90, an entry skips it even under G91 — as does, deliberately, every other non-Incremental value (the rotary-family / / entries are absolute targets; their swing resolution lives in , not here). A coded-position entry ( / — Siemens CAC()/CIC() on a linear indexing axis) carries an indexing position number instead of a coordinate: the number is resolved through via , the word is rewritten to the resolved absolute coordinate, and the entry to ; a failed resolve reports an error and holds the last program position. Axes without an entry follow the modal term unchanged, so brands that never write the section (Fanuc/...) keep the exact legacy behavior. specifies which JSON paths contain axis values that need incremental-to-absolute conversion. Default: [[&quot;Parsing&quot;], [&quot;Parsing&quot;, &quot;G28&quot;]]; the Heidenhain bundle instead walks [&quot;Parsing&quot;, &quot;CC&quot;] for the klartext circle-center record (). All matching paths are converted against the same last program position — a nested record&#39;s words are distances from where the tool stands, exactly like the root&#39;s. Canned cycle paths (Parsing.G81, G82, G83, …) are intentionally excluded — their Z/R incremental semantics differ from normal axes (R is relative to init level, Z is relative to R-point). Resolution is handled by inside each cycle syntax class, which runs before this syntax. Uses to determine which tags are motion axes. Traces backward nodes for last known to resolve incremental values. After this syntax, all axis values in the working paths are absolute — can consume them without incremental logic.">
<meta name="description" content="Resolves G91 incremental axis values to absolute in-place within and its sub-sections. Reads written by . Per-word override: a block-root section (written by for the Siemens per-word coordinate functions, and by the Heidenhain L / C / CC / CYCL CALL POS parsers for the klartext I-prefixed words — see ; the CYCL CALL POS words never reach this syntax, resolves them on the spot ahead of it) beats the modal term for the listed axes on this block only: an entry converts that word even under G90, an entry skips it even under G91 — as does, deliberately, every other non-Incremental value (the rotary-family / / entries are absolute targets; their swing resolution lives in , not here). A coded-position entry ( / — Siemens CAC()/CIC() on a linear indexing axis) carries an indexing position number instead of a coordinate: the number is resolved through via , the word is rewritten to the resolved absolute coordinate, and the entry to ; a failed resolve reports an error and holds the last program position. Axes without an entry follow the modal term unchanged, so brands that never write the section (Fanuc/...) keep the exact legacy behavior. specifies which JSON paths contain axis values that need incremental-to-absolute conversion. Default: [[&quot;Parsing&quot;], [&quot;Parsing&quot;, &quot;G28&quot;]]; the Heidenhain bundle instead walks [&quot;Parsing&quot;, &quot;CC&quot;] for the klartext circle-center record (). All matching paths are converted against the same last program position — a nested record&#39;s words are distances from where the tool stands, exactly like the root&#39;s. Canned cycle paths (Parsing.G81, G82, G83, …) are intentionally excluded — their Z/R incremental semantics differ from normal axes (R is relative to init level, Z is relative to R-point). Resolution is handled by inside each cycle syntax class, which runs before this syntax. Uses to determine which tags are motion axes. Traces backward nodes for last known to resolve incremental values. After this syntax, all axis values in the working paths are absolute — can consume them without incremental logic. Rotary words (A/B/C) at the Parsing root never reach this syntax — consumes them upstream and follows the modal G91 itself. Inside a nested record they do (G91 G28 C10. — the Parsing.G28 intermediate), and a rotary axis has no program→MC transform, so its anchor is the previous modal angle () rather than a component of the program XYZ: the intermediate lands at current + 10&#176;, the way HardNc&#39;s ParseG28 reads it against the previous MC. A never-set rotary axis anchors at 0. The rewrite is in place, and the modal section keeps saying G91 (the next block&#39;s lookback needs it), so a converted word is indistinguishable from a programmed absolute. Every word this syntax converts is therefore also recorded, as programmed, in the one-shot block-root section, shaped like the Parsing tree it came from ({ &quot;X&quot;: 10, &quot;G28&quot;: { &quot;C&quot;: 10 } }). The record doubles as the idempotency guard: a word already listed at its path is left alone, so a second pass over the same block — a project file whose serialized list carries two instances, a re-run over a frozen piece — cannot add the anchor twice. Coded-position words are not recorded (they are not increments of a coordinate; their trace is the rewritten override entry).">
<link rel="icon" href="../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
@@ -160,6 +160,33 @@ After this syntax, all axis values in the working paths are absolute —
<a class="xref" href="Hi.NcParsers.LogicSyntaxs.ProgramXyzSyntax.html">ProgramXyzSyntax</a> can consume them without
incremental logic.
</p>
<p>
Rotary words (A/B/C) at the <code>Parsing</code> root never reach this
syntax — <a class="xref" href="Hi.NcParsers.LogicSyntaxs.McAbcSyntax.html">McAbcSyntax</a> consumes them upstream and follows
the modal G91 itself. Inside a nested record they do
(<code>G91 G28 C10.</code> — the <code>Parsing.G28</code> intermediate), and a
rotary axis has no program→MC transform, so its anchor is the
previous modal angle
(<a class="xref" href="Hi.NcParsers.LogicSyntaxs.RotaryAxisUtil.html#Hi_NcParsers_LogicSyntaxs_RotaryAxisUtil_FindPreviousMcAxisDeg_Hi_Common_Collections_LazyLinkedListNode_Hi_NcParsers_Syntaxs_SyntaxPiece__System_String_">FindPreviousMcAxisDeg(LazyLinkedListNode&lt;SyntaxPiece&gt;, string)</a>) rather than a
component of the program XYZ: the intermediate lands at
current + 10°, the way HardNc's <code>ParseG28</code> reads it against
the previous MC. A never-set rotary axis anchors at 0.
</p>
<p>
The rewrite is in place, and the modal <a class="xref" href="Hi.NcParsers.Keywords.Positioning.html">Positioning</a>
section keeps saying <code>G91</code> (the next block's lookback needs it),
so a converted word is indistinguishable from a programmed absolute.
Every word this syntax converts is therefore also recorded, as
programmed, in the one-shot block-root <a class="xref" href="Hi.NcParsers.Keywords.IncrementalWords.html">IncrementalWords</a>
section, shaped like the <code>Parsing</code> tree it came from
(<code>{ "X": 10, "G28": { "C": 10 } }</code>). The record doubles as the
idempotency guard: a word already listed at its path is left alone,
so a second pass over the same block — a project file whose
serialized list carries two instances, a re-run over a frozen piece
— cannot add the anchor twice. Coded-position words are not recorded
(they are not increments of a coordinate; their trace is the
rewritten override entry).
</p>
</div>
<div class="markdown conceptual"></div>
@@ -259,7 +286,8 @@ incremental deltas:
<code>MachineCoordinateState=(100,200,300)</code>. Under the identity
<code>ProgramToMcTransform</code> chain, <code>GetLastProgramXyz</code> recovers
program XYZ equal to MC, so each axis in <code>Parsing</code> is rewritten
to <code>lastAbs + incremental</code>:
to <code>lastAbs + incremental</code>; the programmed deltas go to
<code>IncrementalWords</code>:
#Previous:</p>
<pre><code class="lang-csharp">{ "MachineCoordinateState": { "X": 100, "Y": 200, "Z": 300 } }</code></pre>
<p>#BeforeBuild:</p>
@@ -270,13 +298,15 @@ to <code>lastAbs + incremental</code>:
<p>#AfterBuild:</p>
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"Parsing": { "X": 110, "Y": 220, "Z": 330 }
"Parsing": { "X": 110, "Y": 220, "Z": 330 },
"IncrementalWords": { "X": 10, "Y": 20, "Z": 30 }
}</code></pre>
<p>G91 + <code>Parsing.G28</code> sub-section — exercises the second entry
of the default <code>WorkingPathList</code>; the root <code>Parsing</code> has
no X/Y/Z so the first path no-ops, but the
<code>[&ldquo;Parsing&rdquo;,&ldquo;G28&rdquo;]</code> path picks up the G28 intermediate axes
and resolves them against the same <code>lastProgramXyz</code>:
and resolves them against the same <code>lastProgramXyz</code>. The record
mirrors the nesting — the words sit under <code>G28</code>:
#Previous:</p>
<pre><code class="lang-csharp">{ "MachineCoordinateState": { "X": 100, "Y": 200, "Z": 300 } }</code></pre>
<p>#BeforeBuild:</p>
@@ -287,7 +317,8 @@ and resolves them against the same <code>lastProgramXyz</code>:
<p>#AfterBuild:</p>
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"Parsing": { "G28": { "X": 105, "Y": 210, "Z": 315 } }
"Parsing": { "G28": { "X": 105, "Y": 210, "Z": 315 } },
"IncrementalWords": { "G28": { "X": 5, "Y": 10, "Z": 15 } }
}</code></pre>
<p>G90 (absolute) with a per-word <code>PositioningOverride</code> — the
Siemens <code>X=IC(10)</code> shape after
@@ -305,11 +336,12 @@ against the last program position; Y follows the modal G90 and stays:
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G90", "Mode": "Absolute" },
"PositioningOverride": { "X": "Incremental" },
"Parsing": { "X": 110, "Y": 20 }
"Parsing": { "X": 110, "Y": 20 },
"IncrementalWords": { "X": 10 }
}</code></pre>
<p>G91 (incremental) with an <code>Absolute</code> override on X — X is
skipped (already absolute, e.g. from <code>X=AC(25)</code>), Y still
converts under the modal G91:
skipped (already absolute, e.g. from <code>X=AC(25)</code>) and stays out
of the record, Y still converts under the modal G91:
#Previous:</p>
<pre><code class="lang-csharp">{ "MachineCoordinateState": { "X": 100, "Y": 200, "Z": 300 } }</code></pre>
<p>#BeforeBuild:</p>
@@ -322,7 +354,8 @@ converts under the modal G91:
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"PositioningOverride": { "X": "Absolute" },
"Parsing": { "X": 25, "Y": 220 }
"Parsing": { "X": 25, "Y": 220 },
"IncrementalWords": { "Y": 20 }
}</code></pre>
<p>Coded-position absolute on a linear indexing axis (the Siemens
<code>X=CAC(2)</code> workholder shape after the unwrap + evaluation
@@ -360,7 +393,46 @@ root carries no axis word on a CC block and is untouched:
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G90", "Mode": "Absolute" },
"PositioningOverride": { "X": "Incremental", "Y": "Incremental" },
"Parsing": { "CC": { "X": 10, "Y": 31 } }
"Parsing": { "CC": { "X": 10, "Y": 31 } },
"IncrementalWords": { "CC": { "X": 0, "Y": 11 } }
}</code></pre>
<p>G91 + <code>Parsing.G28</code> carrying a rotary word (<code>G91 G28 X0 C10.</code>
on the A/B/C fallback axis list): the rotary anchor is the previous
modal angle in <code>#Previous:</code> <code>MachineCoordinateState.C</code>, so
the intermediate C is 30 + 10 = 40 — while X still resolves against
the last program position (100 + 0). A root rotary word would never
be here: <a class="xref" href="Hi.NcParsers.LogicSyntaxs.McAbcSyntax.html">McAbcSyntax</a> consumes it first:
#Previous:</p>
<pre><code class="lang-csharp">{ "MachineCoordinateState": { "X": 100, "Y": 200, "Z": 300, "C": 30 } }</code></pre>
<p>#BeforeBuild:</p>
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"Parsing": { "G28": { "X": 0, "C": 10 } }
}</code></pre>
<p>#AfterBuild:</p>
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"Parsing": { "G28": { "X": 100, "C": 40 } },
"IncrementalWords": { "G28": { "X": 0, "C": 10 } }
}</code></pre>
<p>A second pass over an already-resolved block — the <code>#BeforeBuild:</code>
is the previous case's <code>#AfterBuild:</code> for a root word: the
record lists X, so the syntax leaves the (now absolute) 110 alone
instead of adding the anchor again. The guard is what makes the
in-place rewrite safe against a list that runs the resolve twice:
#Previous:</p>
<pre><code class="lang-csharp">{ "MachineCoordinateState": { "X": 100, "Y": 200, "Z": 300 } }</code></pre>
<p>#BeforeBuild:</p>
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"Parsing": { "X": 110 },
"IncrementalWords": { "X": 10 }
}</code></pre>
<p>#AfterBuild:</p>
<pre><code class="lang-csharp">{
"Positioning": { "Term": "G91", "Mode": "Incremental" },
"Parsing": { "X": 110 },
"IncrementalWords": { "X": 10 }
}</code></pre>