release: strucpp 0.6.0#196
Merged
Merged
Conversation
Bundle a native PLCopen SoftMotion (Light) library alongside iec-standard-fb and oscat-basic, targeting CiA 402 servo drives. Vendors the drive-side OpenSML blocks (GPL-3.0, https://github.com/feecat/OpenSML) — Power/Home/ProfilePosition/ ProfileVelocity/Stop over an OpenSML_Axis CiA 402 reference struct — and exposes canonical PLCopen MC_* wrappers (MC_Power, MC_Home, MC_MoveAbsolute/Relative/Velocity, MC_Stop/Halt, MC_Reset, MC_ReadActualPosition/Velocity, MC_ReadStatus). Position/velocity in raw drive units for now. Built via rebuild-libs.mjs (rebuildLibraryFromDisk, depends on iec-standard-fb for TON) and mirrored to the vscode extension. Users reference the blocks by bare name via bundled-lib auto-discovery; tree-shaking emits only used chunks. Validated ST -> C++ -> g++ end to end; new library test plus the full library suite (446) pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Complete the plcopen-softmotion library with controller-side interpolation. Adds the S7RTT single-axis 7-segment S-curve online trajectory generator (Apache-2.0, https://github.com/feecat/S7RTT), converted from PLCopen XML to ST (FB_S7RTT_OTG, FB_S7RTT_Plan, the FC_S7RTT_* helpers, and the ST_MotionState/ST_MotionLimits structs), plus the OpenSML Cyclic-Synchronous-Position/Velocity blocks and the OpenSML_AxisController over an OpenSML_Control aggregate. Fixes two latent OpenSML issues surfaced during porting: - OpenSML_SyncPosition referenced xSimulation without declaring it - OpenSML_AxisController used dangling `=>`(unconnected) outputs Library now ships 21 FBs + 3 functions + 4 types. Validated ST -> C++ -> g++ end to end, plus a runtime numeric test: a point-to-point OTG move converges to target while respecting the velocity limit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design for CODESYS-like ESI-XML import that binds a CiA 402 servo to an OpenSML_Axis with no manual %I/%Q mapping. Documents Path A (glue-based, ships on today's compiler via generated located globals + a per-axis link POU in openplc-editor; runtime unchanged) and Path B (compiler struct-binding follow-up that removes the glue), plus units/scaling and DC-sync considerations and a recommended sequencing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite the EtherCAT plan around porting CODESYS SoftMotion projects with minimal source changes. Key finding: OpenSML == CODESYS SoftMotion Light (SML) architecturally, and S7RTT covers full-SM3 controller-side csp — so the motion engine already exists; the remaining work is a CODESYS-shaped compatibility layer (AXIS_REF_SML/SM3 types, exact MC_* signatures, LREAL units + scaling, enums, a per-axis drive-bridge FB modeled on SM_Drive_ETC_GenericDSP402, PLCopen state machine). Agreed direction: SML tier first (near-direct over OpenSML, matches the CODESYS SML_CompatibilityCheck_DS402 example), then full SM3 via S7RTT; full LREAL units + scaling from the start; EtherCAT device name = axis name with compile-time global + bridge generation. CODESYS .project import documented as a separate later workstream (autonomy-edge). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Research verdict: CODESYS SoftMotion application code only passes the axis to MC_* blocks and reads/writes fields — axis methods/properties are all driver-internal — so AXIS_REF_SM3 can be a plain STRUCT passed by VAR_IN_OUT reference. Adopt unsuffixed CODESYS names (AXIS_REF_SM3, MC_Power, …) for lowest porting friction; back them drive-side first, upgrade to S7RTT csp later. Add the ranked common block set, the practical axis-field subset, and the behavioral-fidelity gotchas the bridge must honor (call-every-cycle, Execute:=FALSE doesn't stop, Aborting buffer default, command vs RealState, MC_Power drives the PLCopen state machine, unit scaling). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…x FB inout copy-back Phase 1 of the SoftMotion integration: a CODESYS SoftMotion (SM3) compatibility layer over the OpenSML/S7RTT engine so CODESYS single-axis projects port unmodified. Library (libs/sources/plcopen-softmotion/): - sm3_enums.st: SMC_AXIS_STATE, SMC_CONTROLLER_MODE, MC_DIRECTION, MC_BUFFER_MODE, SMC_HOMING_MODE, SMC_ERROR (single-axis subset) - sm3_axis_ref.st: AXIS_REF_SM3 struct (CODESYS field names, LREAL technical units, increments<->units scaling, embedded raw CiA402 Drive) - sm3_drive.st: SM_Drive_GenericDS402 — generic CiA402 drive bridge (analogue of CODESYS SM_Drive_ETC_GenericDSP402): PDO<->axis, scaling, PLCopen state derivation - sm3_mc_blocks.st: CODESYS-signature MC_Power/MoveAbsolute/MoveRelative/ MoveVelocity/Halt/Stop/Reset/Home/ReadStatus/ReadActualPosition/ ReadActualVelocity/ReadAxisError (LREAL units) over the OpenSML engine - removes the earlier raw-unit mc_wrappers.st (superseded) Compiler fix (src/backend/codegen.ts): - FB VAR_IN_OUT params were copied IN at the call site but never copied BACK, silently discarding a callee's mutations — so no MC_* block could modify its axis. Now emits the matching copy-out after the call (true inout semantics), including nested struct-field inouts. A later strucpp branch replaces this copy-in/out with by-reference pointers. Tests: SM3 manifest + compile + runtime drive-simulation (enable sequence + scaled absolute move reaches 250000 inc / 250.0 u) + OTG convergence; codegen copy-back regressions (scalar/struct/nested). Full suite green (1979 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ut copy-back) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bodies
Composite VAR_GLOBAL/VAR_EXTERNAL (structs, arrays, FB instances) were
declarable but any in-body access threw ("not yet supported"). This
blocked the CODESYS SoftMotion pattern where an AXIS_REF_SM3 global is
shared between its drive bridge and the MC_* blocks.
Now a composite external is accessed directly through its canonical
value behind the GlobalVar pointer (`g->value`, `g->value.field`,
`g->value.field.N` for bit access, and as an FB VAR_IN_OUT arg with the
copy-in/copy-out pair). The per-global mutex is intentionally bypassed
for composites: they are shared within a single (bus-cycle) task, e.g. a
SoftMotion axis driven by its bridge and MC_* blocks in one scan.
Cross-task composite sharing (a lock spanning a whole field/call access)
remains future work. Scalar shared globals keep the mutex read()/write().
Updates the two compile tests that asserted the old fail-loud behaviour
to assert the generated `->value` access. Full suite green (1979).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ith_lock)
Replace the direct ->value shortcut for composite shared globals with
proper mutex-locked access through GlobalVar::with_lock — the same
per-global-mutex mechanism scalar globals already use, now generically
for any IEC 61131-3 type (struct / array / array-of-struct / FB) held in
a VAR_GLOBAL and reached via VAR_EXTERNAL. No copy-in/out or sync
machinery: codegen simply takes the global's own mutex on access.
- Reads: G->with_lock([&](auto* p){ return p-><path>; }) — a field/
element read copies only that sub-value; self-contained (acquire →
return → release), so several composite-global reads in one expression
are sequential, never nested.
- Writes: the RHS is hoisted to a temp first, then
G->with_lock([&](auto* p){ p-><path> = tmp; }); bit writes do the
read-modify-write inside the one lock (atomic). Invariant: at most one
global lock held at a time — no deadlock.
- FB VAR_IN_OUT of a composite global: locked whole-value read (copy-in)
+ locked write (copy-out); the by-value inout copy itself is the
separate VAR_IN_OUT mechanism.
The lock lives entirely inside GlobalVar::with_lock (#ifdef
STRUCPP_THREADED), so non-threaded/baremetal builds compile it out — zero
overhead, no <mutex>. GlobalVar<V> is unchanged (already generic).
Factored the access-chain rendering into renderAccessTail so the same
logic renders onto the lock lambda's pointer. strucpp only; no runtime
change. Tests: struct/array/bit/FB-inout access + a two-task shared
composite global compiling under -DSTRUCPP_THREADED and without. Full
suite green (1984).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…through write() Capturing an FB output via `=>` into a VAR_EXTERNAL scalar emitted `g->read() = value;`. Shared-global scalars are pointers whose value is read via `->read()` (an rvalue) and written via `->write()`, so assigning to `->read()` failed to compile with "lvalue required as left operand of assignment". emitCaptureToLvalue now mirrors the scalar-external branch of generateAssignmentStatement: a plain (non-field, non-deref) VAR_EXTERNAL target routes the write through `name->write(value)`. Composite externals (already handled via with_lock) and locals are unchanged. Surfaced by the generated SoftMotion drive bridge, whose command outputs (wControlWord, diTargetVelocity, iTargetTorque, …) are captured with `=>` into located scalar globals bound to the CiA 402 PDO addresses. Adds a g++-compiled regression test asserting the bridge pattern emits `->write(` and never `->read() =`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(IEC 61131-3) An FB's VAR_EXTERNAL was silently code-generated as a plain member — a private copy the FB mutated in isolation, never touching the real global — and was never validated against the project's globals. IEC 61131-3 permits a function block (unlike a function) to access configuration globals via VAR_EXTERNAL. Storage: configuration VAR_GLOBALs are now emitted as file-scope `inline GlobalVar<V>` singletons (one per name) instead of configuration-class members, so the single canonical storage + mutex is reachable from every POU at any nesting depth. Programs are unchanged — the configuration still injects the pointer via the constructor, now handing over the file-scope address (same-name resolution keeps instantiation/located/retain code identical). Function blocks: VAR_EXTERNAL is emitted as a `GlobalVar<V>*` bound in the FB's own constructor to `&<ns>::<name>` (self-bind — file-scope visibility means no pointer threading through containers), and body access is rewritten through the pointer (->read()/->write()/with_lock), exactly like a program. Validation now checks FB externals too and anchors the "no matching VAR_GLOBAL" diagnostic at the declaration (file+line) instead of emitting it file-less; functions remain excluded per the standard. Adds a g++ compile+run regression proving an FB mutates the SHARED global (threaded + non-threaded) and a located FB-external diagnostic test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fig prefix)
Config VAR_GLOBALs moved to file-scope singletons, so the debug pointer table
must address them directly (`&NAME.value`) rather than through the configuration
instance (`&g_config.NAME.value`), which no longer has those members. Also
dedupes by name to match the file-scope emission. Fixes a g++ error in
generated_debug.cpp ("class Configuration_X has no member named …").
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: bundle PLCopen SoftMotion library (OpenSML + S7RTT)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 0.6.0 — SoftMotion SM3 layer, composite/array shared globals via per-global mutex, and IEC 61131-3 function-block VAR_EXTERNAL shared-global access.
Merges development into main for the v0.6.0 tag/release.
🤖 Generated with Claude Code