Class McAbcCyclicPathSyntax
- Namespace
- Hi.NcParsers.LogicSyntaxs
- Assembly
- HiMech.dll
Resolve modular rotary axes to the path the controller takes to each value: the
shortest cyclic path relative to the previous node, unless the word's own
directive, the controller's rule for an absolute word or the Heidenhain
M127 gate says otherwise (see below).
Uses IsModularRotary(string) to determine which axes
within MachineCoordinateState need cyclic resolution
— a rotary axis whose travel is under a turn is outside the set and keeps
its values as written.
Falls back to hardcoded A/B/C if no IMachineAxisConfig is available.
Must be placed after ProgramXyzSyntax in NcSyntaxList.
Two stages, mirroring McXyzSyntax:
- Root
MachineCoordinateState— anchored at the previous block's modal rotary state. - CompoundMotion.ItemsKey[*]
— sequential walk through items, anchoring item 0 at the previous
block's modal state and item
i > 0at itemi-1's post-cycle value (per-axis chain). Items without a rotaryMachineCoordinateStateare skipped.
Per-word directional override: a block-root
PositioningOverride entry (stamped by
SiemensAcIcSyntax) valued
PositiveOnly (Siemens ACP()) or
NegativeOnly (ACN()) swaps that
axis's window for this block only: [anchor, anchor+360°) /
(anchor-360°, anchor] instead of the default ±180° — the approach
direction is forced even when it is the longer way around. A target
congruent with the anchor (within an ULP-scale epsilon) keeps the
anchor value verbatim — no move, never a spurious full turn, and no
deg→rad→deg drift. Shortest (DC())
is the default window and needs no special path here. The override
is read from the current block only (it is one-shot, never carried —
deliberately unlike the modal RotaryWrap gate's one-step
previous fallback) and applies to the root MC stage, not to
CompoundMotion items in general (G28/G74/G75 expansions
capture their words in sub-objects the stamping syntax never sees, so
an override can only ever describe a root word) — with one exception:
an item whose value for an axis equals the root's pre-pass value
verbatim carries that same root word (the canned-cycle
pre-positioning item that
WriteCompoundMotion(JsonObject, string, JsonArray, vec3d_t) stamps
from the root MC) and inherits the root's directive for that axis, so
the default window cannot fold the forced swing the root resolved
into the short way. A directional entry keyed by a rotary axis outside
the modular set, or any of the three keyed by an axis that is not
rotary at all, is reported — Coord-McAbc--007 when the table
names the setting that makes the axis a modulo one
(ModuloSetting),
Coord-McAbc--003 otherwise — the promise
cannot be honored there and silence would mis-read the program's
intent (a Shortest entry on a rotary axis
outside the modular set is honored instead, see the absolute-word
paragraph); an entry with no anchor
to resolve against (first rotary value in the stream) is reported as
Coord-McAbc--004 and adopted unwrapped, matching the default
path.
Incremental words: a rotary word programmed as an increment —
per-word (Siemens IC(), klartext IC+270) or a bare
word under modal G91 — is a signed traverse by definition.
McAbcSyntax already wrote anchor + delta into the MC
and listed the axis in the one-shot IncrementalWords
record; this pass reads that record — and only that record — and
keeps the listed axes verbatim instead of folding them into the ±180°
window (a +270° chain dimension must not become a -90° swing). A
listed non-modular axis needs nothing: the literal value is what the
axis would do anyway. A rotary value the block carries without a
word (lookback fill, or an absolute target another writer put there
— G53.1, the Heidenhain PLANE syntax) is not listed
and keeps the default window even under G91.
Absolute word on a modulo axis, by the controller's rule: a plain,
AC() or CAC() word — one the RotaryWords
record lists that carries no directive of its own — asks the
IRotaryAbsoluteWordConfig on the dependency list how the
controller moves to it. Shortest,
and a list without the interface, is the default window below. The
Siemens machine-data table answers
DisplayedDifference for an axis
whose MD30455 bit 2 is 0 — the factory setting, and an absent row:
"When programming AC exclusively or with G90, the traversing direction
depends on the rotary-axis actual position. If the target position is
greater than the actual position, the axis traverses in the positive
direction, otherwise it traverses in the negative direction"
(SINUMERIK Function Manual Extended Functions 09/2011,
6FC5397-1BP40-2BA0, R2 §13.3.2 p.857; example ④ of that section: from
240° AC(0) turns −240°, where the shortest way is +120°). Both
positions are compared as a modulo axis displays them, within
0°–359.999°, so the word takes the Hi.NcParsers.LogicSyntaxs.McAbcCyclicPathSyntax.DisplayedDifference
directive — the same path as the Heidenhain M127 gate below,
never through 0°/360°. Bit 2 configures the plain G90 word only
("with G90, modulo axis positioned as standard using AC / DC"): with it
set a plain word positions as DC and takes the default window
with the table's Positive tie, while an
AC() or CAC() written out
(ExplicitAbsoluteWordPath(string)) keeps
the displayed difference whatever the bit says. A word outside
0°–359.999° (C-2.659, AC(-180), C400; a DC()
or ACP() / ACN() value too, the range is the same, FB2
p.856 and p.858) is converted into the range as the display does (B-5 =
B355, MD30455 bit 0 = 1; LIS1 03/2013 p.449) and moved to by whatever
directive the word has; with bit 0 = 0, the factory setting, the control
would stop with an alarm instead, which the pass says once per axis
(Coord-McAbc--006, a validation warning) rather than refusing the
word — the range the pass assumes is the factory one; a range moved by
MD30340 / MD30330 is not modeled. An axis whose table has no MD30455 row
runs on the factory setting, and the first plain word on it whose
displayed difference is not the way the shortest-path setting would
turn — C350 from 0° turning +350°, or a word a half turn below
the position (C0 from 180°), which the DC tie would turn
positive instead — is reported once per axis (Coord-McAbc--005,
informational): the simulation does not know how the machine is set,
and the message names the row to add. Values the block carries without
a word (a TRAORI or CYCLE800 solve, the lookback fill)
are not programmed words and keep the default window, as under the tie
rule — on an axis outside the modular set moved to the congruent
position nearest the anchor that the stroke limits admit, so a solver's
fold to (−180°, 180°] is never read as a near-full turn and a solved
value is never folded through the stop of a limited-travel axis
(NearestWithinTravel(double, double, double, double?, double?)). A rotary axis whose travel is under a turn — both stroke limits
set less than 360° apart
(IsModularRotary(string)) — or a
SINUMERIK axis whose MD30310 row is not 1 (the control's factory value
is 0, so a Siemens rotary axis is outside the set until the row is
entered under Parameters (Native)) — is outside the modular set: its plain,
AC() and incremental words are the positions as written, with no
wrap and no rule — the one notice there, Coord-McAbc--008, once
per axis, is a plain or AC() word turning a full turn or more as
written (a DC() program ran the axis past a turn), naming the
row that would make the axis a modulo one where the table has one; a
DC() / CDC() word on it
still takes the shortest way, as the manual says ("Even if the rotary
axis is not defined as a modulo axis, the axis can still be positioned
with DC (Direct Control). The response is the same as on a modulo axis",
FB2 R2 §13.3.3 p.863); an ACP() / ACN() word on it is
reported — Coord-McAbc--007, naming the row that makes the axis
a modulo one, where the table has one; Coord-McAbc--003
otherwise — and stands as written (the control alarms there, 16810 /
16820).
Half-turn tie: a target opposite the anchor — a swing within
Hi.NcParsers.LogicSyntaxs.McAbcCyclicPathSyntax.DirectionalCongruenceEps_rad of ±180° — has two equally
short ways round, and the window [anchor−180°, anchor+180°) used to
decide it by whichever last bit the deg→rad conversion left on the
target or on the anchor: an inverse-kinematics answer 3e-14 rad short
of π turned +180°, one at exactly π turned −180°, and an anchor
carrying ±1e-13° flipped the same target — so the half turn the table
sweeps, the collision check over it and the sign the NC rewrite prints
changed with noise. Inside the band the pass writes anchor ± 180°
computed in degrees, with no deg→rad→deg round trip. Which sign: a
DC() word — the Shortest override, the
Hi.NcParsers.LogicSyntaxs.McAbcCyclicPathSyntax.TiePositive directive — turns positive, the word's own
documented rule ("If the path to be traversed is the same in both
directions (180°), the positive direction of rotation receives
preference", SINUMERIK Function Manual Extended Functions 09/2011,
6FC5397-1BP40-2BA0, R2 §13.3.2 p.858 and §13.3.3 p.863, with the
240°→60° example), whatever controller the list names; a CDC()
word takes the same directive by analogy — the manual says only that it
takes the shortest path (the same Extended Functions manual, T1 Indexing
axes, p.943 and p.946) and names no tie. Every other value asks the controller's rule,
read per axis from the IRotaryHalfTurnTieConfig on the
dependency list. A Siemens plain, AC() or CAC() word under
MD30455 bit 2 = 0 never reaches the band: the displayed difference
above decides a half turn too (a word above the displayed position turns
positive, below it negative), which is also what the table's
ProgrammedSide answer says; with bit 2
set such words are DC and the table answers
Positive. The
Heidenhain table answers ProgrammedSide
("the control selects the direction of motion according to the
programmed value" — axis at 180°, L C+0 or L C-360 moves
to 0, L C+360 to 360; TNC 640 Setup manual 1261174-22 p.455,
TNC7 1358773-23 p.549), so a programmed word turns to the side its
value lies on relative to the anchor (the Hi.NcParsers.LogicSyntaxs.McAbcCyclicPathSyntax.TieTowardsWord
directive CollectDirectives(JsonObject, string[], bool[], bool, RotaryHalfTurnTie?[], RotaryAbsoluteWordPath?[], RotaryAbsoluteWordPath?[], IRotaryAbsoluteWordConfig, ISentenceCarrier, NcDiagnosticProgress) gives the axes the
RotaryWords record lists) while an angle no word
programmed — the PLANE solve, the lookback fill — takes the
default, because the M126 sections name no direction of their
own and point at pre-positioning or the tilting solution instead
(892909-2C p.440, 1358773-23 p.575). A list without the interface, or
a value the rule does not reach, takes
Hi.NcParsers.LogicSyntaxs.McAbcCyclicPathSyntax.DefaultHalfTurnTieSwing_deg, the negative half turn the
lower-inclusive window gave an exact ±180° word from an anchor at 0°.
Only that anchor's exact ties are unchanged by the band: from other
anchors the old window's pick followed the rounding of anchor ± π (196
of the 902 exact integer ties between −180° and 360° went positive,
e.g. 4°→184°), and all of them now take the rule. Fanuc
(B-64304EN-2/01 §5.4 p.95 "Rotating in the
direction in which an angular displacement becomes shortest";
parameter No.1008 RABx "In which the distance to the target is
shorter", B-64310EN/02 p.38) describes the shortest path without
resolving the tie, so the negative half turn is this engine's
documented default, not a brand rule.
Rotary wrap off (Heidenhain M127): the modal
RotaryWrap { Shortest: false } section
HeidenhainMFunctionSyntax writes — read from
the current block, else the previous one — governs the rotary words
the block programmed, the axes the one-shot RotaryWords
record lists: each takes the programmed path — from the actual position
as a modulo axis displays it, within 0°–360°, to the programmed value as
it displays it; the word is placed in the anchor's turn, so the machine
coordinate stays continuous and lands on the word exactly (without M126,
and with the machine
parameter shortestDistance 300401 off, the control "does not move
the rotary axis beyond 360°" — TNC7 User's Manual for Programming and
Testing 09/2024, 1358773-23, p.575 — and "moves a rotary axis whose
position display is reduced to less than 360° along a long path", from
350° to 10° by −340° — TNC 640 ISO Programming User's Manual 10/2023,
892909-2C, p.440; the sentence "the control traverses the programmed
value from the actual position to the nominal position" of the older
klartext manual, 892903-29 01/2021 p.460, is gone from its 10/2023
edition 892903-2C; the engine assumes that parameter off — a machine with
it on takes the shortest path even under M127, and the engine does not
read it). For a word and an anchor both within 0°–360° that is the
written value verbatim (L C+350 from 0 turns +350, L C-10
from a coordinate at −10 does not move); a coordinate that has run past a
turn — a literal word below 0°, a solved angle anchored past 360° — does
not change the swing the next word commands (L C+90 from a
coordinate at 360 turns +90 to 450, not −270 to 90). A rotary value the
block carries without a
word — the lookback fill, or the angles the Heidenhain
PLANE … TURN/MOVE syntax solves — is not a programmed word, so
the gate does not apply to it: it keeps the default window and the
half-turn tie rule, anchored at the previous value, exactly as under
M126. The manual says how the two kinematic solutions are chosen
("Based on the current position of the rotary axes, choose the
possible solution with the shortest path", p.451) and nothing about
how a modulo axis then travels to the solved angle under M127;
anchoring at the previous value is this engine's default.
public class McAbcCyclicPathSyntax : ISituNcSyntax, INcSyntax, IMakeXmlSource
- Inheritance
-
McAbcCyclicPathSyntax
- Implements
- Inherited Members
- Extension Methods
Examples
Cases below run with no IMachineAxisConfig on the dep list, so the syntax uses the A/B/C fallback (a configuration warning is emitted but does not affect the JSON), except the last nine, which say which brand table they inject. The syntax is the tail-pass rotary-wrap centraliser — upstream rotary writers (McAbcSyntax, G28, G53.1, ...) store raw degrees and let this pass resolve to the shortest cyclic path.
Current B is within ±180° of the previous B — no wrap needed; the value is rewritten in place but equals the input:
#Previous:{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{ "MachineCoordinateState": { "B": 10 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": 10 } }
Current B is 270° but previous B is 0° — the shortest path is the
other way around, so the value is rewritten as -90° (mathematically
equivalent, geometrically the same orientation, but signalling the
shorter rotation to a downstream motion consumer). 270/0 round-trips
through ToRad→Cycle→ToDeg with no rounding
noise (1.5π → -0.5π → -90 exactly); other angle pairs (e.g.
350° → -10°) emit a trailing ULP-scale drift instead:
#Previous:
{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{ "MachineCoordinateState": { "B": 270 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": -90 } }
First block of the stream (no #Previous:) — no anchor to
resolve against, so the syntax early-returns and the raw value is
preserved verbatim:
#BeforeBuild:
{ "MachineCoordinateState": { "B": 350 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": 350 } }
CompoundMotion.Items walk — two items chain: item 0 cycles
against the previous block's modal B = 0° (270° → -90°), and item 1
cycles against item 0's post-cycle -90° (170° → -190°, since the
shorter path from -90° to 170° wraps backward through -180°). If
item 1 had used the previous-block anchor instead of the chained
anchor, 170° would have stayed at 170° (already in the ±180° window
around 0°), so the test discriminates between chain and no-chain:
{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{
"CompoundMotion": {
"Items": [
{ "MachineCoordinateState": { "B": 270 } },
{ "MachineCoordinateState": { "B": 170 } }
]
}
}
#AfterBuild:
{
"CompoundMotion": {
"Items": [
{ "MachineCoordinateState": { "B": -90 } },
{ "MachineCoordinateState": { "B": -190 } }
]
}
}
ACP (PositiveOnly) forces the positive swing even though the
shortest path from 0° to 270° is -90° (compare the default case
above — same numbers, opposite outcome). The override section stays
on the block (nothing consumes it away):
{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 270 },
"PositioningOverride": { "B": "PositiveOnly" }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 270 },
"PositioningOverride": { "B": "PositiveOnly" }
}
ACN (NegativeOnly) mirror — target 90° from anchor 0° swings
-270° instead of the shortest +90°:
#Previous:
{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 90 },
"PositioningOverride": { "B": "NegativeOnly" }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": -270 },
"PositioningOverride": { "B": "NegativeOnly" }
}
Congruent target under a directional override — the raw anchor sits
at 370° (e.g. after an earlier ACP long-way swing) and the ACN
target 10° is the same physical position: no move, the anchor value
is kept verbatim (the epsilon snap; without it, radian ULP noise
would decide between "no move" and a spurious full -360° turn):
#Previous:
{ "MachineCoordinateState": { "B": 370 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 10 },
"PositioningOverride": { "B": "NegativeOnly" }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 370 },
"PositioningOverride": { "B": "NegativeOnly" }
}
Incremental word (B=IC(270) / klartext L IB+270): the
rotary writer accumulated anchor 0° + 270° = 270° and listed B in
IncrementalWords, and the pass keeps the traverse literal —
the same 270° that the default window above rewrote to -90° stays
+270°. The per-word entry is still on the block (nothing consumes
it) but is not what this pass reads:
#Previous:
{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 270 },
"PositioningOverride": { "B": "Incremental" },
"IncrementalWords": { "B": 270 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 270 },
"PositioningOverride": { "B": "Incremental" },
"IncrementalWords": { "B": 270 }
}
A CompoundMotion item carrying the root word verbatim — the
canned-cycle pre-positioning item that
WriteCompoundMotion(JsonObject, string, JsonArray, vec3d_t) stamps from
the root MC of a B=ACP(270) block — inherits the root's
PositiveOnly directive: both stay +270 instead of the item
folding to -90 as the plain items-walk case above would (same
numbers, opposite outcome). The second item is not a copy (170 ≠ 270)
and resolves under the default window against the chained anchor
270°, where 170 is already the short way:
#Previous:
{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 270 },
"PositioningOverride": { "B": "PositiveOnly" },
"CompoundMotion": {
"Items": [
{ "MachineCoordinateState": { "B": 270 } },
{ "MachineCoordinateState": { "B": 170 } }
]
}
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 270 },
"PositioningOverride": { "B": "PositiveOnly" },
"CompoundMotion": {
"Items": [
{ "MachineCoordinateState": { "B": 270 } },
{ "MachineCoordinateState": { "B": 170 } }
]
}
}
A bare rotary word under modal G91 (Fanuc G91 … B270.) has no
per-word entry at all — the rotary writer accumulated 0° + 270° and
listed B in IncrementalWords, and that listing alone keeps the
+270° traverse literal. C is on the block without a word (the value
an absolute writer such as G53.1 would leave), so it is not
listed and still resolves under the default window: 270° from anchor
0° folds to −90°. Neither Positioning nor RotaryWords
is consulted here (RotaryWords is read only under the
RotaryWrap gate or on a controller whose tie rule is the
programmed word's side), so the case carries neither:
#Previous:
{ "MachineCoordinateState": { "B": 0, "C": 0 } }
#BeforeBuild:
{
"IncrementalWords": { "B": 270 },
"MachineCoordinateState": { "B": 270, "C": 270 }
}
#AfterBuild:
{
"IncrementalWords": { "B": 270 },
"MachineCoordinateState": { "B": 270, "C": -90 }
}
Half-turn tie, noisy target: the inverse-kinematics answer π − 3.1e-14 rad (the noise the rotation-matrix survey measured on the stroke solves) converts to 179.9999999999982°, which the plain window from anchor 0° kept as a +180° swing while an answer at exactly π turned −180°. Inside the tie band the pass writes anchor − 180 = −180 for both, so the last bit no longer picks the direction:
#Previous:{ "MachineCoordinateState": { "B": 0 } }
#BeforeBuild:
{ "MachineCoordinateState": { "B": 179.9999999999982 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": -180 } }
Half-turn tie, noisy anchor: the previous B carries +1e-13°, the
previous C −1e-13° (the last-bit residue a long rotary chain leaves),
and both targets are 180°. The plain window turned B +180° and
C −180°; the band writes anchor − 180 for both, in degrees, so the
residue rides along instead of choosing the side:
#Previous:
{ "MachineCoordinateState": { "B": 1E-13, "C": -1E-13 } }
#BeforeBuild:
{ "MachineCoordinateState": { "B": 180, "C": 180 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": -179.9999999999999, "C": -180.0000000000001 } }
An exact half turn from anchor 0 resolves as the lower-inclusive window
always resolved it there: +180 and −180 both become −180 (from other
anchors the old window's pick followed rounding — 4°→184° went +180 —
and now takes the same rule):
#Previous:
{ "MachineCoordinateState": { "B": 0, "C": 0 } }
#BeforeBuild:
{ "MachineCoordinateState": { "B": 180, "C": -180 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": -180, "C": -180 } }
Rotary wrap off (Heidenhain M127; the modal RotaryWrap
section sits on the previous block): the programmed word B — listed
in RotaryWords — passes through literally, 350 from anchor 0
staying the long +350° way, while the C value the block carries
without a word (a PLANE-solved −180 from anchor 170) is not a
programmed word and takes the default window: the nearest
representation is +180, a +10° swing, not a literal −350° turn:
#Previous:
{
"MachineCoordinateState": { "B": 0, "C": 170 },
"RotaryWrap": { "Shortest": false }
}
#BeforeBuild:
{
"MachineCoordinateState": { "B": 350, "C": -180 },
"RotaryWords": { "B": 350 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 350, "C": 180 },
"RotaryWords": { "B": 350 }
}
A DC() word on a half-turn tie turns positive whatever the
controller: B carries the Shortest override (a DC(180)
word) and turns +180 from anchor 0, while the plain C word — the same
180 from 0, no override, and no controller table on the dependency
list to state a rule — takes the engine default and turns −180:
#Previous:
{ "MachineCoordinateState": { "B": 0, "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 180, "C": 180 },
"PositioningOverride": { "B": "Shortest" },
"RotaryWords": { "B": 180, "C": 180 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 180, "C": -180 },
"PositioningOverride": { "B": "Shortest" },
"RotaryWords": { "B": 180, "C": 180 }
}
A plain word on a SINUMERIK modulo axis takes the displayed difference,
the control's rule for a plain or AC() word with MD30455 bit 2 =
0 (this case injects a SiemensMachineDataTable declaring B and C
rotary and modulo axes, MD30310 = 1 — the control's factory value is 0,
see the last case — with no MD30455 row): C270 from anchor 0 turns +270 — the
shortest window turned the same numbers to −90 in the second case of
this list — while the DC() word on B, the same 270 from 0, still
takes the shortest way, −90:
{ "MachineCoordinateState": { "B": 0, "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 270, "C": 270 },
"PositioningOverride": { "B": "Shortest" },
"RotaryWords": { "B": 270, "C": 270 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": -90, "C": 270 },
"PositioningOverride": { "B": "Shortest" },
"RotaryWords": { "B": 270, "C": 270 }
}
The manual's example ④ (FB2 R2 §13.3.2 p.857): from 240°, AC(0)
"traverses to position 0° in the negative direction of rotation" — a
−240° turn, where the shortest way would be +120° to 360 (the same
Siemens table, C a modulo axis):
#Previous:
{ "MachineCoordinateState": { "C": 240 } }
#BeforeBuild:
{
"MachineCoordinateState": { "C": 0 },
"PositioningOverride": { "C": "Absolute" },
"RotaryWords": { "C": 0 }
}
#AfterBuild:
{
"MachineCoordinateState": { "C": 0 },
"PositioningOverride": { "C": "Absolute" },
"RotaryWords": { "C": 0 }
}
With MD30455 bit 2 set (the Siemens table carries MD30310 = 1 and the
MD30455 rows B = 4 and C = 4) a plain word positions as
DC: the same C270
from 0 takes the shortest way, −90 — while B's AC(270), written
out (the Absolute override), keeps the AC rule the bit does not
touch and turns +270:
#Previous:
{ "MachineCoordinateState": { "B": 0, "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 270, "C": 270 },
"PositioningOverride": { "B": "Absolute" },
"RotaryWords": { "B": 270, "C": 270 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 270, "C": -90 },
"PositioningOverride": { "B": "Absolute" },
"RotaryWords": { "B": 270, "C": 270 }
}
A word outside 0°–359.999° on the same modulo axis (the Siemens table
with MD30310 = 1 and no MD30455 row): C−90 from 0 is converted to its displayed 270 (B-5 =
B355) and compared from there — 270 above 0 turns +270, where the
shortest window turned −90; a control with the factory MD30455 bit 0 = 0
would have stopped with an alarm, which the pass reports as
Coord-McAbc--006 (not part of the JSON):
#Previous:
{ "MachineCoordinateState": { "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "C": -90 },
"RotaryWords": { "C": -90 }
}
#AfterBuild:
{
"MachineCoordinateState": { "C": 270 },
"RotaryWords": { "C": -90 }
}
A rotary axis whose travel is under a turn is not modular (this case
injects a FanucParameterTable declaring A and C rotary with A's
stroke limits at −120° and +120°): A100 from −100 stays the position as
written, +200° through the travel, where the modular window would have
turned −160° to −260 through the stop; C, with no limits, still wraps
270 from 0 to −90:
#Previous:
{ "MachineCoordinateState": { "A": -100, "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "A": 100, "C": 270 },
"RotaryWords": { "A": 100, "C": 270 }
}
#AfterBuild:
{
"MachineCoordinateState": { "A": 100, "C": -90 },
"RotaryWords": { "A": 100, "C": 270 }
}
A SINUMERIK axis with MD30310 = 0 (the Siemens table carries the row
C = 0) is not a modulo axis whatever its travel: C−90 from 0 is
the position as written — no conversion, no rule, no message — where the
modulo axis of the out-of-range case above turned +270:
#Previous:
{ "MachineCoordinateState": { "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "C": -90 },
"RotaryWords": { "C": -90 }
}
#AfterBuild:
{
"MachineCoordinateState": { "C": -90 },
"RotaryWords": { "C": -90 }
}
On such an axis a DC() word still takes the shortest way — "Even
if the rotary axis is not defined as a modulo axis, the axis can still be
positioned with DC (Direct Control). The response is the same as on a
modulo axis" (FB2 R2 §13.3.3 p.863) — with no boundary warning (the
Siemens table carries MD30310 = 0 for B and C): B's DC(270) from 0
turns −90 while C's plain −90 stands as written:
#Previous:
{ "MachineCoordinateState": { "B": 0, "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 270, "C": -90 },
"PositioningOverride": { "B": "Shortest" },
"RotaryWords": { "B": 270, "C": -90 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": -90, "C": -90 },
"PositioningOverride": { "B": "Shortest" },
"RotaryWords": { "B": 270, "C": -90 }
}
A SINUMERIK axis without an MD30310 row runs on the control's factory
value, 0, and is not a modulo axis either (the Siemens table declares B
and C rotary and carries no row — the preset's state until the row is
entered under Parameters (Native)): B350 from an anchor at 370 is −20
as written, where a modulo axis would compare the displayed 10 with 350
and turn +340 to 710; C−90 from 0 is −90, where the modulo axis of the
out-of-range case above turned +270 — no conversion, no rule, no
message:
#Previous:
{ "MachineCoordinateState": { "B": 370, "C": 0 } }
#BeforeBuild:
{
"MachineCoordinateState": { "B": 350, "C": -90 },
"RotaryWords": { "B": 350, "C": -90 }
}
#AfterBuild:
{
"MachineCoordinateState": { "B": 350, "C": -90 },
"RotaryWords": { "B": 350, "C": -90 }
}
A value such an axis carries without a word — a CYCLE800 or
TRAORI solve, the lookback fill — is not a programmed word and is
not taken as written: it goes to the congruent position nearest the
anchor that the stroke limits admit (the same Siemens table without an
MD30310 row, B's limits set to −120° and +120°, C's not set, and no
RotaryWords): C−170 from 170 is the turn of +20 to 190, where a
solver's fold to (−180°, 180°] read as written would have turned −340;
B100 from −100 stays 100 — the window's −260 lies outside the travel,
and 100 is the admitted position nearest the anchor:
#Previous:
{ "MachineCoordinateState": { "B": -100, "C": 170 } }
#BeforeBuild:
{ "MachineCoordinateState": { "B": 100, "C": -170 } }
#AfterBuild:
{ "MachineCoordinateState": { "B": 100, "C": 190 } }
Constructors
McAbcCyclicPathSyntax()
Initializes a new instance with default settings.
public McAbcCyclicPathSyntax()
McAbcCyclicPathSyntax(XElement)
Initializes a new instance by deserializing from the given XML element.
public McAbcCyclicPathSyntax(XElement src)
Parameters
srcXElementSource XML element.
Properties
Name
Syntax kind name (typically the concrete type name).
public string Name { get; }
Property Value
XName
XML element name used to register this syntax with XFactory.
public static string XName { get; }
Property Value
Methods
Build(LazyLinkedListNode<SyntaxPiece>, List<INcDependency>, NcDiagnosticProgress)
Build syntax arrangement into the
syntaxPieceNode in-place.
public void Build(LazyLinkedListNode<SyntaxPiece> syntaxPieceNode, List<INcDependency> ncDependencyList, NcDiagnosticProgress ncDiagnosticProgress)
Parameters
syntaxPieceNodeLazyLinkedListNode<SyntaxPiece>ncDependencyListList<INcDependency>ncDiagnosticProgressNcDiagnosticProgress
MakeXmlSource(string, string, bool)
Creates an XML representation of the object. This method may also generate additional resources such as related files.
public XElement MakeXmlSource(string baseDirectory, string relFile, bool exhibitionOnly)
Parameters
baseDirectorystringThe base directory for resolving relative paths
relFilestringThe relative file path for the XML source
exhibitionOnlyboolif true, the extended file creation is suppressed.
Returns
- XElement
An XML element representing the object's state
Remarks
For the demand of easy moving source folder (especially project folder) without configuration file path corruption, the relative file path is applied.
The baseDirectory is typically the folder at the nearest configuration file folder.
Since the folder can be moving with the configuration file.
Reg(XFactory)
Registers this type's deserializer with the given XFactory
(or Default when factory is
null). Idempotent.
public static void Reg(XFactory factory = null)
Parameters
factoryXFactory