This commit is contained in:
2026-09-12 18:16:27 +08:00
parent b7f55aa631
commit 397a7a8cc4
96 changed files with 1167 additions and 1259 deletions
@@ -180,7 +180,7 @@ public static void Reg(XFactory factory = null)
<p><strong><code>exhibitionOnly</code> suppresses the XML writes; it does not reach the copy.</strong> <code>MakeXmlSourceToFileRef</code> and <code>SaveToFileRef</code> both honour it and produce the reference elements without creating anything, but <code>StlFile</code>'s copy-on-save does not consult it at all. So a purely in-memory round trip still copies cached meshes into a base directory that lacks them — which is what a project load does when it materialises the runtime equipment from the authored one. Writing is not confined to saving.</p>
</div>
<h2 id="boot-path">Boot path</h2>
<p>An application's entry point (web service <code>Program.cs</code>, WPF <code>App.xaml.cs</code>, test fixture, etc.) must call the appropriate top-level <code>Reg()</code> once at startup, before any project XML is deserialized. For the simulation pipeline this is:</p>
<p>An application's entry point (web service <code>Program.cs</code>, the <code>App</code> class of a WPF host, test fixture, etc.) must call the appropriate top-level <code>Reg()</code> once at startup, before any project XML is deserialized. For the simulation pipeline this is:</p>
<pre><code class="lang-csharp">LocalProjectService.Reg();
</code></pre>
<p><code>LocalProjectService.Reg()</code> chains <code>MachiningProject.Reg()</code>, which in turn chains every type the simulation pipeline may deserialize. After this single call returns, <code>XFactory.Default.Generators</code> carries the full deserialization graph.</p>
@@ -112,17 +112,17 @@ the word on its left to the word on its right.</p>
<li><p>Reserved words are the topology keywords: <code>X</code>, <code>Y</code>, <code>Z</code>, <code>A</code>, <code>B</code>, <code>C</code> become motion axes, and <code>w</code>
and <code>t</code> become the end anchors. <strong>Any other word is a plain component</strong> carried along without
motion, such as <code>base</code> or <code>S</code> above. A code must name exactly one <code>[w]</code> and one <code>[t]</code>; the six
axes are optional, and each may be used at most once. That uniqueness check reads the words
without regard to letter case, so it refuses a code naming no table buckle at all — <em>the chain
code must contain the table buckle [w]</em> — and a code letting two components claim one role —
<em>more than one component claims the table buckle: [w], [W]</em>. A lone <code>[W]</code> satisfies it, one
component claiming the role, and builds a component named <code>W</code>; that code is refused further
along, by the exact-match end-anchor check below.</p>
axes are optional, and each may be used at most once. Words merge by exact name, so no two
components can claim one role; what the check refuses is a code naming no table buckle at all —
<em>the chain code must contain the table buckle [w].</em> A code carrying only <code>[W]</code> is refused by
that same check, and the message names the near miss: <em>The code carries [W], which differs only
in letter case; the keyword is matched exactly, so rename it to [w].</em></p>
</li>
<li><p><strong>Spell the keywords exactly</strong> — upper case for the axes, lower case for <code>t</code> and <code>w</code>. Everything
downstream of the code compares them as written: axis discovery, the default collision pairs, and
the end-anchor check a machine file is loaded through. So a mis-cased axis contributes no motion,
and a mis-cased end anchor stops the file loading, with a message naming the near miss.</p>
<li><p><strong>Spell the keywords exactly</strong> — upper case for the axes, lower case for <code>t</code> and <code>w</code>. The code
itself and everything downstream of it compare them as written: role resolution, axis discovery,
the default collision pairs, and the end-anchor check a machine file is loaded through. So a
mis-cased axis is a plain component contributing no motion, and a mis-cased end anchor stops the
code building, with a message naming the near miss.</p>
</li>
<li><p><code>[]</code> is an anonymous component. Each occurrence is a separate one, it cannot be referred to from
another segment, and it cannot hold a geometric shape — shapes are stored by component name.</p>
@@ -246,7 +246,8 @@ its own for the rest — see <a href="#siemens-sinumerik">Siemens SINUMERIK</a>.
</table>
<p>Radius compensation is resolved against the blocks that actually travel in the compensation plane. A
block whose own words command no movement in that plane — a bare <code>G41</code> / <code>G42</code>, a comment, an empty
line, a Z-only plunge — takes no offset of its own and no part in a corner:</p>
line, a plunge along the plane normal (Z under G17) — takes no offset of its own and no part in a
corner:</p>
<ul>
<li><strong>Start-up waits for motion.</strong> A <code>G41</code> or <code>G42</code> block that does not move in the plane does not
start the offset; the first block that travels in the plane is the start-up block and takes the
@@ -254,8 +255,15 @@ perpendicular (type A) offset. The blocks between them carry the previous block'
so nothing moves on their account.</li>
<li><strong>Corners are resolved between travelling blocks.</strong> Wordless blocks sitting between two moves are
passed over, and the corner is the intersection of the two real segments. Running axes are the
in-plane part of each displacement, so a ramping move keeps its full offset and a Z-only plunge
stays on the offset line.</li>
in-plane part of each displacement, so a ramping move keeps its full offset and a plunge along
the plane normal stays on the offset line.</li>
<li><strong>The plane cannot change inside a region.</strong> <code>G17</code> / <code>G18</code> / <code>G19</code> selects the
compensation plane, and it may only be selected while compensation is cancelled. A plane
code that resolves to a different plane while <code>G41</code> / <code>G42</code> is active — including one
sharing its block with the <code>G40</code> that cancels the region — is reported as
<code>RadiusComp--PlaneChangeInRegion</code>, because the corner spanning the change would be built
from one running axis per plane. A control alarms there (Fanuc PS0037 CAN NOT CHANGE
PLANE IN CRC). Re-selecting the plane already in force is not a change and is accepted.</li>
<li><strong>A bare <code>G40</code> closes the region.</strong> The last compensated block ends on its own perpendicular
offset rather than turning toward the first uncompensated move after the cancel. A <code>G40</code> that
carries motion is the last corner's partner instead, and the compensated path meets the cancel