Skip to content

Commit 1a8071a

Browse files
feat(plc): generate isolated CODESYS application bundles
1 parent 9857a25 commit 1a8071a

20 files changed

Lines changed: 1089 additions & 384 deletions

agent-skills/skills/moqui-plc-designer/SKILL.md

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
---
22
name: moqui-plc-designer
33
description: Use when generating or refining PLC code from Moqui automation data. This skill reads devices, parameters, device requests, status flows, and related metadata, then fills reusable templates for IOFacade, DeviceFacade, DeviceManager, DeviceDiagnostics, MainStatus, Main, and MainRuleEngine.
4-
compatibility: Requires Python 3.14+
5-
license: ../../LICENSE.md
6-
metadata:
7-
author: moqui-induatrial
8-
version: "1.0"
94
---
105

116
# Moqui PLC Designer
@@ -46,20 +41,30 @@ Use this skill when the task is to define or generate a data-driven automation m
4641

4742
## Output Layout
4843

49-
Generated PLC files should be written into an `output/<component-name>/` tree that mirrors the `mantle-hvac` structure in [moqui/moqui-plc](https://github.com/moqui/moqui-plc).
44+
Generate one isolated bundle for each top-level CODESYS Application. Each bundle
45+
contains a dedicated framework copy and one runtime component that mirrors
46+
`mantle-hvac`.
5047

5148
In a saved parent session, prefer:
5249

53-
- `output/sessions/<session-id>/generated-plc/<component-name>/...`
50+
- `output/sessions/<session-id>/generated-plc/codesys-applications/<application-id>/...`
5451

55-
Preferred layout:
52+
Application layout:
5653

57-
- `output/<component-name>/data/`
58-
- `output/<component-name>/src/main/<namespace>/<component-name>/`
54+
- `framework/`
55+
- `runtime/component/<component-name>/data/`
56+
- `runtime/component/<component-name>/src/main/<namespace>/<component-name>/`
57+
- `application-manifest.json`
58+
- `plc-traceability.md`
59+
60+
Runtime component layout:
61+
62+
- `data/`
63+
- `src/main/<namespace>/<component-name>/`
5964
- `MainStatus.dut`
6065
- `Main.pou`
6166
- `MainRuleEngine.pou`
62-
- `output/<component-name>/src/main/org/moqui/device/`
67+
- `src/main/org/moqui/device/`
6368
- `IOFacade.dut`
6469
- `DeviceFacade.dut`
6570
- `DeviceManager.pou`
@@ -88,22 +93,26 @@ CODESYS device-tree objects remain external to the Moqui seed model:
8893
- `InputSignalUpdate` and `OutputSignalUpdate` stay manual
8994
- `DeviceManager` and `DeviceDiagnostics` are auto-generable only when every listed device is blocking for the machine
9095
- complex redundancy, backup, standby, or non-blocking `DeviceGroup` roles are out of scope
91-
- `Main.pou` and `MainRuleEngine.pou` intentionally remain in standby until real project test cases are available to validate the final generation rules
96+
- `MainRuleEngine.pou` computes boolean transition requests; `Main.pou` alone consumes those requests and applies same-flow state changes
97+
- every top-level controlled system is generated as a separate CODESYS Application with its own framework copy
98+
- subsystem controllers execute sequentially by unique ascending `call_sequence`; `DeviceManager` executes once afterwards
99+
- cross-flow transitions require reviewed `request_assignments` and `apply_assignments`; generation stops if either side is missing
92100
- the repository should currently be treated as a semilavorato/base framework that each development team may further specialize
93101

94102
## Workflow
95103

96104
1. Read the seed XML and related Moqui model files.
97105
2. Decompose the machine into subsystems and levels.
98-
3. Identify the main orchestration FSM and the atomic devices at the last level.
106+
3. Identify the flat orchestration FSMs owned by systems/subsystems; introduce nesting only when required.
99107
4. Collect or derive the physical signal catalog for `IOFacade`.
100108
5. Collect or derive the logical parameter and device catalog for `DeviceFacade`.
101109
6. Select or derive the `StatusFlow`.
102110
7. Run a guided survey for each FSM state to collect the output function of `Main`.
103111
8. Run a guided survey for each `StatusFlowTransition` to collect predicates, boolean conditions, and precedence for `MainRuleEngine`.
104-
9. Fill the PLC code templates.
105-
10. Write the generated files into the component output tree instead of a flat scratch directory.
106-
11. Cross-check the generated PLC artifacts back against the seed-derived catalog before treating them as reviewable output.
112+
9. Require `outputs_reviewed: true` for every state and `code_generation_approved: true` for every FSM.
113+
10. Generate one Application per top-level system and order its subsystem controllers by `call_sequence`.
114+
11. Fill the PLC code templates without unresolved orchestration placeholders.
115+
12. Cross-check the generated PLC artifacts back against the seed-derived catalog before treating them as reviewable output.
107116

108117
Useful helper scripts:
109118

@@ -114,6 +123,10 @@ Useful helper scripts:
114123
- both helpers support `--session-dir` for session-aware output and status updates
115124
- `scripts/validate_generated_plc_against_seed.py`
116125
- verifies that the generated PLC declarations still match the seed-derived device, parameter, request, and status-flow catalog
126+
- `scripts/render_codesys_applications.py`
127+
- generates all isolated CODESYS Application bundles from the reviewed session
128+
- copies the framework unless `--no-copy-framework` is used
129+
- validates orchestration fields and writes invocation-order traceability
117130

118131
Important distinction:
119132

@@ -216,6 +229,7 @@ Read these files when generating code or collecting inputs:
216229
- `references/plc-codegen-templates/MainRuleEngine.template.pou`
217230
- `references/main-rule-engine-input-schema.md`
218231
- `references/main-rule-engine-input-schema.yaml`
232+
- `references/codesys-application-architecture.md`
219233
- `references/device-manager-full-call-signatures.md`
220234
- `references/moqui-seed-xml-workflow.md`
221235
- `references/moqui-seed-template.xml`
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
display_name: Moqui PLC Designer
2-
short_description: Generate PLC skeletons and supporting code from Moqui devices, parameters, requests, and status flows.
3-
default_prompt: Generate or refine PLC code from Moqui Device, Parameter, DeviceRequest, and StatusFlow data using the bundled templates and scripts.
2+
short_description: Generate isolated CODESYS Application bundles from reviewed Moqui PLC models.
3+
default_prompt: Generate and validate one isolated CODESYS Application per top-level system, with reviewed supervisor logic and sequential subsystem FSM controllers.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# CODESYS Application architecture
2+
3+
Use one isolated CODESYS `Application` for every top-level controlled system.
4+
CODESYS permits multiple uniquely named Application objects below one PLC device;
5+
each Application contains its own POUs, libraries, global variables and Task
6+
Configuration.
7+
8+
For every generated Application:
9+
10+
- include one dedicated copy of `iec61131/moqui/framework`;
11+
- include one runtime component under `runtime/component/<component>`;
12+
- keep exactly one supervisor `Main`, `MainRuleEngine`, `DeviceFacade`,
13+
`DeviceManager` and `DeviceDiagnostics`;
14+
- generate subsystem FSMs as uniquely named controller/status pairs;
15+
- call subsystem controllers sequentially by ascending `call_sequence`;
16+
- call `DeviceManager` once, after all subsystem controllers;
17+
- let the developer create control/communication tasks and the device tree;
18+
- let the developer verify connected devices and select the Application used for
19+
device I/O when multiple Applications share the same PLC device.
20+
21+
Official reference:
22+
https://content.helpme-codesys.com/en/CODESYS%20Development%20System/_cds_obj_application.html
Lines changed: 15 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,15 @@
1-
# MainRuleEngine Input Schema
2-
3-
Questo schema definisce i dati minimi necessari per generare:
4-
5-
- `${SENSOR_PREDICATES}`
6-
- `${STATE_TRANSITION_CASES}`
7-
8-
nel template:
9-
10-
- `references/plc-codegen-templates/MainRuleEngine.template.pou`
11-
12-
## Obiettivo
13-
14-
Separare chiaramente:
15-
16-
- topologia della FSM, derivabile da `StatusFlow`
17-
- semantica dei predicati, da fornire o confermare
18-
- precedenza delle transizioni
19-
20-
## Struttura
21-
22-
Lo schema YAML di esempio e' in:
23-
24-
- [main-rule-engine-input-schema.yaml](./main-rule-engine-input-schema.yaml)
25-
26-
## Sezioni principali
27-
28-
### `context`
29-
30-
Informazioni generali:
31-
32-
- `component_name`
33-
- `status_enum`
34-
- `main_status_flow_id`
35-
- `initial_state`
36-
37-
### `request_reset_block`
38-
39-
Elenca quali request vanno resettate all'inizio di ogni scan.
40-
41-
### `predicate_groups`
42-
43-
Raggruppa i predicati per dominio:
44-
45-
- `process`
46-
- `environment`
47-
- `safety`
48-
- `timing`
49-
- `custom`
50-
51-
Ogni predicato ha:
52-
53-
- `name`
54-
- `target`
55-
- `expression`
56-
- `comment`
57-
- `depends_on`
58-
59-
### `transitions`
60-
61-
Una voce per ogni arco di `StatusFlowTransition`.
62-
63-
Campi principali:
64-
65-
- `from_status`
66-
- `to_status`
67-
- `transition_name`
68-
- `priority`
69-
- `condition`
70-
- `request_assignments`
71-
- `comment`
72-
73-
### `global_fault_gate`
74-
75-
Specifica eventuali condizioni globali che causano `faultRequest`.
76-
77-
## Regole di generazione
78-
79-
- `${SENSOR_PREDICATES}` si genera concatenando i `predicate_groups`
80-
- `${STATE_TRANSITION_CASES}` si genera raggruppando le `transitions` per `from_status`
81-
- l'ordine degli `ELSIF` deriva da `priority`
82-
- se manca `condition`, lo skill deve chiedere chiarimenti all'utente
83-
- lo skill puo' precompilare parti dello schema leggendo:
84-
- seed XML `StatusFlowItem`
85-
- seed XML `StatusFlowTransition`
86-
- seed XML `ParameterDef`
87-
- seed XML `Parameter`
88-
- seed XML `DeviceRequestItem`
1+
# MainRuleEngine input
2+
3+
Use `main-rule-engine-survey.yaml` as the code-owned semantic input paired with
4+
the topology in `main-fsm-survey.yaml`.
5+
6+
- Define every predicate with an Application-global `name` and IEC 61131-3 ST
7+
`expression`.
8+
- Define every transition condition and unique precedence for its source state.
9+
- For same-flow transitions, omit `request_assignments` to use the generated
10+
target-state request.
11+
- For cross-flow transitions, provide `consume_condition` plus reviewed
12+
`request_assignments` and `apply_assignments`; generation must stop when any
13+
part is absent.
14+
- Keep these expressions in the saved session and generated PLC source. Do not
15+
persist them in `StatusFlowTransition.conditionExpression`.
Lines changed: 22 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,22 @@
1-
context:
2-
component_name: hvac
3-
status_enum: MainStatus
4-
main_status_flow_id: HvacMainStatusFlow
5-
initial_state: Standby
6-
7-
request_reset_block:
8-
assignments:
9-
- "dev.faultRequest := FALSE;"
10-
- "dev.standbyRequest := TRUE;"
11-
- "dev.heatingRequest := FALSE;"
12-
- "dev.coolingRequest := FALSE;"
13-
- "dev.ventilationRequest := FALSE;"
14-
- "dev.dryingRequest := FALSE;"
15-
- "dev.surfaceThawingRequest := FALSE;"
16-
- "dev.coreThawingRequest := FALSE;"
17-
18-
predicate_groups:
19-
process:
20-
- name: tempInRange
21-
target: dev.tempInRange
22-
expression: "(dev.tempFeedback >= dev.tempMin + dev.tempHysteresis) AND (dev.tempFeedback <= dev.tempMax - dev.tempHysteresis)"
23-
comment: "Process temperature inside operating window"
24-
depends_on: [dev.tempFeedback, dev.tempMin, dev.tempMax, dev.tempHysteresis]
25-
- name: tempOverMax
26-
target: dev.tempOverMax
27-
expression: "dev.tempFeedback > dev.tempMax"
28-
comment: "Process temperature above maximum"
29-
depends_on: [dev.tempFeedback, dev.tempMax]
30-
safety:
31-
- name: ductTempOverMax
32-
target: dev.ductTempOverMax
33-
expression: "dev.ductTempFeedback > dev.ductTempMax"
34-
comment: "Supply air too hot"
35-
depends_on: [dev.ductTempFeedback, dev.ductTempMax]
36-
37-
transitions:
38-
- from_status: Standby
39-
to_status: Cooling
40-
transition_name: Cooling
41-
priority: 20
42-
condition: "(dev.lastStatus = MainStatus.Cooling OR dev.lastStatus = MainStatus.Standby) AND dev.tempOverMax"
43-
request_assignments:
44-
- "dev.standbyRequest := FALSE;"
45-
- "dev.coolingRequest := TRUE;"
46-
comment: "Temperature above limit -> start/resume cooling"
47-
48-
- from_status: Standby
49-
to_status: Heating
50-
transition_name: Heating
51-
priority: 30
52-
condition: "(dev.lastStatus = MainStatus.Heating OR dev.lastStatus = MainStatus.Standby) AND dev.tempUnderMin"
53-
request_assignments:
54-
- "dev.standbyRequest := FALSE;"
55-
- "dev.heatingRequest := TRUE;"
56-
comment: "Temperature below limit -> start/resume heating"
57-
58-
- from_status: Cooling
59-
to_status: Standby
60-
transition_name: Standby
61-
priority: 10
62-
condition: "dev.tempUnderMin OR dev.isCompleted"
63-
request_assignments:
64-
- "dev.standbyRequest := TRUE;"
65-
comment: "Cooling target reached or work complete"
66-
67-
global_fault_gate:
68-
expression: "error OR ((dev.signalMgmt.cumulativeOutputAction AND SignalOutputAction.ImmediateStop) <> 0)"
69-
assignment: "dev.faultRequest := TRUE;"
70-
return_after_fault: true
1+
fsms:
2+
- fsm_id: ExampleFsm
3+
status_flow_id: ExampleStatusFlow
4+
predicates:
5+
- name: startAllowed
6+
expression: dev.enableRequest AND NOT dev.faultRequest
7+
comment: Reviewed start permission
8+
transitions:
9+
- from_status_id: ExStandby
10+
to_status_id: ExRunning
11+
to_fsm_id: ""
12+
name: Start
13+
condition: dev.startAllowed
14+
consume_condition: ""
15+
precedence: 10
16+
request_assignments: []
17+
apply_assignments: []
18+
notes: Same-flow transitions use the generated target-state request by default.
19+
global_overrides:
20+
fault_condition: error OR ((dev.signalMgmt.cumulativeOutputAction AND SignalOutputAction.ImmediateStop) <> 0)
21+
reset_condition: ""
22+
notes: ""

agent-skills/skills/moqui-plc-designer/references/plc-codegen-templates/DeviceFacade.template.dut

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,12 @@ ${PROCESS_MODE_DECLARATIONS}
2626
lastStatus : ${MAIN_STATUS_ENUM} := ${INITIAL_STATUS};
2727
${STATE_REQUEST_DECLARATIONS}
2828

29+
(* Subsystem FSM states exposed for diagnostics and upstream publication. *)
30+
${SUBSYSTEM_FSM_DECLARATIONS}
31+
2932
(* ========== Atomic devices ========== *)
3033
${ATOMIC_DEVICE_DECLARATIONS}
3134

3235
(* Signal management - centralised fault / alarm / warning engine *)
3336
signalMgmt : SignalMgmt;
3437
END_STRUCT END_TYPE
35-
36-
(* Questions for the skill/user:
37-
* 1. Which analog input signals and setpoints must be declared as REAL?
38-
* 2. Which digital signals must be declared as BOOL?
39-
* 3. Which computed predicates are required by MainRuleEngine?
40-
* 4. Which timing/process flags are needed (estimatedRuntime, minRuntime, etc.)?
41-
* 5. Which request flags correspond to the states in the selected StatusFlow?
42-
* 6. Which atomic devices exist: Actuator, ActuatorGroup, Axis, AxisGroup, ProcessPid?
43-
*)

0 commit comments

Comments
 (0)