Skip to content

Commit ccd8e71

Browse files
feat(ax-port): port Trajectory/TrajectoryPoint to simatic-ax
- Add convert_motion_namespaces() transform: SM3_Robotics.*/SM3_Basic.MC_BUFFER_MODE -> CoordMotion.*/Motion.MC_BUFFER_MODE for auto-converted .st files - Add Trajectory to MOTION_MANUAL_OVERRIDE_STEMS: AX SIMATIC does not allow non-literal array bounds in TYPE definitions, so ARRAY[0..63] is hardcoded in the manual src-manual override (= TRAJECTORY_POINT_LIST_MAX_SIZE - 1) - Remove MotionConf manual override: top-level VAR_GLOBAL is also invalid in AX SIMATIC outside CONFIGURATION/NAMESPACE; Trajectory override is the correct fix for the array-bound visibility problem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 875e07a commit ccd8e71

3 files changed

Lines changed: 75 additions & 0 deletions

File tree

scripts/ax_port/policy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ def includes(self, relative_path: Path) -> bool:
7474
Path("framework/src/main/org/moqui/motion/AxisGroupCompat"),
7575
Path("framework/src/main/org/moqui/motion/AxisInternalStatus"),
7676
Path("framework/src/main/org/moqui/motion/AxisGroupInternalStatus"),
77+
# AX SIMATIC does not allow non-literal array bounds in TYPE definitions.
78+
# The manual version hardcodes the literal (keep in sync with TRAJECTORY_POINT_LIST_MAX_SIZE).
79+
Path("framework/src/main/org/moqui/motion/Trajectory"),
7780
})
7881

7982
# Backward-compatible alias: equals the Motion set (superset of all overrides).

scripts/ax_port/transforms.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ class TransformResult:
1414

