Skip to content

Commit 848257f

Browse files
feat(gateway): generate executable device request seeds
1 parent 1a8071a commit 848257f

17 files changed

Lines changed: 515 additions & 15 deletions

File tree

agent-skills/skills/moqui-device-gateway-startup/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
22
name: moqui-device-gateway-startup
33
description: Use when the user wants a guided first startup of moqui-device-gateway from reviewed Moqui seed data. This skill inspects the modeled gateway identity, device groups, and DeviceRequest rows, then emits a step-by-step startup checklist aligned with the gateway README and automated tests.
4-
compatibility: Requires Python 3.14+
54
license: ../../LICENSE.md
65
metadata:
76
author: moqui-industrial
87
version: "1.0"
8+
compatibility: Requires Python 3.14+
99
---
1010

1111
# Moqui Device Gateway Startup

agent-skills/skills/moqui-device-gateway-startup/scripts/render_gateway_startup_guide.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
from pathlib import Path
1717

1818

19+
GATEWAY_RUN_SERVICE = "moqui.device.DeviceGatewayServices.run#GatewayDeviceRequest"
20+
21+
1922
def utc_now() -> str:
2023
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
2124

@@ -193,6 +196,16 @@ def analyze_model(model: dict) -> dict:
193196
if request.get("routerEnumId") == "DrrMoquiDeviceGateway"
194197
and request.get("deviceId")
195198
and request.get("deviceId") not in scoped_devices
199+
and request.get("runServiceName") != GATEWAY_RUN_SERVICE
200+
)
201+
dispatch_wrappers = sorted(
202+
(
203+
request
204+
for request in requests.values()
205+
if request.get("deviceId") == gateway_id
206+
and request.get("runServiceName") == GATEWAY_RUN_SERVICE
207+
),
208+
key=request_name_of,
196209
)
197210

198211
if not scoped_request_names:
@@ -206,6 +219,7 @@ def analyze_model(model: dict) -> dict:
206219
"group_ids": group_ids,
207220
"scoped_devices": [scoped_devices[key] for key in sorted(scoped_devices)],
208221
"requests": [requests[name] for name in scoped_request_names],
222+
"dispatch_wrappers": dispatch_wrappers,
209223
"request_items_by_name": request_items_by_name,
210224
"out_of_scope_request_names": out_of_scope_request_names,
211225
}
@@ -344,6 +358,15 @@ def render_guide(seed_path: Path, output_path: Path, analysis: dict) -> str:
344358
lines.append("- No in-scope gateway-routed requests were found.")
345359
lines.append("")
346360

