OEM Skids and Vendor Units¶
A vendor supplies a process skid with its own controller and its own logic, and the plant needs it under recipe control without re-engineering it. The skid keeps its sequences and its interlocks. Cloud-Native DCS commands it from a recipe phase and reads back what it reports.
Direction is worth settling before anything else, because this is the one southbound page in the Integration section. On every other page here an external system calls the DCS or subscribes to what it publishes. Toward a skid the DCS is the client and the orchestrator. It opens the session, it invokes, and it waits on the skid's own feedback. The gateway exposes no northbound OPC UA server, so a vendor asking for the address of your server to poll is describing an integration this product does not offer.
Which path the skid takes¶
Two questions settle the route, and the vendor can answer both. Does the skid serve OPC UA, and does it ship an MTP file?
| What the skid exposes | Route | What lands in the cluster |
|---|---|---|
| An MTP ServiceControl interface over OPC UA | Device discovery recognizes the interface and flips the candidate to the service plane | A Unit whose serviceBinding declares mtp services carrying their resolved ServiceControl addresses |
| OPC UA Methods | Device discovery introspects each Method's arguments | A Unit whose serviceBinding declares one service per picked Method |
| OPC UA variables and no callable surface | Device discovery on its ControlModule default | ControlModule instances under a parent Unit you pick, sharing one opcua IOModule |
| Modbus TCP registers | I/O authoring | An IOModule carrying a control-device field-device record, and the ControlModules bound to its channels |
| Something with no driver in tree, such as CIP tags on ControlLogix, Profibus, or DeviceNet | An OPC UA aggregation server in front of it | Whichever row above matches what the aggregation server presents |
The split that decides the shape is service plane against data plane, and the
protocol sits underneath it
(ADR 0020). A
skid that runs its own sequences and offers them as named services becomes a
Unit, and the DCS orchestrates it. A box that presents values and coils
becomes control modules under a unit the DCS already owns, and the DCS runs the
loop.
Bringing one up¶
Point discovery at the endpoint. The Device Discovery Wizard browses the skid's address space, classifies each node, and applies the emitted resources. It owns the mechanics of the security policy, the gateway's own client identity, the scan, per-row classification, and the apply.
Declare what the live server could not answer for. Discovery persists what
it read and guesses at nothing else. For an MTP service, procedure identity and
the self-completing versus continuous split are vendor engineering data that no
live server serves. Declare procedures on the Unit after apply, or take them
from the vendor's file with
MTP import. The vendor's file is where they
are written down. For a
generic OPC UA device, emitted tags carry an address, a data type and no
role, because discovery cannot tell a setpoint from a feedback
(ADR 0016). Declare roles when you
templatize the module. Two more items land after apply: a credentials Secret
named in the wizard resolves in the Unit's own namespace, and the observed
server-certificate fingerprint becomes a pin on
spec.serviceBinding.security.serverCertSha256Pin.
Author the phases. An MTP service is driven with MTP_COMMAND,
MTP_STATE and MTP_COMMAND_ENABLED. A Method service is called with
CALL_SERVICE. The authoring shape is the same for both: the command goes in a
step action, and the module's feedback goes in the transition that follows. See
Structured Text for the
builtins and examples/smart-skid/ for a worked Unit and its phase templates.
A step whose action commands a skid should carry a timeoutSeconds, since the
guard after it is waiting on a remote module.
A Modbus skid takes the I/O route. The IOModule is authored
against the register map in the vendor's manual, and a
phase commands the skid by writing its handshake tags and waiting on its
feedback tags.
What the vendor's controller keeps owning¶
The skid's interlocks, its lower-level loops and its internal sequencing stay inside the skid. That boundary is a modeling rule in this product, and four consequences follow from it for the phases that command such a unit.
- Orchestrate mode only. A
Unitcarrying aserviceBindingmust carry no equipment modules, and admission rejects the pair. The Unit's state machine is the DCS view of the orchestration, and it never mirrors the skid's internal states (ADR 0001). - A refused command holds the phase, and the refusal is legible. Every MTP
command is gated on the live
CommandEnword the module publishes. A command the skid is not offering holds the phase with a message naming the state the service is in and the commands it would accept. An interlock inside the skid can therefore refuse a service the recipe treats as mandatory, and that arrives as a hold on the batch. - The skid's response to a phase hold is something you write. Holding a
phase does not stop the skid. The
holdingChartdecides what the skid is told, and therestartingChartis the only thing that re-commands a step's action afterward, because a resumed step is never replayed (ADR 0047). - One physical thing gets one record. An
IOModuleaddressing the same host as aserviceBindingUnit is that skid's data plane, and the Unit is the box. The reconciler reports the overlap onstatus.conditions[type=DistinctAssetRecord]and the asset position stays with the Unit (ADR 0033).
MTP, and how much of it is real¶
pkg/mtpservice carries VDI/VDE/NAMUR 2658 Blatt 4 verbatim: the 16 states in
five levels, the ten commands, the Table 14 bit codings, and the Figure 3
transitions, with the ISA-88 correspondence declared in one place
(ADR 0045). make test-mtp-interop writes
a Table 14 command word to a node of each declared integer width on Microsoft's
opc-plc, so the coding is proven against a server this project did not write.
The file half now reads. dcs mtp import and the gateway's import endpoint
parse the AutomationML container a vendor ships and generate the resources.
The manifest is the input, with no retyping step
(#1497).
See Importing the vendor's MTP file below.
MTP export and the Blatt 5.1 runtime binding are still unimplemented, and no
conformance claim is made. The full posture is on the
MTP standards page.
One gap in the proof is worth knowing before a commissioning trip. The ServiceControl interaction has not run against a vendor PEA. Blatt 4 defines a variable shape with no companion NodeSet behind it, so there is no third-party address space to instantiate a fixture from. The PEA in this repository's captures therefore runs on the same Go stack as the client reading it (ADR 0045). The command-word coding is the part a foreign server has checked.
Importing the vendor's MTP file¶
A skid that ships an MTP file already has its services, procedures, parameters and every OPC UA address written down. There is therefore a second way in that does not need the skid powered on:
dcs mtp import ./GasControl.mtp \
--site plant-01 --process-cell gas-cell | kubectl apply -f -
The CRs go to stdout and the report to stderr, so that pipeline applies the
import while the report stays on the terminal. A --report-only run reads the
file and emits nothing, which is the right first run against a manifest you
have not seen. The file may be a .mtp/.amlx container or a bare .aml,
decided by its contents alone. The gateway offers the same thing at
POST /api/v1/import/mtp (API reference).
Import produces what discovery produces: an IOModule for the endpoint, a
Unit whose serviceBinding declares each service, and a template-free
ControlModule per data assembly. Two things it adds that a live scan cannot.
Procedure identity and the self-completing split come out of the file, so the
"declare what the live server could not answer for" step above is already done
for procedures. And a tag's role is declared, because an MTP data assembly's
attribute set is fixed by the standard. PosFbk on a valve is a feedback in
every conforming file. That is a declaration, and the name-based
inference ADR 0016 forbids plays no
part.
Nothing binds to the file. What comes out is yours to edit from the moment it applies, exactly like the wizard's output.
Read the report¶
The import prints a finding per item it could not carry whole, and a partial import that quietly dropped a service would be worse than a refusal. Four are worth knowing before you run one.
A manifest in the older vocabulary has every service refused. Files in the
field carry two incompatible service models. The current one declares a
ServiceProcedure with a ProcedureID. The older one declares a
ServiceStrategy with a StrategyID and names its channels CurrentState,
CommandEnable and CurrentStrategy. The channels line up one for one, which
is exactly why they are not treated as aliases: the older file does not say
which coding its command words use, and this product writes the Table 14
coding. A declaration emitted anyway would connect and then command the skid
with words it does not accept. That failure surfaces as a wedged phase, far
from the author-time error the refusal gives you instead.
Ask the vendor for an export in the current revision. The
file's control modules still import.
An engineering range often is not in the file. The current revision
addresses VSclMin and VSclMax as OPC UA nodes, so the numbers live on the
PEA and the manifest carries only their addresses. Those modules land without
engMin/engMax, and ADR 0050's range
check has nothing to compare a commanded value against until you declare them.
The older revision writes them as numbers and they are carried straight through.
Imported modules render in the generic group. deviceClass can only be
declared on a ControlModuleTemplate, and an imported module is emitted
template-free so the control operator does not compile a ControlProgram for
logic the PEA owns. The report names each module's MTP class, which is what you
would set the deviceClass from. Per-tag roles are declared and do apply.
A data assembly class this product does not model is skipped, by name. Its channel set is unknown, and a tag emitted with a guessed data type reads garbage silently.
Legacy controllers behind an aggregation server¶
Brownfield plants usually attach a legacy controller through an OPC UA aggregation server the site already owns. Kepware (KEPServerEX) is the industry default, FactoryTalk Linx Gateway is the Rockwell path, and Siemens IDLink fronts S7-300/400 and PCS 7. The vendor family table lists the recommended path per family and is the authority on it.
Three things change when the endpoint is an aggregation server.
- The box at the address is not the box the signal came from. Declare
spec.fieldDevice.type: gatewayon theIOModuleso the asset record says what it is. - One endpoint usually fronts many devices, so the emitted resources are one
shared
IOModulefor the endpoint and aControlModuleper device. - An aggregation server projects tags, and a service surface rarely survives
the hop. A skid reached this way is a data-plane device: the phase writes its
start tag and waits on its done tag, and the sequencing stays in the vendor's
controller with no service for
CALL_SERVICEor the MTP builtins to reach.
What is proven, and what is not¶
I/O carries the protocol status matrix and the vendor table, and it is the only place they live. Three of its lines decide most skid projects.
- OPC UA is implemented-untested against real metal. Connect, read, write, subscribe and the secured channel run end to end against opc-plc and open62541 on every full CI run. No PLC has answered yet, and field validation waits on a design partner.
- EtherNet/IP is experimental and gated. unit-runtime refuses to load the
driver without
--enable-experimental-drivers, and the gateway UI does not offer it. It implements the Generic Device Profile with assembly-instance addressing. CIP-tag addressing on ControlLogix and CompactLogix is a different protocol layer, and the driver has no support for it (#272). Bridge a Rockwell skid through OPC UA. - Modbus TCP is field-proven on the reference rig for single-register values, and the four vendor word and byte layouts are proven in both directions against an independent Modbus implementation. No cross-register value has been read off physical hardware.
MTP import is proven against real vendor manifests. Two are vendored under
pkg/mtpimport/testdata/vendor/ with
their provenance: a current-revision export from WAGO's e!Cockpit and an
older-revision one, both MIT-licensed. That mattered. A fixture written here
would have agreed with our own parser by construction, and these two refuted
two rules that would otherwise have shipped: a service joins its channel set
by a shared RefID value where our draft rule joined by name, and the two
vocabularies exist at all. What is still unproven is the same gap the ServiceControl interaction
has: no vendor PEA has answered an imported declaration on live metal.
Modbus TCP and EtherNet/IP also carry no authentication and no encryption, so a skid on either one needs the network segmentation the threat model specifies. Where the skid offers OPC UA, run it signed or signed-and-encrypted.
Related Documentation¶
- I/O -- protocol status, the vendor family table, and IOModule authoring
- Device Discovery Wizard -- the browse, classify, and emit flow this page routes to
- Module Type Package (VDI/VDE/NAMUR 2658) -- the standard and the adherence posture
- Structured Text -- the builtins a phase drives a skid with
- Phases -- the chart layer that issues the commands
- ADR 0001 -- why a smart skid is a Unit with a serviceBinding
- ADR 0045 -- MTP recognition, vocabulary, and conformance posture