diff --git a/docs/design/index.rst b/docs/design/index.rst index 29ab9016f..15ef48ee5 100644 --- a/docs/design/index.rst +++ b/docs/design/index.rst @@ -19,6 +19,7 @@ This section contains design documentation for the ros2_medkit project packages. ros2_medkit_msgs/index ros2_medkit_opcua/index ros2_medkit_param_beacon/index + ros2_medkit_roxsie_msgs/index ros2_medkit_serialization/index ros2_medkit_sovd_service_interface/index ros2_medkit_topic_beacon/index diff --git a/docs/design/ros2_medkit_roxsie_msgs b/docs/design/ros2_medkit_roxsie_msgs new file mode 120000 index 000000000..96dc3276c --- /dev/null +++ b/docs/design/ros2_medkit_roxsie_msgs @@ -0,0 +1 @@ +../../src/ros2_medkit_roxsie_msgs/design \ No newline at end of file diff --git a/src/ros2_medkit_roxsie_msgs/CHANGELOG.rst b/src/ros2_medkit_roxsie_msgs/CHANGELOG.rst new file mode 100644 index 000000000..1c284c6da --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/CHANGELOG.rst @@ -0,0 +1,7 @@ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changelog for package ros2_medkit_roxsie_msgs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Forthcoming +----------- +* Initial interface definitions for the ROXSIE integration: ``Alarm.msg`` and ``AlarmList.msg`` for the cyclic picture of alarms in the PLC, ``DiagnosticsStatus.msg`` for the condition sent back into the PLC. diff --git a/src/ros2_medkit_roxsie_msgs/CMakeLists.txt b/src/ros2_medkit_roxsie_msgs/CMakeLists.txt new file mode 100644 index 000000000..7b7fcd06d --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright 2026 mfaferek93 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.8) +project(ros2_medkit_roxsie_msgs) + +# Note: ROS2MedkitWarnings is intentionally NOT included here. +# rosidl-generated code triggers strict warnings we cannot fix. +find_package(ament_cmake REQUIRED) +find_package(rosidl_default_generators REQUIRED) +find_package(builtin_interfaces REQUIRED) + +rosidl_generate_interfaces(${PROJECT_NAME} + "msg/Alarm.msg" + "msg/AlarmList.msg" + "msg/DiagnosticsStatus.msg" + DEPENDENCIES builtin_interfaces +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + +ament_package() diff --git a/src/ros2_medkit_roxsie_msgs/README.md b/src/ros2_medkit_roxsie_msgs/README.md new file mode 100644 index 000000000..ceacdc5ad --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/README.md @@ -0,0 +1,115 @@ +# ros2_medkit_roxsie_msgs + +Messages for the ROXSIE integration: PLC alarms into ROS 2, and one status value back +into the PLC. + +## Overview + +ROXSIE generates the PLC blocks and a ROS 2 package for the data you declare in the YAML +config, exchanged over shared memory on the host. That covers process values. It does not +cover alarms, because alarms live in the CPU message system and not in a data block. + +So on a machine with a SIMATIC PLC and ROS 2 you still have two separate lists of what is +wrong. To join them today you either read alarm bits over a protocol and rebuild their +meaning from an engineering export, or someone copies the alarms by hand into a second +list on the panel. Both drift. + +This package holds the messages for that exchange. Alarms in the PLC arrive in ROS 2 and +become faults with freeze-frames. Back the other way, the PLC gets one value it can act on. + +## A picture, not a stream + +`AlarmList` carries every alarm that is active or waiting for acknowledgement, published +cyclically. That is a deliberate choice. + +The transport is cyclic anyway: the PLC-side node reads a data block at a fixed rate, so a +picture is what it naturally has. + +And a picture survives a restart. Whoever restarts, the next cycle carries the full truth +again. An event stream loses whatever happened while it was away, and the consumer cannot +tell an empty stream from a healthy machine. That gap is not hypothetical: reading alarms +over OPC UA has the same hole, because no table of pending alarms is served on reconnect. + +The bridge compares consecutive pictures. An entry that appears becomes a fault, an entry +that disappears heals it. + +## Two ways to fill the list + +**From bits.** The customer declares alarm bits in a data block and ROXSIE carries them +like any other data. Nothing new is needed on the generator side, so this works today. +`alarm_class`, `priority`, `producer`, `plc_timestamp` and `associated_values` stay empty, +and the freeze-frame comes from the process topics instead. Acknowledgement is a bit, so +either side can set it. + +**From the CPU alarm system.** Generated code reads the alarm system with `Get_Alarm` and +mirrors it into the data block. Still no OPC UA and no license. This fills everything: the +event time from the CPU clock, the alarm class, the priority the plant already engineers +with, and the values latched when the alarm fired. + +It also picks up alarms nobody declared. The CPU message system carries system diagnostics, +ProDiag, GRAPH, motion and security alarms, and `producer` says which one an entry came +from. A module fault or a drive alarm arrives without any extra engineering. + +The trade is acknowledgement. The CPU owns the acknowledged state and has no per-alarm +acknowledge instruction, so setting it for a single alarm needs the panel or OPC UA. We +mirror it rather than write it. + +## Messages + +### Alarm.msg + +One alarm, as one entry of the list. + +| Field | Type | Description | +|-------|------|-------------| +| `alarm_id` | uint32 | Numeric alarm identifier from the PLC project | +| `source` | string | Block or instance that declared the alarm | +| `alarm_class` | string | Alarm class from the project, empty in the bit path | +| `priority` | uint8 | 0 to 16 as the PLC knows it, higher is more important | +| `producer` | string | `user_program`, `system_diagnostics`, `prodiag`, `graph`, `motion`, `security` | +| `active` | bool | Whether the condition is currently present | +| `acknowledged` | bool | Whether the alarm has been acknowledged | +| `plc_timestamp` | builtin_interfaces/Time | Last state change, from the PLC clock, UTC | +| `associated_values` | float64[] | Values latched when the alarm fired, 512 bytes total in the CPU | + +Two flags rather than one enum, because an alarm that is gone but unacknowledged is a real +state and stays in the list. Those are the same two bits the CPU alarm system tracks. + +Alarm text does not travel. The text belongs to the project, changes with the project, and +is resolved on the ROS 2 side from a table that ships with the deployment. + +### AlarmList.msg + +| Field | Type | Description | +|-------|------|-------------| +| `stamp` | builtin_interfaces/Time | When this picture was taken | +| `alarms` | Alarm[] | Alarms active or not yet acknowledged | + +### DiagnosticsStatus.msg + +| Field | Type | Description | +|-------|------|-------------| +| `heartbeat` | uint16 | Liveness counter of the diagnostics layer, about 1 Hz | +| `condition` | uint8 | `OK`, `ABNORMAL`, `ACTION_REQUIRED`, `STOP` | +| `class_bitmask` | uint16 | Optional, coarse fault classes for interlocks | +| `scope` | string | Which entity in the SOVD tree this covers | + +`condition` says what we are asking of the machine, not what to light up. Machines already +have a state machine, and where PackML is used, a block that turns machine state into +signal column bits. We hand over a condition and let that logic decide. + +When `heartbeat` stops, a watchdog in the PLC program raises its own alarm, so the plant +sees that diagnostics are down instead of a green light that means nothing. + +`condition` covers every active fault in scope, not only the ones that came from the PLC. A +navigation fault on the robot moves the same value. + +## Related packages + +- `ros2_medkit_msgs` - the core fault model these alarms are mapped onto +- `ros2_medkit_fault_manager` - fault aggregation, debounce and freeze-frame capture +- `ros2_medkit_diagnostic_bridge` - the same bridging pattern for ROS 2 `/diagnostics` + +## License + +Apache-2.0 diff --git a/src/ros2_medkit_roxsie_msgs/design/index.rst b/src/ros2_medkit_roxsie_msgs/design/index.rst new file mode 100644 index 000000000..d773bf861 --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/design/index.rst @@ -0,0 +1,227 @@ +ros2_medkit_roxsie_msgs +======================= + +This section contains design documentation for the ros2_medkit_roxsie_msgs package. + +Overview +-------- + +ROXSIE generates the PLC blocks and a ROS 2 package for the data you declare in the YAML +config, exchanged over shared memory on the host. The generated node sits on the ROS 2 +graph, so anything already listening to that graph can read the data. + +That covers process values. It does not cover alarms, because alarms live in the CPU +message system and not in a data block. + +So on a machine with a SIMATIC PLC and ROS 2 you still have two separate lists of what is +wrong. To join them today you either read alarm bits over a protocol and rebuild their +meaning from an engineering export, or someone copies the alarms by hand into a second +list on the panel. Both drift. + +This package holds the messages for that exchange. It has no runtime code, only ``.msg`` +definitions that ``rosidl`` compiles into C++ and Python bindings. + +A Picture, Not a Stream +----------------------- + +``AlarmList`` carries every alarm that is active or waiting for acknowledgement, published +cyclically. Two reasons. + +The transport is cyclic anyway. The PLC-side node reads a data block at a fixed rate, so a +picture is what it naturally has. An event stream would be built on top of that and would +need its own delivery guarantees. + +A picture also survives a restart. Whoever restarts, the next cycle carries the full truth +again. An event stream loses whatever happened while it was away, and the consumer cannot +tell an empty stream from a healthy machine. The gap is not hypothetical: reading alarms +over OPC UA has the same hole, because no table of pending alarms is served on reconnect. + +The bridge compares consecutive pictures. An entry that appears is reported as a fault, an +entry that disappears heals it. Debounce, freeze-frame capture and the audit trail are the +same ones every other fault source in medkit goes through. + +Two Ways to Fill the List +------------------------- + +**From bits.** The customer declares alarm bits in a data block and ROXSIE carries them +like any other declared data. Nothing new is needed on the generator side, so this path +works today. ``alarm_class``, ``priority``, ``producer``, ``plc_timestamp`` and +``associated_values`` stay empty, and the freeze-frame comes from the process topics the +same deployment already carries. Acknowledgement is a bit, so either side can set it. + +**From the CPU alarm system.** Generated code reads the alarm system with ``Get_Alarm`` and +mirrors it into the data block. Still no OPC UA and no license. This fills everything the +message can hold: + +* the event time from the CPU clock, rather than the moment ROS 2 noticed +* the alarm class, which says whether the alarm needs acknowledgement +* the priority the plant already engineers with, 0 to 16 +* the values the alarm system latched when the alarm fired, capped by the CPU at 512 bytes + across all of them + +It also picks up alarms nobody declared. The CPU message system carries system +diagnostics, ProDiag, GRAPH, motion and security alarms, and ``producer`` says which one an +entry came from. A module fault or a drive alarm arrives without any extra engineering, +which is the strongest argument for this path. + +The trade is acknowledgement. The CPU owns the acknowledged state and has no per-alarm +acknowledge instruction, so setting it for a single alarm needs the panel or OPC UA. We +mirror it rather than write it. + +One cost worth naming: ``Get_Alarm`` reads from a queue, one entry at a time, while the +data block has to hold a picture. The generated code has to keep that picture itself. That +is real work, not a flag. + +Architecture +------------ + +.. plantuml:: + :caption: ROXSIE alarm integration - data flow + + @startuml roxsie_alarm_integration_architecture + + skinparam linetype ortho + + package "SIMATIC PLC" { + [CPU alarm system] as AS + [Alarm bits] as BITS + [Alarm list block] as GEN + [Consumed status block] as DB + [Machine logic\nSignal column] as SIG + } + + package "ROS 2" { + [ROXSIE generated node] as NODE + [ros2_medkit_roxsie_bridge] as BR + [FaultManager] as FM + [SOVD gateway] as GW + } + + [Panel page, dashboards,\nmaintenance clients] as CONS + + AS --> GEN : Get_Alarm + BITS --> GEN : bit path + GEN --> NODE : shared memory + NODE --> BR : AlarmList + BR --> FM : ReportFault + FM --> BR : fault events + BR --> NODE : DiagnosticsStatus + NODE --> DB : shared memory + DB --> SIG + FM -- GW + GW --> CONS : HTTPS + + @enduml + +The generated node talks to the PLC over shared memory on the same host, but publishes on +the ROS 2 graph over the network. So the diagnostics side does not have to run on the +controller. It subscribes like any other ROS 2 participant. + +What Goes Back Into the PLC +--------------------------- + +One value and a heartbeat. ``DiagnosticsStatus`` says what the diagnostics side is asking +of the machine: nothing, something is abnormal, someone has to act, or stop. + +It does not say what to light up. Machines already have a state machine, and where PackML +is used, a block that turns machine state into signal column bits according to +ISA TR88.00.02. Driving the lamp ourselves would put two writers on it. We hand over a +condition and let the machine's own logic decide what that means for the lamp, for an +interlock, or for a state transition. + +The condition covers every active fault in scope, not only the ones that came from the PLC. +A navigation fault on the robot moves the same value, which is how a problem on the ROS 2 +side reaches an operator who never looks at a screen. + +``class_bitmask`` is there for machine logic that needs to interlock on a kind of fault +without reading a list. Which fault sets which bit is deployment configuration. + +Message Definitions +------------------- + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Message + - Purpose + * - ``Alarm.msg`` + - One alarm: identity, source, class, priority, producer, active and acknowledged + flags, PLC timestamp, latched values + * - ``AlarmList.msg`` + - The cyclic picture: every alarm active or not yet acknowledged + * - ``DiagnosticsStatus.msg`` + - What the diagnostics side asks of the machine: heartbeat, condition, class bits, + scope + +Alarm text does not travel at runtime. The text belongs to the project, changes with the +project, and is resolved on the ROS 2 side from a table that ships with the deployment. + +Failure Behaviour +----------------- + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Failure + - Behaviour + * - Diagnostics layer or link down + - The heartbeat stops and a watchdog in the PLC program raises its own alarm, so the + plant sees that diagnostics are down rather than a green light that means nothing + * - PLC or the generated node down + - The bridge reports a communications fault on the ROS 2 side + * - Anything restarts + - The next picture carries the full set of active alarms, so nothing is silently lost + +The first row matters most. If our layer dies, the plant has to see it. + +What Comes After +---------------- + +This package covers alarms from the PLC and one condition back. Two extensions follow, both +shaped by what a deployment needs rather than by what is interesting to build. + +**OpenTelemetry as another fault source.** A cell is more than a PLC and a robot. The switch +that flaps, the controller that runs hot, the compute in another building that this machine +depends on - none of them speak the PLC alarm system, and most of them already speak +OpenTelemetry. Every one of those vendors ships its own API, and asking each of them for an +integration does not scale; asking all of them for OpenTelemetry does. An OTLP receiver next +to this bridge leaves three things to settle: + +* **Which signal becomes a fault.** Log records at error level are the obvious answer. + Metrics are the more interesting one, because ``ros2_medkit_fault_detection`` already + evaluates thresholds and status words for PLC tags and can take a metric the same way. +* **Which entity a stream belongs to.** Resource attributes have to map onto the tree, or + every host lands in one unattached pile. +* **What the operator sees.** Nothing new to wire: an IT fault in the same list already + moves the condition that reaches the signal column. + +**OpenTelemetry as an export.** The same faults served over REST can be emitted as +OpenTelemetry, and the per-scope condition as a metric. Two renderings of one state, not two +sources of truth. Teams that already run an observability pipeline add a source rather than +integrate an API. + +Neither exists today. There is a hook where an exporter plugs in and nothing behind it. +Building it against a concrete consumer beats guessing at a generic one. + +Open Questions +-------------- + +#. Is ``alarm_id`` unique per CPU, or only per block? If only per block, the key has to be + ``(source, alarm_id)``. +#. Where does the alarm number to text table come from - the project export the generator + already reads, or a separate export? +#. How many entries can the generated data block hold? The array is fixed size once + generated, and a plant can declare hundreds of alarms. If the ceiling is low, the fill + has to prioritise and say that it truncated. +#. Is there any per-alarm acknowledge path in the CPU that we have missed? If not, + acknowledgement from the ROS 2 side stays a bit-path feature. +#. Numeric associated values, or typed? Numeric keeps the entry fixed size. Typed carries + more and brings strings back. +#. The bridge needs a mapping: which bit or alarm number means which fault code, and which + fault classes set which bits going back. Kept by hand, that mapping and the PLC program + drift apart, which is the spreadsheet problem all over again. The generator reads the + config and the project export already, so it could emit the mapping file alongside the + generated code and there would be one source of truth. Does the project export carry the + alarm declarations and texts needed for that? diff --git a/src/ros2_medkit_roxsie_msgs/msg/Alarm.msg b/src/ros2_medkit_roxsie_msgs/msg/Alarm.msg new file mode 100644 index 000000000..cc271bb0b --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/msg/Alarm.msg @@ -0,0 +1,77 @@ +# Copyright 2026 mfaferek93 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Alarm.msg - one alarm in the PLC, as one entry of AlarmList. +# +# Not an event. An entry stays in the list while the alarm is active or waiting +# for acknowledgement, and drops out once it is gone and acknowledged. The two +# flags below mirror how the CPU alarm system tracks a condition. +# +# Fields the bit path cannot fill stay empty. A consumer that finds an empty +# alarm_class or no associated values is looking at a bit-filled alarm, not at +# an error. + +# Numeric alarm identifier from the PLC project. +# +# OPEN QUESTION: unique per CPU, or only per block? If only per block, the key +# is (source, alarm_id) and a consumer cannot key on the number alone. +uint32 alarm_id + +# Block or instance that declared the alarm. Static knowledge of the generator. +# Also the natural handle for placing the alarm under an entity in the tree. +string source + +# Alarm class from the project, e.g. whether the alarm needs acknowledgement. +# Empty when the alarm comes from bits, which carry no class. +string alarm_class + +# Priority as the PLC knows it: 0 to 16, higher is more important. This is the +# scale the plant already engineers with, so we carry it instead of inventing +# one. 0 when unknown. +uint8 priority + +# Where the alarm came from inside the CPU: "user_program", "system_diagnostics", +# "prodiag", "graph", "motion", "security". Empty for bit-filled alarms. +# +# Worth carrying because the CPU message system produces more than user alarms. +# A module fault or a drive alarm arrives here without anyone declaring it. +string producer + +# Whether the condition is currently present. +bool active + +# Whether the alarm has been acknowledged. +# +# Both flags together, not one enum: an alarm that is gone but unacknowledged is +# a real state and stays in the list. Same two bits the CPU alarm system tracks. +# +# Who owns this depends on how the list is filled. Bits: shared, either side can +# set it. CPU alarm system: the CPU owns it, we mirror it, and setting it for a +# single alarm needs the panel or OPC UA, because the CPU has no per-alarm +# acknowledge instruction. +bool acknowledged + +# Time the condition last changed, from the PLC clock, UTC. +# +# The source time, so a consumer can compare it against its own clock and spot a +# PLC that is not time-synchronised. That happens often on plant networks. Empty +# in the bit path, where nobody recorded when it happened. +builtin_interfaces/Time plc_timestamp + +# Values the alarm system latched when the alarm fired. They become the +# freeze-frame of the fault. +# +# The CPU caps these at 512 bytes across all of them. In the bit path there are +# none, and the freeze-frame comes from the process topics instead. +float64[] associated_values diff --git a/src/ros2_medkit_roxsie_msgs/msg/AlarmList.msg b/src/ros2_medkit_roxsie_msgs/msg/AlarmList.msg new file mode 100644 index 000000000..2c5484c5f --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/msg/AlarmList.msg @@ -0,0 +1,43 @@ +# Copyright 2026 mfaferek93 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# AlarmList.msg - every alarm in the PLC that is active or waiting for +# acknowledgement, published cyclically. +# +# A picture of the current state, not a stream of events. Two reasons. +# +# The transport is cyclic: the PLC-side node reads a data block at a fixed rate, +# so a picture is what it naturally has. An event stream would have to be built +# on top of it and would then need its own delivery guarantees. +# +# A picture survives a restart. Whoever restarts - the PLC-side node, the bridge, +# the whole diagnostics stack - the next cycle carries the full truth again. An +# event stream loses whatever happened while it was away, and the consumer has no +# way to tell an empty stream from a healthy machine. This is the same gap we hit +# reading alarms over OPC UA, where no table of pending alarms is served on +# reconnect. +# +# The bridge compares consecutive pictures: an entry that appears is reported as a +# fault, an entry that disappears heals it. + +# When this picture was taken, from the PLC clock. +builtin_interfaces/Time stamp + +# Alarms that are active or not yet acknowledged. Order is not significant. +# +# OPEN QUESTION: how many entries can the generated data block hold? A plant can +# declare hundreds of alarms, and the array is fixed size once generated. If the +# ceiling is low, the fill has to prioritise, and then it also has to say that it +# truncated. +Alarm[] alarms diff --git a/src/ros2_medkit_roxsie_msgs/msg/DiagnosticsStatus.msg b/src/ros2_medkit_roxsie_msgs/msg/DiagnosticsStatus.msg new file mode 100644 index 000000000..2c988105d --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/msg/DiagnosticsStatus.msg @@ -0,0 +1,51 @@ +# Copyright 2026 mfaferek93 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# DiagnosticsStatus.msg - what the diagnostics side asks of the machine. +# +# Written into a consumed data block by the PLC-side node. This is everything we +# put inside the PLC. Faults stay on the ROS 2 side and whoever wants them reads +# them over REST. +# +# It says what we are asking for, not what to display. Machines already have a +# state machine and, where PackML is used, a block that turns machine state into +# signal column bits. We do not drive lamps; we hand the machine a condition its +# own logic can act on. Two writers on one lamp is a fault of its own. + +# Liveness counter of the diagnostics layer, incremented about once per second. +# +# When it stops, a watchdog in the PLC program raises its own alarm. The plant +# then sees that diagnostics are down instead of a green light that means +# nothing. +uint16 heartbeat + +# What we are asking the machine to do about the faults in scope. +# +# The wording follows the colour semantics the signalling standards already use, +# so an integrator can wire it without a translation table. +uint8 condition +uint8 CONDITION_OK = 0 +uint8 CONDITION_ABNORMAL = 1 +uint8 CONDITION_ACTION_REQUIRED = 2 +uint8 CONDITION_STOP = 3 + +# Optional, for machine logic only, one bit per coarse fault class. Meant for an +# interlock that must not depend on reading a fault list. Which fault sets which +# bit is deployment configuration on the ROS 2 side. Zero when unused. +uint16 class_bitmask + +# Which part of the plant this status covers, as an entity in the SOVD tree, e.g. +# an area or a component id. Lets one diagnostics instance serve several cells +# without them seeing each other's faults. +string scope diff --git a/src/ros2_medkit_roxsie_msgs/package.xml b/src/ros2_medkit_roxsie_msgs/package.xml new file mode 100644 index 000000000..7427e848a --- /dev/null +++ b/src/ros2_medkit_roxsie_msgs/package.xml @@ -0,0 +1,26 @@ + + + + ros2_medkit_roxsie_msgs + 0.6.0 + Message definitions for the ROXSIE integration: PLC alarm events into ROS 2 and aggregate diagnostics state back into the PLC + + mfaferek93 + Apache-2.0 + + ament_cmake + rosidl_default_generators + + builtin_interfaces + + rosidl_default_runtime + + ament_lint_auto + ament_lint_common + + rosidl_interface_packages + + + ament_cmake + +