1515
REFERENCE_TO_RE = re.compile(r"\bREFERENCE\s+TO\b")
1616
MC_DIRECTION_RE = re.compile(r"\bMC_DIRECTION\b")
17+
18+
# CODESYS motion namespace → AX compat namespace substitutions.
19+
# Applied mechanically to all files; matches are scoped to motion files by the prefixes used.
20+
MOTION_NAMESPACE_REPLACEMENTS: list[tuple[str, str]] = [
21+
("SM3_Robotics.SMC_COORD_SYSTEM", "CoordMotion.SMC_COORD_SYSTEM"),
22+
("SM3_Robotics.MC_TRANSITION_MODE", "CoordMotion.MC_TRANSITION_MODE"),
23+
("SM3_Robotics.SMC_POS_REF", "CoordMotion.SMC_POS_REF"),
24+
("SM3_Robotics.SMC_CIRC_MODE", "CoordMotion.SMC_CIRC_MODE"),
25+
("SM3_Robotics.MC_CIRC_PATHCHOICE", "CoordMotion.MC_CIRC_PATHCHOICE"),
26+
("SM3_Robotics.AXIS_GROUP_REF_SM3", "TO_Kinematics"),
27+
("SM3_Basic.MC_BUFFER_MODE", "Motion.MC_BUFFER_MODE"),
28+
]
1729
CONTROL_END_RE = re.compile(r"\b(END_IF|END_CASE|END_FOR|END_WHILE|END_REPEAT)\b(?!\s*;)")
1830
REF_ASSIGN_RE = re.compile(
1931
r"(?P<indent>^[ \t]*)(?P<lhs>[A-Za-z_][A-Za-z0-9_\.\[\]]*)\s+REF=\s+(?P<rhs>[^;\r\n]+);",
@@ -146,6 +158,15 @@ def convert_enum_access(text: str, enum_type_names: Iterable[str]) -> tuple[str,
146158
return text, total
147159

148160

161+
def convert_motion_namespaces(text: str) -> tuple[str, int]:
162+
count = 0
163+
for source, target in MOTION_NAMESPACE_REPLACEMENTS:
164+
if source in text:
165+
text = text.replace(source, target)
166+
count += 1
167+
return text, count
168+
169+
149170
def ensure_final_newline(text: str) -> str:
150171
return text if not text or text.endswith("\n") else text + "\n"
151172

@@ -168,5 +189,6 @@ def convert_text(relative: Path, source_text: str, enum_type_names: Iterable[str
168189
_apply(result, "convert_enum_access", lambda t: convert_enum_access(t, enum_type_names))
169190
_apply(result, "reference_to_to_ref_to", lambda t: REFERENCE_TO_RE.subn("REF_TO", t))
170191
_apply(result, "normalize_mc_direction", lambda t: MC_DIRECTION_RE.subn("MC_Direction", t))
192+
_apply(result, "convert_motion_namespaces", convert_motion_namespaces)
171193
result.text = ensure_final_newline(result.text)
172194
return result
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
(*
2+
* A trajectory as a fixed-size array of waypoints with shared motion dynamics.
3+
*
4+
* Intended use:
5+
* 1. moqui-device-gateway populates this struct from the TrajectoryPointComponent view
6+
* and writes it to the PLC via MQTT/OPC UA.
7+
* 2. The PLC iterates points[0..pointCount-1] in sequenceNum order, feeding each
8+
* TrajectoryPoint.pos to AxisGroup.endPoint and dispatching the appropriate
9+
* motion command (MoveLinearAbsolute, MoveDirectAbsolute, etc.).
10+
*
11+
* Blending rules applied per transition:
12+
* - TrajectoryPoint.isBreakPoint = TRUE -> aborting + TMNone (exact stop, always wins)
13+
* - TrajectoryPoint.blendingMode > 0 -> overrides this.blendingMode for that point
14+
* - Otherwise -> bufferMode + transitionMode + blendingMode
15+
*
16+
* Source entity: moqui.math.Trajectory (approximatedFunctionId = trajectoryId)
17+
* moqui blending enum: moqui.math.ParametricPath.compositionMethodEnumId (PpcmPlc* values)
18+
*)
19+
TYPE Trajectory : STRUCT
20+
(* moqui.math.Trajectory.approximatedFunctionId *)
21+
trajectoryId : STRING;
22+
(* Number of valid waypoints; execute points[0..pointCount-1] in order *)
23+
pointCount : DINT;
24+
(* Coordinate system applied to all points (MCS for Cartesian, ACS for joint-space) *)
25+
coordSystem : CoordMotion.SMC_COORD_SYSTEM;
26+
(* Shared dynamics applied to all CP moves between waypoints *)
27+
velocity : LREAL; (* [u/s] *)
28+
acceleration : LREAL; (* [u/s²] *)
29+
deceleration : LREAL; (* [u/s²] *)
30+
jerk : LREAL; (* [u/s³] *)
31+
(*
32+
* Default PLCopen buffer mode for transitions between non-break points.
33+
* moqui: ParametricPath.compositionMethodEnumId = PpcmPlcAborting / PpcmPlcBlendingLow / ...
34+
*)
35+
bufferMode : Motion.MC_BUFFER_MODE;
36+
(*
37+
* Default PLCopen transition mode (geometric blend shape at the corner).
38+
* moqui: ParametricPath.compositionMethodEnumId = PpcmPlcTMNone / PpcmPlcTMCornerDist / ...
39+
*)
40+
transitionMode : CoordMotion.MC_TRANSITION_MODE;
41+
(*
42+
* Default blend parameter for TMCornerDistance [mm] or TMConstantVelocity [u/s].
43+
* moqui: ParametricPathPoint.tolerance (per-point) or trajectory-level default here.
44+
*)
45+
blendingMode : LREAL;
46+
(* Waypoints in execution order; valid range: [0..pointCount-1] *)
47+
(* Array bound = TRAJECTORY_POINT_LIST_MAX_SIZE - 1 = 63. AX SIMATIC does not allow
48+
non-literal array bounds in TYPE definitions; keep in sync with configuration.st. *)
49+
points : ARRAY[0..63] OF TrajectoryPoint;
50+
END_STRUCT; END_TYPE

0 commit comments

Comments
 (0)