361+
if gateway["dispatch_wrappers"]:
362+
lines.append("### Moqui REST dispatch wrappers")
363+
lines.append("")
364+
for request in gateway["dispatch_wrappers"]:
365+
lines.append(
366+
f"- `{request_name_of(request)}` invokes `{request.get('query', '')}` through `{request.get('brokerUri', '')}`."
367+
)
368+
lines.append("")
369+
347370
first_write = next(
348371
(request for request in gateway["requests"] if request.get("requestTypeEnumId") == "DrtWrite"),
349372
None,

agent-skills/skills/moqui-device-seed-designer/SKILL.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Useful helper scripts:
6464
- `scripts/render_seed_from_surveys.py`
6565
- validates multi-FSM surveys and materializes StatusType/StatusItem/StatusFlow topology
6666
- assigns each FSM to its owning subsystem Device and preserves the physical device parent tree
67+
- generates executable gateway-side MQTT/OPC UA requests plus their Moqui-side REST dispatch wrappers
6768

6869
## Validation Principle
6970

@@ -75,6 +76,10 @@ The skill should verify:
7576
- parent/child references are resolvable
7677
- enumerations and purpose fields are coherent
7778
- request items point to existing parameters
79+
- each gateway sampling domain resolves to exactly one declared gateway transport
80+
- MQTT requests define a Camel `paho-mqtt5:` base URI and explicit topics
81+
- OPC UA requests reference a generated `DeviceConnection` and explicit node IDs
82+
- each Moqui-side dispatch wrapper targets a gateway Device and references its field-side request by `query`
7883
- status flows contain ordered states and valid transitions
7984
- every system/subsystem owns at most one directly visible FSM Device projection
8085
- flat FSMs remain independent; nested transitions use `toStatusFlowId` only when explicitly requested
@@ -106,6 +111,18 @@ The standard request families are normally only:
106111
2. recipe export request
107112
3. one live-parameter request for values acquired by `MqttParameterSub` in [moqui/moqui-plc](https://github.com/moqui/moqui-plc)
108113

114+
For each gateway-executed family, keep the two model rows distinct:
115+
116+
- the field-side request targets the PLC and contains MQTT/OPC UA transport data;
117+
- the Moqui-side wrapper targets the gateway, uses
118+
`moqui.device.DeviceGatewayServices.run#GatewayDeviceRequest`, stores the
119+
gateway REST base URL in `brokerUri`, and names the field-side request in
120+
`query`.
121+
122+
Do not put API keys, broker passwords, or OPC UA credentials in generated seed
123+
files. Supply credentials through deployment configuration or referenced user
124+
accounts.
125+
109126
So the skill should avoid over-designing many different request families when
110127
the real variability belongs to the parameter model and to the request items.
111128

agent-skills/skills/moqui-device-seed-designer/references/seed-survey-fields.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ For each elementary device:
7171
- `iecType`
7272
- optional `reverseLogic`
7373
- source classification rule
74+
- explicit `gatewayQuery` (MQTT topic or OPC UA node ID) when the signal is projected through the gateway
7475
- notes
7576

7677
## Sampling domains
@@ -112,6 +113,8 @@ Optional but strongly recommended when the application also uses
112113
- `gatewayName`
113114
- optional `gatewayDeviceTypeEnumId`
114115
- optional `gatewayMemberPurposeEnumId`
116+
- `restBaseUri` used by Moqui to invoke the standalone gateway
117+
- positive `restTimeoutSeconds`
115118
- `scopedSubsystemIds`
116119
- optional `scopedDeviceIds`
117120
- optional notes
@@ -146,6 +149,25 @@ Required before declaring the seed complete:
146149
- default PLC4J `runServiceName`
147150
- connection strategy notes
148151
- rationale / constraints
152+
- one `gatewayTransports` row per runtime transport:
153+
- stable `transportId`
154+
- owning `gatewayDeviceId`
155+
- protocol: `mqtt` or `opcua`
156+
- explicitly scoped sampling-domain IDs
157+
- MQTT Camel base `brokerUri`, or OPC UA connection/driver/transport fields
158+
- optional ownership and topic of the single PLC-log and live-parameter MQTT channels
159+
160+
The MQTT base URI must leave the topic portion empty so the gateway can append
161+
each `DeviceRequestItem.query`, for example
162+
`paho-mqtt5:?brokerUrl=tcp://artemis:1883&qos=1`. Credentials are deployment
163+
configuration, not generated seed data.
164+
165+
`MqttParameterSub` subscribes to one configured live-parameter topic. Therefore
166+
all items in the generated live-parameter request use that same topic;
167+
`mqttKey` remains the application-level JSON mapping key recorded as
168+
`DeviceRequestItem.requestItemName`. The PLC-side JSON mapper must explicitly
169+
support the gateway payload contract; seed generation does not infer or validate
170+
that Structured Text mapping.
149171

150172
This survey is where the workflow captures the non-optional fact that the
151173
model must project onto at least one runtime transport layer:

agent-skills/skills/moqui-device-seed-designer/scripts/README.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ Current scripts:
2626
- materializes:
2727
- root PLC/controller `Device` + `PhysicalDevice`
2828
- optional survey-derived gateway `Device` + `PhysicalDevice`
29-
- optional survey-derived `DeviceConnection` rows for `moqui-plc4j`
29+
- optional survey-derived `DeviceConnection` rows for OPC UA gateway transports and `moqui-plc4j`
3030
- survey-derived elementary child `Device` + `PhysicalDevice` rows
3131
- subsystem and gateway `DeviceGroupMember` rows
3232
- signal-derived `ParameterDef` + `Parameter` rows
3333
- grouped physical I/O `DeviceRequest` + `DeviceRequestItem` rows for:
34-
- `moqui-device-gateway` using signal names / MQTT semantics
34+
- `moqui-device-gateway` using explicit MQTT topics or OPC UA node IDs
3535
- `moqui-plc4j` using explicit `plc4j_query` values from the surveys
36+
- Moqui-side REST dispatch wrappers linked to gateway-side requests through `DeviceRequest.query`
3637
- runs the upstream survey validation first, so partial surveys block generation
3738
- keeps the output explicitly reviewable rather than pretending that all enums or grouping decisions are final
3839

0 commit comments

Comments
 (0)