Skip to content

Commit f6cae59

Browse files
authored
Report operational status with mock hardware (#1923)
1 parent 4da09f5 commit f6cae59

2 files changed

Lines changed: 58 additions & 3 deletions

File tree

ur_robot_driver/test/test_mock_hardware.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@
3737
import rclpy
3838
from geometry_msgs.msg import Inertia, Vector3
3939
from rclpy.node import Node
40+
from rclpy.qos import DurabilityPolicy, QoSProfile, ReliabilityPolicy
4041
from control_msgs.action import FollowJointTrajectory
4142
from controller_manager_msgs.srv import SwitchController
43+
from std_msgs.msg import Bool
44+
from ur_dashboard_msgs.msg import RobotMode, SafetyMode
4245

4346
sys.path.append(os.path.dirname(__file__))
4447
from test_common import ( # noqa: E402
@@ -100,6 +103,46 @@ def test_get_robot_software_version(self):
100103
self._configuration_controller_interface.get_robot_software_version().major, 1
101104
)
102105

106+
def test_mock_hardware_publishes_operational_status(self):
107+
"""Mock hardware reports deterministic happy-path status on latched topics."""
108+
messages = {}
109+
qos = QoSProfile(
110+
depth=1,
111+
durability=DurabilityPolicy.TRANSIENT_LOCAL,
112+
reliability=ReliabilityPolicy.RELIABLE,
113+
)
114+
subscriptions = [
115+
self.node.create_subscription(
116+
RobotMode,
117+
"/io_and_status_controller/robot_mode",
118+
lambda msg: messages.setdefault("robot_mode", msg.mode),
119+
qos,
120+
),
121+
self.node.create_subscription(
122+
SafetyMode,
123+
"/io_and_status_controller/safety_mode",
124+
lambda msg: messages.setdefault("safety_mode", msg.mode),
125+
qos,
126+
),
127+
self.node.create_subscription(
128+
Bool,
129+
"/io_and_status_controller/robot_program_running",
130+
lambda msg: messages.setdefault("program_running", msg.data),
131+
qos,
132+
),
133+
]
134+
135+
deadline = time.monotonic() + 10.0
136+
while len(messages) < 3 and time.monotonic() < deadline:
137+
rclpy.spin_once(self.node, timeout_sec=0.1)
138+
139+
self.assertEqual(messages.get("robot_mode"), RobotMode.RUNNING)
140+
self.assertEqual(messages.get("safety_mode"), SafetyMode.NORMAL)
141+
self.assertIs(messages.get("program_running"), True)
142+
143+
for subscription in subscriptions:
144+
self.node.destroy_subscription(subscription)
145+
103146
def test_start_jtc_controller(self):
104147
# Deactivate controller, if it is not already
105148
self.assertTrue(

ur_robot_driver/urdf/ur.ros2_control.xacro

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,21 @@
186186
<state_interface name="tool_analog_input_type_0"/>
187187
<state_interface name="tool_analog_input_type_1"/>
188188

189-
<state_interface name="robot_mode"/>
189+
<state_interface name="robot_mode">
190+
<xacro:if value="${use_mock_hardware}">
191+
<param name="initial_value">7</param>
192+
</xacro:if>
193+
</state_interface>
190194
<state_interface name="robot_status_bit_0"/>
191195
<state_interface name="robot_status_bit_1"/>
192196
<state_interface name="robot_status_bit_2"/>
193197
<state_interface name="robot_status_bit_3"/>
194198

195-
<state_interface name="safety_mode"/>
199+
<state_interface name="safety_mode">
200+
<xacro:if value="${use_mock_hardware}">
201+
<param name="initial_value">1</param>
202+
</xacro:if>
203+
</state_interface>
196204
<state_interface name="safety_status_bit_0"/>
197205
<state_interface name="safety_status_bit_1"/>
198206
<state_interface name="safety_status_bit_2"/>
@@ -205,7 +213,11 @@
205213
<state_interface name="safety_status_bit_9"/>
206214
<state_interface name="safety_status_bit_10"/>
207215

208-
<state_interface name="program_running"/>
216+
<state_interface name="program_running">
217+
<xacro:if value="${use_mock_hardware}">
218+
<param name="initial_value">1</param>
219+
</xacro:if>
220+
</state_interface>
209221
</gpio>
210222

211223
<gpio name="${tf_prefix}payload">

0 commit comments

Comments
 (0)