Table of Contents

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:

  1. Root MachineCoordinateState — anchored at the previous block's modal rotary state.
  2. CompoundMotion.ItemsKey[*] — sequential walk through items, anchoring item 0 at the previous block's modal state and item i > 0 at item i-1's post-cycle value (per-axis chain). Items without a rotary MachineCoordinateState are skipped.
The items pass enables rotary motion (e.g. G28 ABC intermediate / home stages) to surface as motion IAct segments rather than a single root-MC stamp.

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:

#Previous:
{ "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):

#Previous:
{ "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:

#Previous:
{ "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

src XElement

Source XML element.

Properties

Name

Syntax kind name (typically the concrete type name).

public string Name { get; }

Property Value

string

XName

XML element name used to register this syntax with XFactory.

public static string XName { get; }

Property Value

string

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

syntaxPieceNode LazyLinkedListNode<SyntaxPiece>
ncDependencyList List<INcDependency>
ncDiagnosticProgress NcDiagnosticProgress

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

baseDirectory string

The base directory for resolving relative paths

relFile string

The relative file path for the XML source

exhibitionOnly bool

if 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

factory XFactory