Supply the motion verbs OPC 40010 leaves undefined - #47
Conversation
OPC 40010-1 describes robot topology in detail and defines no motion verbs at all: its whole actuation surface is Start, Stop and loading a named program. A client can discover everything about a robot's construction and cannot ask it to move anywhere. This adds OPC UA - Robot Intent, which supplies the verbs and nothing else, so the two compose rather than compete. The lifecycle is the harder half. A motion takes seconds and a pick takes a minute, while OPC 10000-4 clause 5.12.2 discards a method result when the Session ends "independent of the task actually performed at the Server" - so a synchronous motion method loses the outcome of work that has already physically happened. Submission therefore returns a Part 10 program instance and the client watches it, which is the resolution OPC 10000-10 clause 4.1 already reaches for exactly this case. Verbs are a DataType hierarchy rather than one Method each, so a single submission and a mission step are the same shape and extension is subtyping. Queueing is PLCopen MC_BufferMode and concurrency is VDA 5050 blockingType, both adopted unchanged because both are already implemented everywhere and no OPC UA specification defines either. Missions carry an immutable committed base and a revisable horizon. Orientation is a unit quaternion: OPC UA defines no quaternion type, and the A/B/C fields of ThreeDOrientation carry no convention of their own. Annex C carries the bidirectional conversion, including the clamp that stops a pole orientation becoming a domain error. Pick and Place reference Location nodes rather than naming a station in a string, which would be a second definition of a fact the address space already holds. Clause 10 declares the interface non-safety-rated, restricts submission to Automatic and Automatic External, and records that a stop request selects no IEC 60204-1 stop category. Clause 8 says plainly that command authority arbitrates between clients and is not the single point of control ISO 10218-2 requires. The NodeSet is standalone on base OPC UA, following the Vision precedent; OPC 40010-1 interop is an optional profile carried by a ReferenceType. The validator re-derives from the committed artifacts rather than asking the generator what it emitted, and cross-checks the specification against the model in both directions. It was mutation-tested against thirteen defects, including one introduced in the generator and fully regenerated so that the NodeSet, the CSV and Annex A all agree with each other - which only the specification invariants can catch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1dab1302-19c5-4a9b-a50c-d97d389713aa
A DataTypeEncoding browses as "Default Binary" in namespace 0. The BrowseName is standard, not model-defined, and every published companion NodeSet writes it that way. Both generators were emitting 1:Default Binary. This is not cosmetic. Tooling that resolves encodings by BrowseName cannot find them: the UA-.NETStandard model source generator fails outright on such a NodeSet, and the failure surfaces as a NullReferenceException with no indication of which node caused it - it took a binary search over the node list to find. Both models are regenerated. No NodeId moves, because only the BrowseName and the added SymbolicName change: Vision is nine lines, Robot Intent eighteen. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1dab1302-19c5-4a9b-a50c-d97d389713aa
47a8b3d to
f5789cb
Compare
Five things the specification did not do. Four are now defined; the fifth turns out not to be a scoping choice at all. Real-time was an exclusion and is now a division of labour. Trajectory execution is expressible over request/response - the whole path is handed over once and the robot's own motion kernel runs it, which is what FollowJointTrajectory and the PLCopen buffered path blocks already do - so TrajectoryIntentDataType, CartesianPathIntentDataType and ForceIntentDataType are defined here. Streaming control is not expressible, so the model brokers a channel instead: RealTimeChannelType describes and leases RTDE, EGM, FRI, RSI, MotoROS2 or OPC UA FX and the samples never traverse OPC UA. That is the same shape as the Vision model brokering a media endpoint rather than carrying pixels. Safety cannot be brought into scope as a rating, and the reason is structural rather than editorial. OPC 10000-15 carries cyclic safety data from a provider to a consumer; the consumer's request holds an identifier, a monitoring number and one octet of explicitly non-safety flags, so a caller has no channel through which to supply safety-rated arguments. Every safety fieldbus expresses a safety command as a continuously asserted cyclic signal, because the integrity argument rests on the fail-safe state that follows when assertion stops - and a Method call has no defined behaviour when it stops being called. So safety is in scope as awareness plus a duty: SafetyStateType reports what the safety system enforces, clause 10.4 requires a Server to refuse work that would exceed it, and the same clause says plainly that neither of those makes anything safe. A Server may observe the safety system and refuse on what it sees; it may never instruct it. Robot construction was excluded on the grounds that OPC 40010 covers it. It does not cover this part: OPC 40010-1 describes topology and axes and defines no kinematic chain an IK solver could use, and no tool centre point at all. RobotDescriptionType is therefore additive, and Annex B fixes which side decides where both are present. Process models are defined for all six applications, with fastening left deliberately thin: OPC 40450 and OPC 40451 already own joining and tightening, so FastenIntentDataType references a joint there rather than restating torque strategies - the rule that already made Pick take a Location node instead of a station string. Mission branching follows IEC 61131-3 rather than a behaviour tree. Steps and transitions with alternative and parallel divergence are the notation the controller audience knows and have an IEC serialization; a behaviour tree needs a tick runtime controller vendors do not provide. Transition conditions reuse the base UA ContentFilter, so nobody has to write a parser for an invented expression language, and an empty transition array leaves a mission the flat sequence it was. 83 nodes added, 0 removed, 0 renumbered. The validator gained invariants for the process hierarchy, the ContentFilter reuse and the mandatory safety members, and the mutation suite grew from 13 defects to 17 - all caught, with the tree restored byte-exactly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1dab1302-19c5-4a9b-a50c-d97d389713aa
|
Stacked below #49. That PR branches from this one and restructures what this specification interoperates with, so this one should land first. Relevant to reviewing this PR: #49 adds Annex E and the |
The prose said TrajectoryIntentDataType carries PathTolerance, GoalTolerance and GoalTimeTolerance "all MotionToleranceDataType". GoalTimeTolerance is a Duration. An implementer decoding the structure from the prose gets the wire format wrong at that field, and the structure is not self-describing there. Lateness is one number, not a pose deviation. The 6.3 state diagram had Retriable --> Queued and gave Retriable no exit to terminal, so execution resumed on the same instance. Clause 3 defines Retriable as a state "from which execution does not resume", 6.6 says Retry creates a NEW operation with the original left terminal, and 6.7 says a terminal Result shall not change thereafter. The model cannot distinguish the two readings - Retriable to Queued is Halted to Ready, which Part 10 permits - so a conformance test built from the model alone rejects neither, and two implementations get incompatible operation lifetimes. FinalResultData and ProgramDiagnostic are Optional in ProgramStateMachineType and were not promoted. So 6.7's "shall also be reachable under FinalResultData" rested on a member a conformant Server may omit, and 1.2's headline "auditable commanding" claim - which IS ProgramDiagnostic and nothing else - was false against a legal implementation. Both are now Mandatory on IntentOperationType, which is the repository's stated remedy: a conformance unit that depends on an inherited member promotes that member. Written beside their type the promotions renumbered 39 members; they are allocated at the end of the id space instead. 0 moved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1dab1302-19c5-4a9b-a50c-d97d389713aa
Review response — 3 High findings fixed
The state diagram let execution resume from a state the document defines as terminal. §6.3 had Two normative claims rested on Optional Part 10 members that were never promoted. §6.7's "shall also be reachable under Writing the promotions beside their type renumbered 39 members; they are allocated at the end of the id space instead. 0 moved. 9/9 checks green. Not fixedSeven Medium findings are left for a follow-up rather than folded in here, since none is a correctness bug in the model: three |
Implementing this specification in the OPC UA .NET Standard stack surfaced six defects. Each is a claim the document already made that the model did not let a conformant Server keep. Clause 6.2 orders six refusals and clause 5.8 calls the failure set small and diagnosable on purpose, but SubmitIntent returned only IntentId and Operation. A client could see that something had been refused and never which of the six it was, so the ordering was unobservable and the failure taxonomy unreachable. SubmitIntent, SubmitMission and Retry now return Accepted, Failure and Message, clause 6.2 states that a Server returns Good and reports the refusal there rather than substituting a Bad StatusCode, and OpenRealTimeChannel gains a Message because clause 6.9 hid four grounds for refusal behind one boolean. IntentOperationType declared ProgramDiagnostic as a Property of PropertyType reached by HasProperty. OPC 10000-10 declares it as a Variable of ProgramDiagnostic2Type reached by HasComponent, so the declaration added a second member beside the inherited one instead of promoting it - which is why a Part 10 client would not have found it where Part 10 says it is, and why the first stack to generate the model deleted it. A promotion changes the ModellingRule and nothing else; clause 6.1 now says so. WaitIntentDataType.Signal read "an OutputSignal or other node", which clause 11.3 cannot check, and an unvalidated NodeId is the surface clause 11.3 exists to close. It is now bounded to an OutputSignal under the controller or a Boolean Variable under it, and clause 11.3 tabulates the expected type of every NodeId-valued member rather than leaving "the expected type" to the reader. Ready, ActiveIntent, ActiveMission and ControlOwner were in the model and in no clause; clause 5.7.0 gives them normative meaning. Clause 6.9 now bounds RequestedLease, which the lease rules never limited. The NodeSet, the CSV and Annex A are regenerated; no previously assigned NodeId moves.
Implementing the specification a second time surfaced three more places where a conformant Server could tell a client something untrue. Clause 9 already had three rules keeping the capability declaration honest, but none of them said that a declared capability must come with the Methods that make it usable. SubmitMission, UpdateMission, CancelMission and both real-time channel Methods are Optional on IntentControllerType, so a Server could report MissionsSupported true and omit SubmitMission entirely - which is exactly what the first implementation did, and the contradiction only surfaced when a client called something that was not there. A fourth rule and a table now fix which Methods each declaration implies, and like the other three it is checkable by browsing a running Server. Clause 6.5 gave StopMode the PossibleStopModes vocabulary of OPC 40010-1 and then said nothing about a Server that treats every value alike, so accepting the argument and discarding it looked conformant. A Server must now either honour it or treat every value as its single stop behaviour, and should say which - a client that asks for OnPath and silently receives a QuickStop has been told something untrue about how the cell came to rest. Clause 6.4 left the stop mode of a superseded intent undefined, because an Aborting submission names none. The Server chooses, should choose the most urgent stop the cell tolerates since the successor is about to command motion of its own, and should document which. Prose only; the model is unchanged and regenerates identically.
Clause 12 defined conformance in terms of facets and then gave a Server nowhere to say which ones it had. A client that wanted to know whether a controller supported blending had to re-derive the whole of Table 12.2 from the address space itself, which is what the reference implementation ended up doing. That is worse than verbose. Several rows of the table are behavioural -- "the four blending buffer modes accepted and honoured", "the refusal rules of 6.2", "the base immutability rules of 7.2" -- and no amount of browsing settles them. Two clients deriving conformance independently could therefore reach opposite conclusions about the same Server and both be reading the specification correctly. OPC 10000-5 already answers this with ServerCapabilitiesType. ServerProfileArray: the Server states its profiles rather than leaving them to be inferred. IntentCapabilitiesType now carries SupportedFacets on the same principle, Mandatory, naming the facets of Table 12.2 verbatim. Clause 12.2 now separates the two kinds of requirement it had been mixing. Structural requirements are settled by reading -- the Outputs folder, more than one ToolType, axes covering 0 to AxisCount - 1 -- and a Server shall not list a facet whose structural requirements are unmet. Behavioural requirements are the Server's attestation, and clause 9 says what that means: listing RI-Blending while treating the buffer modes as Buffered is the same false statement rule 3 already forbids, whatever BlendingSupported says. RI-Base requires SupportedFacets, since a conformance claim that cannot be read is not a claim. The member appends at i=6139; no existing NodeId moves.
Two defects, both found by implementing the draft and reviewing the result. The promotions of FinalResultData and ProgramDiagnostic were emitted with namespace-1 BrowseNames. OPC 10000-3 says a subtype overrides an inherited InstanceDeclaration by re-declaring a child with the same QualifiedName, and a QualifiedName includes the namespace index -- so 1:ProgramDiagnostic never overrode 0:ProgramDiagnostic, it declared a second member beside it and left the inherited Optional one in place. That is the same failure the TypeDefinition and reference type were already careful to avoid, reached by a different route, and it defeated the entire point of promoting: clause 6.7's SHALL and the clause 1.2 auditable-commanding claim still rested on members a conformant Server could omit. The generated client made it visible -- its accessor used `new` to hide the base Part 10 one. Both now carry namespace-0 BrowseNames, using the same mechanism already applied to DataTypeEncoding browse names for the same reason, and every other NodeSet in the reference stack promotes inherited state-machine members unprefixed. Clause 6.6 required Pause to suspend execution retaining position, but the model defines no channel through which a Server can tell an actuator to pause mid-motion. A Server whose controller offers no pause could therefore only publish Suspended and hope -- and Part 10 defines that state as position retained, so an operator reading the HMI sees the arm at rest while it finishes the move. The reference implementation did exactly that until a review caught it. The requirement stays, because stopping the robot is what Pause should mean. What is added is the honesty rule the rest of clause 9 already applies elsewhere: a Server that cannot suspend a running intent declares PauseSupported false, may still stop its queue, and leaves the executing operation Executing with Ready reflecting that nothing new is admitted. Suspended is never reported while the robot is still moving. That makes clause 9 four rules rather than three, alongside the BlendingSupported rule it is modelled on. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a248a589-9a20-4372-868e-5d347e57001b
What this PR changes or proposes
Adds OPC UA — Robot Intent (
metaverse-specs/robot-intent/, Release 0.1.0 draft): an information model for commanding a robot at the level of task intent — move there, follow this path, weld this seam, pick from here, place at that — with a lifecycle that survives the minutes such work actually takes.OPC-UA-Robot-Intent.md— the specification (13 clauses, 4 annexes)OPC-UA-Robot-Intent-Research.md— the prior art, the gaps, and the decisions those gaps forcedOpc.Ua.RobotIntent.NodeSet2.xml/.NodeIds.csv— generated (256 nodes, 69 types)extras/robot-intent/tools/— generator, validator, generated Annex AIt also fixes a defect in both generators: a
DataTypeEncodingbrowses asDefault Binaryin namespace 0, and Vision and Robot Intent were both emitting1:Default Binary. Vision is regenerated — 9 lines, no NodeId churn.Why
OPC 40010-1 Robotics describes robot topology in detail and defines no motion verbs at all. Its entire actuation surface is
Start,Stopand loading a named program. A conformant client can discover everything about a robot's construction and cannot ask it to move anywhere. Every integration above "run program 7" is therefore bespoke, even though every vendor already has joint, linear and circular moves, a speed, an acceleration, a blend, a tool frame and a work frame.This supplies the verbs, and nothing else, so the two compose rather than compete.
The lifecycle is the harder half. A motion takes seconds and a pick takes a minute, while OPC 10000-4 §5.12.2 discards a method result when the Session ends "independent of the task actually performed at the Server" — so a synchronous motion method loses the outcome of work that has already physically happened. Submission therefore returns a Part 10 program instance the client watches, which is the resolution OPC 10000-10 §4.1 already reaches for exactly this case.
Design decisions worth review
SupportedIntentsrather than a probe for BrowseNames.A/B/Cfields ofThreeDOrientationcarry no convention of their own. Annex C carries the normative bidirectionalThreeDFrameconversion, including the clamp that stops a pole orientation becoming a domain error.ExecutionStaterefines the Part 10 state rather than restating it.Queued,Cancellingand the three terminal outcomes cannot be told apart fromCurrentStatealone; §6.3 tabulates every legal pairing, so the refinement is checkable rather than asserted.Pick/PlacereferenceLocationnodes, not station strings, andFastenreferences a joint in OPC 40450/40451 rather than restating tightening. A free-text identifier would be a second definition of a fact the address space already holds.MC_BufferModeand concurrency is VDA 5050blockingType, both adopted unchanged. No OPC UA specification defines either, and both are already implemented across the industry.ContentFilterso nobody has to write a parser for an invented expression language, and an empty transition array leaves a mission the flat sequence it was.HasIntentController(Annex B).Real-time: a division of labour, not an exclusion
Trajectory execution is in scope — the whole time-parameterised path is handed over once and the robot's own motion kernel runs it, which is what ROS
FollowJointTrajectoryand the PLCopen buffered path blocks already do. Streaming control is not expressible over OPC UA client/server, so the model brokers a channel instead: it describes and leases RTDE, EGM, FRI, RSI, MotoROS2 or OPC UA FX (OPC 10000-80…84), and the samples never traverse OPC UA. Same shape as the Vision model brokering a media endpoint rather than carrying pixels.Safety: awareness and a refusal duty, explicitly not a rating
This one cannot be brought into scope as asked, and the specification says so rather than working around it.
OPC 10000-15 carries cyclic safety data from a SafetyProvider to a SafetyConsumer; the consumer's request holds an identifier, a monitoring number and one octet of explicitly non-safety flags, so a caller has no channel through which to supply safety-rated arguments. Every safety fieldbus — PROFIsafe, CIP Safety, FSoE, openSAFETY — expresses a safety command as a continuously asserted cyclic signal, because the integrity argument rests on the fail-safe state that follows when assertion stops. A Method call has no defined behaviour when it stops being called, and therefore cannot be a safety function however it is labelled.
So the model reports what the safety system is enforcing (
SafetyStateType, with the IEC 61800-5-2 function vocabulary) and refuses work that would exceed it (§10.4), and states plainly that neither of those makes anything safe. A Server may observe the safety system and refuse on what it sees; it may never instruct it.Standards check
The widely repeated claim that OPC 40010 Parts 2 (Skills) and 3 (Motion Program) are in development was tested against primary sources and is not supported — the OPC Foundation reference index lists only Part 1. What exists is
opcua-skills/skill-nodeset, a VDMA SOArc prototype stalled at v0.0.5 since 2020 underhttp://opcfoundation.org/UA/Skills/. That namespace is avoided and the prior art is cited (Annex D). Whether a non-public VDMA working draft exists is the one question public research cannot close.Type
Checklist
metaverse-specs/validate_all.py --self-containedandextras/robot-intent/tools/validate_local.py)Validation
metaverse-specs/validate_all.py --self-contained— all extensions OKmain— 83 added, 0 removed, 0 renumbered for Robot Intent; unchanged for VisionThe validator re-derives everything from the committed artifacts rather than asking the generator what it emitted, and cross-checks the specification against the model in both directions: every type and enumeration literal the model declares must be named in the prose, and every
ns=1;i=<n>the prose cites must exist in the model. That check earned its keep twice — 48 unnamed members the first time, and another 48 when the scope was enlarged.It is mutation-tested against 17 defects and catches all of them, restoring the tree byte-exactly afterwards. That includes the case that is easy to miss — a defect introduced in the generator and then fully regenerated, so the NodeSet, the CSV and Annex A all agree with each other and only the specification invariants can catch it — plus, since the scope grew, a process intent reparented off its abstract base, a transition condition swapped off
ContentFilter, and a mandatory safety member demoted to optional.Note for the reviewer
metaverse-specs/README.mdwas rebased over the OPC Foundation review move (#44/#46). Therelease-spec-linkmarkers and the review notice are preserved verbatim; the section heading is now "The OpenUSD pair: two parts, two directions" because the tree holds four specifications rather than two, andvision/androbot-intent/are listed alongside them.Related
The .NET stack implementation is now open as a draft at OPCFoundation/UA-.NETStandard#4165. It generates the model from this NodeSet, so the two move together — 149 tests pass there on net48 and net10.0.
The encoding BrowseName fix in this PR is what unblocked it: the .NET model source generator fails on
1:Default Binarywith a bareNullReferenceExceptionthat names no node.