Bug fiix import patron.xacro and append packages realsense2

This commit is contained in:
Даниил Грабарь
2026-04-14 14:20:27 +03:00
parent 1156c768bb
commit c95c94f67b
233 changed files with 41337 additions and 1 deletions
@@ -0,0 +1,63 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package realsense2_ros_mqtt_bridge
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4.57.4 (2025-11-02)
-------------------
* Update realsense2__ros_mqtt_bridge package.xml to .57.4
* PR `#3441 <https://github.com/IntelRealSense/realsense-ros/issues/3441>`_ from remibettan/ros2-development: merging 4.57.3 to ros2-development
* Merge tag '4.57.3' into ros2-development
* Contributors: Remi Bettan
4.57.3 (2025-09-15)
-------------------
* PR `#3428 <https://github.com/realsenseai/realsense-ros/issues/3428>`_ from remibettan: fixing mqtt readme
* PR `#3417 <https://github.com/realsenseai/realsense-ros/issues/3417>`_ from remibettan: Merging ros2 hkr to ros2 dev final
* PR `#38 <https://github.com/realsenseai/realsense-ros/issues/38>`_ from PrasRsRos: Implement hw reset in mqtt and add test
* PR `#37 <https://github.com/realsenseai/realsense-ros/issues/37>`_ from PrasRsRos: Hmc test update
* PR `#36 <https://github.com/realsenseai/realsense-ros/issues/36>`_ from PrasRsRos: Add hmc command tests to mqtt
* PR `#32 <https://github.com/realsenseai/realsense-ros/issues/32>`_ from SamerKhshiboun: Support HWM command as ROS2 service and in the ROS-MQTT bridge node
* PR `#34 <https://github.com/realsenseai/realsense-ros/issues/34>`_ from PrasRsRos: mqtt tf testcase
* PR `#30 <https://github.com/realsenseai/realsense-ros/issues/30>`_ from SamerKhshiboun: Use new apis of SIC and SP that works directly with JSON inputs/outputs
* PR `#31 <https://github.com/realsenseai/realsense-ros/issues/31>`_ from SamerKhshiboun: Support TF lookup for ROS-MQTT bridge node
* PR `#29 <https://github.com/realsenseai/realsense-ros/issues/29>`_ from PrasRsRos/mqtt_negative__tests
* PR `#28 <https://github.com/realsenseai/realsense-ros/issues/28>`_ from SamerKhshiboun: Fix MQTT Demo and update values for and update TC consecutives
* PR `#26 <https://github.com/realsenseai/realsense-ros/issues/26>`_ from PrasRsRos: More system tests for mqtt
* PR `#24 <https://github.com/realsenseai/realsense-ros/issues/24>`_ from PrasRsRos: Test TC on two cameras in parallel
* PR `#23 <https://github.com/realsenseai/realsense-ros/issues/23>`_ from PrasRsRos: Ros tc implementation
* PR `#22 <https://github.com/realsenseai/realsense-ros/issues/22>`_ from PrasRsRos: Added device info test at the system level
* PR `#17 <https://github.com/realsenseai/realsense-ros/issues/17>`_ from SamerKhshiboun: Change sucess response "true/false" to be boolean
* PR `#19 <https://github.com/realsenseai/realsense-ros/issues/19>`_ from PrasRsRos: DeviceInfo and TC Mqtt tests
* PR `#16 <https://github.com/realsenseai/realsense-ros/issues/16>`_ from PrasRsRos: RS ROS Mqtt bridge unit tests
* PR `#15 <https://github.com/realsenseai/realsense-ros/issues/15>`_ from SamerKhshiboun: Support device info service in ROS-MQTT bridge
* PR `#14 <https://github.com/realsenseai/realsense-ros/issues/14>`_ from SamerKhshiboun: Merge ros2-development into hkr
* PR `#13 <https://github.com/realsenseai/realsense-ros/issues/13>`_ from SamerKhshiboun: Sٍupport set/get application config as ROS service and in ROS-MQTT bridge
* PR `#12 <https://github.com/realsenseai/realsense-ros/issues/12>`_ from SamerKhshiboun: TC Support in ROS-MQTT Bridge Node
* PR `#10 <https://github.com/realsenseai/realsense-ros/issues/10>`_ from SamerKhshiboun: Add ROS MQTT Bridge (Python) Node Into realsense-ros-private
* Contributors: Nir Azkiel, PrasRsRos, Remi Bettan, Samer Khshiboun
* PR `#3428 <https://github.com/realsenseai/realsense-ros/issues/3428>`_ from remibettan: fixing mqtt readme
* readme fixed
* PR `#3417 <https://github.com/realsenseai/realsense-ros/issues/3417>`_ from remibettan: Merging ros2 hkr to ros2 dev final
* PR `#38 <https://github.com/realsenseai/realsense-ros/issues/38>`_ from PrasRsRos: Implement hw reset in mqtt and add test
* PR `#37 <https://github.com/realsenseai/realsense-ros/issues/37>`_ from PrasRsRos: Hmc test update
* PR `#36 <https://github.com/realsenseai/realsense-ros/issues/36>`_ from PrasRsRos: Add hmc command tests to mqtt
* PR `#32 <https://github.com/realsenseai/realsense-ros/issues/32>`_ from SamerKhshiboun: Support HWM command as ROS2 service and in the ROS-MQTT bridge node
* PR `#34 <https://github.com/realsenseai/realsense-ros/issues/34>`_ from PrasRsRos: mqtt tf testcase
* PR `#30 <https://github.com/realsenseai/realsense-ros/issues/30>`_ from SamerKhshiboun: Use new apis of SIC and SP that works directly with JSON inputs/outputs
* PR `#31 <https://github.com/realsenseai/realsense-ros/issues/31>`_ from SamerKhshiboun: Support TF lookup for ROS-MQTT bridge node
* PR `#29 <https://github.com/realsenseai/realsense-ros/issues/29>`_ from PrasRsRos/mqtt_negative__tests
* PR `#28 <https://github.com/realsenseai/realsense-ros/issues/28>`_ from SamerKhshiboun: Fix MQTT Demo and update values for and update TC consecutives failures threshold
* PR `#26 <https://github.com/realsenseai/realsense-ros/issues/26>`_ from PrasRsRos: More system tests for mqtt
* PR `#24 <https://github.com/realsenseai/realsense-ros/issues/24>`_ from PrasRsRos: Test TC on two cameras in parallel
* PR `#23 <https://github.com/realsenseai/realsense-ros/issues/23>`_ from PrasRsRos: Ros tc implementation
* PR `#22 <https://github.com/realsenseai/realsense-ros/issues/22>`_ from PrasRsRos: Added device info test at the system level
* PR `#17 <https://github.com/realsenseai/realsense-ros/issues/17>`_ from SamerKhshiboun: Change sucess response "true/false" to be boolean
* PR `#19 <https://github.com/realsenseai/realsense-ros/issues/19>`_ from PrasRsRos: DeviceInfo and TC Mqtt tests
* PR `#16 <https://github.com/realsenseai/realsense-ros/issues/16>`_ from PrasRsRos: RS ROS Mqtt bridge unit tests
* PR `#15 <https://github.com/realsenseai/realsense-ros/issues/15>`_ from SamerKhshiboun: Support device info service in ROS-MQTT bridge
* PR `#14 <https://github.com/realsenseai/realsense-ros/issues/14>`_ from SamerKhshiboun: Merge ros2-development into hkr
* PR `#13 <https://github.com/realsenseai/realsense-ros/issues/13>`_ from SamerKhshiboun: Sٍupport set/get application config as ROS service and in ROS-MQTT bridge
* PR `#12 <https://github.com/realsenseai/realsense-ros/issues/12>`_ from SamerKhshiboun: TC Support in ROS-MQTT Bridge Node
* PR `#10 <https://github.com/realsenseai/realsense-ros/issues/10>`_ from SamerKhshiboun: Add ROS MQTT Bridge (Python) Node Into realsense-ros-private
* Contributors: Nir Azkiel, PrasRsRos, Remi Bettan, Samer Khshiboun
+808
View File
@@ -0,0 +1,808 @@
<p align="center">
<!-- Light mode -->
<img src="../res/realsense-logo-light-mode.png#gh-light-mode-only" alt="Logo for light mode" width="70%"/>
<!-- Dark mode -->
<img src="../res/realsense-logo-dark-mode.png#gh-dark-mode-only" alt="Logo for dark mode" width="70%"/>
<br><br>
</p>
<h1 align="center">
MQTT <-> ROS bridge for Intel&copy; RealSense&trade; Cameras<br>
</h1>
## Table of contents
- [Installation](#installation)
- [Starting the ros-mqtt-bridge node](#starting-the-ros-mqtt-bridge-node)
- [ros2 run](#ros2-run)
- [ros2 launch](#ros2-launch)
- [Parameters](#parameters)
- [Client Usage](#client-usage)
- [Enumerate Devices](#enumerate-devices)
- [Reset the device](#reset-the-device)
- [Get Device Info](#get-device-info)
- [Get Transformation](#get-transformation)
- [Send HWM Command](#send-hwm-command)
- [Get Parameter](#get-parameter)
- [Set Parameter](#set-parameter)
- [Get Frame](#get-frame)
- [Get Safety Preset](#get-safety-preset)
- [Set Safety Preset](#set-safety-preset)
- [Get Safety Interface Config](#get-safety-interface-config)
- [Set Safety Interface Config](#set-safety-interface-config)
- [Get Calib Config](#get-calib-config)
- [Set Calib Config](#set-calib-config)
- [Get Safety Application Config](#get-application-config)
- [Set Safety Application Config](#set-application-config)
- [Triggered Calibration](#triggered-calibration)
- [Supported Parameters For Set/Get](#supported-parameters-for-setget)
- [Supported Streams](#supported-streams)
- [Usage Example](#usage-example)
# Installation
***This step assumes you have installed ROS environment and ROS Wrapper for Realsense Cameras (including RealSense SDK). For more info about these steps, click [here](https://github.com/realsenseai/realsense-ros/tree/ros2-development?tab=readme-ov-file)***
- Install paho-mqtt from https://pypi.org/project/paho-mqtt/2.1.0/
```
sudo pip3 install paho-mqtt==2.1.0
```
- Create a ROS2 workspace
```bash
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src/
```
- Build
```bash
colcon build
```
- Source environment
```bash
ROS_DISTRO=<YOUR_SYSTEM_ROS_DISTRO> # set your ROS_DISTRO: iron, humble
source /opt/ros/$ROS_DISTRO/setup.bash
cd ~/ros2_ws
./install/local_setup.bash
```
# Starting the ros-mqtt-bridge node
***this step assumes there is a running and configured MQTT broker, and at least one running realsense2_camera node***
### ros2 run
ros2 run realsense2_ros_mqtt_bridge realsense2_ros_mqtt_bridge
# or, with parameters, for example
ros2 run realsense2_ros_mqtt_bridge realsense2_ros_mqtt_bridge --ros-args -p broker_ip:='localhost'
### ros2 launch
ros2 launch realsense2_ros_mqtt_bridge rs_launch.py
# or, with parameters, for example
ros2 launch realsense2_ros_mqtt_bridge rs_launch.py broker_ip:='localhost'
# Parameters
***All parameters can be configured or overriden by the `ros2 run` and `ros2 launch` commands (see examples in above section), or from the `rs_launch.py` file***
- broker_ip
- description: MQTT broker ip address
- default value: 'localhost'
- broker_port
- description: MQTT port
- default value: '1883'
- log_level
- description: log level [DEBUG|INFO|WARN|ERROR|FATAL]
- default value: INFO
# Client Usage
## Enumerate Devices
* mqtt request message example
```
{
"camera_namespace_prefix": "robot",
"camera_names_prefix": "c_"
}
```
* request topic
```
enumrete_devices_request
```
* response topic
```
enumerate_devices_response
```
* mqtt response message example:
```
{
"success": True,
"error_msg": "",
"available_nodes_count": "1",
"available_nodes":
"[
{camera_namespace: robot1, camera_name: c_333622320169}
]"
}
```
## Reset the Device
* mqtt request message example
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
}
```
* request topic
```
send_hw_reset_request
```
* response topic
```
send_hw_reset_response
```
* mqtt response message example:
```
{
"camera_namespace": "camera",
"camera_name": "camera",
"success": true,
"error_msg": ""
}
```
## Get Device Info
* mqtt request message example
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
}
```
* request topic
```
get_device_info_request
```
* response topic
```
get_device_info_response
```
* mqtt response message example:
```
{
"camera_namespace": "camera",
"camera_name": "camera",
"device_name": "intel_realsense_d585s",
"serial_number": "333622320169",
"firmware_version": "8.17.15566.148",
"usb_type_descriptor": "3.2",
"firmware_update_id": "333622320169",
"sensors": "depth_module,rgb_camera,safety_camera,depth_mapping_camera,motion_module",
"physical_port": "/sys/devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.0/video4linux/video0",
}
```
## Get Transformation
* mqtt request message example
```
{
"source": "c_353322320702_link",
"destination": "c_353322320702_color_frame",
}
```
* request topic
```
get_transformation_request
```
* response topic
```
get_transformation_response
```
* mqtt response message example:
```
{
"rotation": {"x": -0.0022762807482196233, "y": -0.0011598517160371544, "z": -0.0011766824618274568, "w": 0.9999960443463445},
"translation": {"x": -4.12423032685183e-05, "y": -0.04789295792579651, "z": 0.0005400447407737374},
"success": true,
"error_msg": ""
}
```
## Send HWM Command
* mqtt request message example (read of safety interface config table)
```
{
"camera_namespace": "robot1",
"camera_name": "c_353322320702",
"opcode": 167, # opcode of GET_HKR_CONFIG_TABLE 0xA7
"param1": 1, # read from flash (1)
"param2": 49372, # table id (safety interface config) 0xC0DC
"param3": 1, # 0 dynamic, 1 gold
"param4": 0, # unused (ChunkId)
"data": []
}
```
* request topic
```
send_hwm_command_request
```
* response topic
```
send_hwm_command_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_353322320702",
"success": true,
"result": [167, 0, 0, 0, 0, 5, 220, 192, 148, 0, 0, 0, 0, 0, 0, 0, 250, 239, 161, 49, 0, 1, 1, 3, 1, 2, 0, 12, 0, 13, 0, 14, 0, 9, 0, 8, 0, 16, 0, 17, 0, 19, 0, 18, 0, 11, 1, 20, 0, 10, 0, 15, 0, 0, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 128, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 61, 138, 62, 20, 0, 12, 6, 4, 100, 0, 20, 100, 0, 20, 10, 15, 10, 95, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"error_msg": ""
}
```
## Get Parameter
***See [available parameters and their types](#supported-parameters-for-setget)***
* mqtt request message example
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"parameter_name": "rgb_camera.exposure"
}
```
* request topic
```
get_param_request
```
* response topic
```
get_param_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"parameter_name": "rgb_camera.exposure",
"success": True,
"error_msg": "",
"parameter_type": "integer",
"parameter_value": "6012"
}
```
## Set Parameter
***See [available parameters and their types](#supported-parameters-for-setget)***
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"parameter_name": "rgb_camera.exposure"
"parameter_value": "6012",
"parameter_type": "int"
}
```
* request topic
```
set_param_request
```
* response topic
```
set_param_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": ""
}
```
## Get Frame
***See [Supported Streams](#supported-streams)***
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"stream_name": "color"
}
```
* request topic:
```
get_frame_request
```
* response topic:
```
get_frame_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"stream_name": "color"
"success": True,
"error_msg": "",
"frame": "[0, 118, 124, 0, 0, ...., 255]" # array of bytes
}
```
## Get Safety Preset
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"index": "1"
}
```
* request topic:
```
get_safety_preset_request
```
* response topic:
```
get_safety_preset_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": "",
"safety_preset": "{safety preset as json}"
}
```
## Set Safety Preset
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"safety_preset": "{safety preset as json}"
"index": "1"
}
```
* request topic:
```
set_safety_preset_request
```
* response topic:
```
set_safety_preset_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": "",
}
```
## Get Safety Interface Config
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
}
```
* request topic:
```
get_safety_interface_config_request
```
* response topic:
```
get_safety_interface_config_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"safety_inteface_config": "{safety interface config as JSON}",
"success": True,
"error_msg": "",
}
```
## Set Safety Interface Config
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"safety_inteface_config": "{safety interface config as JSON}",
}
```
* request topic:
```
set_safety_interface_config_request
```
* response topic:
```
set_safety_interface_config_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": "",
}
```
## Get Calib Config
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
}
```
* request topic:
```
get_calib_config_request
```
* response topic:
```
get_calib_config_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"calib_config": "{calib config as JSON}",
"success": True,
"error_msg": "",
}
```
## Set Calib Config
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"calib_config": "{calib config as JSON}"
}
```
* request topic:
```
set_calib_config_request
```
* response topic:
```
set_calib_config_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": "",
}
```
## Get Application Config
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
}
```
* request topic:
```
get_application_config_request
```
* response topic:
```
get_application_config_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"application_config": "{application config as JSON}",
"success": True,
"error_msg": "",
}
```
## Set Application Config
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"application_config": "{application config as JSON}"
}
```
* request topic:
```
set_application_config_request
```
* response topic:
```
set_application_config_response
```
* mqtt response message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": "",
}
```
## Triggered Calibration
* Before calling triggered calibration, user should set the following parameters:
* `safety_camera.safety_mode: 2` # switch to service mode
* `depth_module.visual_preset: 1` # switch to visual preset #1 in depth module
* `depth_module.emitter_enabled: true` # enable emitter in depth module
* `depth_module.enable_auto_exposure: true` # enable AE in depth moudle
* `enable_depth: false` # turn off depth stream
* `enable_infra1: false` # turn off infra1 stream
* `enable_infra2: false` # turn off infra2 stream
* `enable_safety: false` # turn off safety stream
* `enable_labeled_point_cloud: false` # turn off labeled pointcloud stream
* `enable_occupancy: false` # turn off occupancy stream
* mqtt request message example:
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
}
```
* request topic:
```
triggered_calibration_request
```
* response topic:
```
triggered_calibration_response
```
* mqtt response message example: (user will get messages on the `triggered_calibration_response` on every progress update)
```
{
"camera_namespace": "robot1",
"camera_name": "c_333622320169",
"success": True,
"error_msg": "",
"calibration": {calibration table as json}
"health": 3.0
"progress": 100.0 # [%0...%100]
}
```
# Supported Parameters For Set/Get
```
accel_fps (type: integer)
accel_info_qos (type: string)
accel_qos (type: string)
align_depth.enable (type: boolean)
align_depth.frames_queue_size (type: integer)
angular_velocity_cov (type: double)
base_frame_id (type: string)
camera_name (type: string)
clip_distance (type: double)
color_info_qos (type: string)
color_qos (type: string)
colorizer.color_scheme (type: integer)
colorizer.enable (type: boolean)
colorizer.frames_queue_size (type: integer)
colorizer.histogram_equalization_enabled (type: boolean)
colorizer.max_distance (type: double)
colorizer.min_distance (type: double)
colorizer.stream_filter (type: integer)
colorizer.stream_format_filter (type: integer)
colorizer.stream_index_filter (type: integer)
colorizer.visual_preset (type: integer)
decimation_filter.enable (type: boolean)
decimation_filter.filter_magnitude (type: integer)
decimation_filter.frames_queue_size (type: integer)
decimation_filter.stream_filter (type: integer)
decimation_filter.stream_format_filter (type: integer)
decimation_filter.stream_index_filter (type: integer)
depth_info_qos (type: string)
depth_mapping_camera.frames_queue_size (type: integer)
depth_mapping_camera.global_time_enabled (type: boolean)
depth_mapping_camera.labeled_point_cloud_format (type: string)
depth_mapping_camera.labeled_point_cloud_profile (type: string)
depth_mapping_camera.occupancy_format (type: string)
depth_mapping_camera.occupancy_profile (type: string)
depth_module.auto_exposure_roi.bottom (type: integer)
depth_module.auto_exposure_roi.left (type: integer)
depth_module.auto_exposure_roi.right (type: integer)
depth_module.auto_exposure_roi.top (type: integer)
depth_module.depth_format (type: string)
depth_module.depth_profile (type: string)
depth_module.emitter_always_on (type: boolean)
depth_module.emitter_enabled (type: boolean)
depth_module.enable_auto_exposure (type: boolean)
depth_module.error_polling_enabled (type: boolean)
depth_module.exposure (type: integer)
depth_module.frames_queue_size (type: integer)
depth_module.gain (type: integer)
depth_module.global_time_enabled (type: boolean)
depth_module.infra1_format (type: string)
depth_module.infra2_format (type: string)
depth_module.infra_profile (type: string)
depth_module.inter_cam_sync_mode (type: integer)
depth_module.laser_power (type: double)
depth_module.visual_preset (type: integer)
depth_qos (type: string)
device_type (type: string)
diagnostics_period (type: double)
disparity_filter.enable (type: boolean)
disparity_to_depth.enable (type: boolean)
enable_accel (type: boolean)
enable_color (type: boolean)
enable_depth (type: boolean)
enable_gyro (type: boolean)
enable_infra1 (type: boolean)
enable_infra2 (type: boolean)
enable_labeled_point_cloud (type: boolean)
enable_occupancy (type: boolean)
enable_rgbd (type: boolean)
enable_safety (type: boolean)
enable_sync (type: boolean)
filter_by_sequence_id.enable (type: boolean)
filter_by_sequence_id.frames_queue_size (type: integer)
filter_by_sequence_id.sequence_id (type: integer)
gyro_fps (type: integer)
gyro_info_qos (type: string)
gyro_qos (type: string)
hdr_merge.enable (type: boolean)
hdr_merge.frames_queue_size (type: integer)
hold_back_imu_for_frames (type: boolean)
hole_filling_filter.enable (type: boolean)
hole_filling_filter.frames_queue_size (type: integer)
hole_filling_filter.holes_fill (type: integer)
hole_filling_filter.stream_filter (type: integer)
hole_filling_filter.stream_format_filter (type: integer)
hole_filling_filter.stream_index_filter (type: integer)
infra1_info_qos (type: string)
infra1_qos (type: string)
infra2_info_qos (type: string)
infra2_qos (type: string)
initial_reset (type: boolean)
json_file_path (type: string)
labeled_point_cloud_info_qos (type: string)
labeled_point_cloud_qos (type: string)
linear_accel_cov (type: double)
motion_module.enable_motion_correction (type: boolean)
motion_module.frames_queue_size (type: integer)
motion_module.global_time_enabled (type: boolean)
occupancy_info_qos (type: string)
occupancy_qos (type: string)
pointcloud.allow_no_texture_points (type: boolean)
pointcloud.enable (type: boolean)
pointcloud.filter_magnitude (type: integer)
pointcloud.frames_queue_size (type: integer)
pointcloud.ordered_pc (type: boolean)
pointcloud.pointcloud_qos (type: string)
pointcloud.stream_filter (type: integer)
pointcloud.stream_format_filter (type: integer)
pointcloud.stream_index_filter (type: integer)
publish_tf (type: boolean)
reconnect_timeout (type: double)
rgb_camera.auto_exposure_priority (type: boolean)
rgb_camera.auto_exposure_roi.bottom (type: integer)
rgb_camera.auto_exposure_roi.left (type: integer)
rgb_camera.auto_exposure_roi.right (type: integer)
rgb_camera.auto_exposure_roi.top (type: integer)
rgb_camera.brightness (type: integer)
rgb_camera.color_format (type: string)
rgb_camera.color_profile (type: string)
rgb_camera.contrast (type: integer)
rgb_camera.enable_auto_exposure (type: boolean)
rgb_camera.enable_auto_white_balance (type: boolean)
rgb_camera.exposure (type: integer)
rgb_camera.frames_queue_size (type: integer)
rgb_camera.gain (type: integer)
rgb_camera.gamma (type: integer)
rgb_camera.global_time_enabled (type: boolean)
rgb_camera.hue (type: integer)
rgb_camera.power_line_frequency (type: integer)
rgb_camera.saturation (type: integer)
rgb_camera.sharpness (type: integer)
rgb_camera.white_balance (type: double)
rosbag_filename (type: string)
safety_camera.frames_queue_size (type: integer)
safety_camera.global_time_enabled (type: boolean)
safety_camera.safety_format (type: string)
safety_camera.safety_mode (type: integer)
safety_camera.safety_preset_active_index (type: integer)
safety_camera.safety_profile (type: string)
safety_info_qos (type: string)
safety_qos (type: string)
serial_no (type: string)
spatial_filter.enable (type: boolean)
spatial_filter.filter_magnitude (type: integer)
spatial_filter.filter_smooth_alpha (type: double)
spatial_filter.filter_smooth_delta (type: integer)
spatial_filter.frames_queue_size (type: integer)
spatial_filter.holes_fill (type: integer)
spatial_filter.stream_filter (type: integer)
spatial_filter.stream_format_filter (type: integer)
spatial_filter.stream_index_filter (type: integer)
temporal_filter.enable (type: boolean)
temporal_filter.filter_smooth_alpha (type: double)
temporal_filter.filter_smooth_delta (type: integer)
temporal_filter.frames_queue_size (type: integer)
temporal_filter.holes_fill (type: integer)
temporal_filter.stream_filter (type: integer)
temporal_filter.stream_format_filter (type: integer)
temporal_filter.stream_index_filter (type: integer)
tf_publish_rate (type: double)
unite_imu_method (type: integer)
usb_port_id (type: string)
use_sim_time (type: boolean)
wait_for_device_timeout (type: double)
```
# Supported Streams
- color
- depth
- infra1 (Left IR)
- infra2 (Right IR)
# Usage Example
[Minimal Python MQTT Client Example](examples/minimal_mqtt_client.py)
[jazzy-badge]: https://img.shields.io/badge/-JAZZY-orange?style=flat-square&logo=ros
[jazzy]: https://docs.ros.org/en/jazzy/index.html
[humble-badge]: https://img.shields.io/badge/-HUMBLE-orange?style=flat-square&logo=ros
[humble]: https://docs.ros.org/en/humble/index.html
[iron-badge]: https://img.shields.io/badge/-IRON-orange?style=flat-square&logo=ros
[iron]: https://docs.ros.org/en/iron/index.html
[ubuntu24-badge]: https://img.shields.io/badge/-UBUNTU%2024%2E04-blue?style=flat-square&logo=ubuntu&logoColor=white
[ubuntu24]: https://releases.ubuntu.com/noble/
[ubuntu22-badge]: https://img.shields.io/badge/-UBUNTU%2022%2E04-blue?style=flat-square&logo=ubuntu&logoColor=white
[ubuntu22]: https://releases.ubuntu.com/jammy/
@@ -0,0 +1,695 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""Main module for interacting with the MQTT client."""
import json
import random
import time
import numpy as np
from paho.mqtt import client as paho_mqtt_client
class DemoMQTTClient:
"""
Class that acts as Demo MQTT Client.
This class provides methods for initializing the MQTT client, handling
MQTT connections, publishing messages, and interacting with MQTT topics.
Attributes:
mqtt_broker_ip: The IP address of the MQTT broker.
mqtt_broker_port: The port number of the MQTT broker.
mqtt_client: The MQTT client instance.
"""
def __init__(self, mqtt_broker_ip, mqtt_broker_port):
"""
Initialize the Demo MQTT client.
Args:
mqtt_broker_ip: The IP address of the MQTT broker.
mqtt_broker_port: The port number of the MQTT broker.
"""
self.mqtt_broker_ip = mqtt_broker_ip
self.mqtt_broker_port = mqtt_broker_port
# Generate a Client ID with a random user-id suffix.
mqtt_client_id = f'mqtt-client-user-{random.randint(0, 1000)}'
self.mqtt_client = paho_mqtt_client.Client(
paho_mqtt_client.CallbackAPIVersion.VERSION1, mqtt_client_id)
# client.username_pw_set(username, password)
self.mqtt_client.on_connect = self.on_connect
self.mqtt_client.connect(mqtt_broker_ip, mqtt_broker_port)
self.tc_done = False
def on_connect(self, client, userdata, flags, rc):
"""
Handle the MQTT connection event.
Args:
client: The MQTT client instance.
userdata: The user data associated with the connection.
flags: The connection flags.
rc: The result code of the connection attempt.
"""
del client, userdata, flags # delete unused params
if rc == 0:
print(f'Connected to MQTT Broker on'
f' ip:{ self.mqtt_broker_ip} port:{self.mqtt_broker_port}')
else:
print(f'Could not Connect to MQTT Broker on'
f' ip:{ self.mqtt_broker_ip} port:{self.mqtt_broker_port}'
f' return_code: {rc}')
def publish(self, msg, topic):
"""
Publish a message to the specified MQTT topic.
Args:
msg: The message to publish.
topic: The MQTT topic to publish to.
"""
msg_count = 1
while True:
time.sleep(1)
result = self.mqtt_client.publish(topic, msg, qos=2)
# result: [0, 1]
status = result[0]
if status == 0:
print(f'Send {msg} to topic {topic}')
else:
print(f'Failed to send message to topic {topic}')
msg_count += 1
if msg_count > 1:
break
def on_message(self, client, userdata, msg):
"""
Handle the MQTT message event.
Args:
client: The MQTT client instance.
userdata: The user data associated with the message.
msg: The received MQTT message.
"""
del client, userdata # delete unused params
content = msg.payload.decode('utf-8')
if msg.topic == 'get_frame_response':
print('got image... \
not printing it since its raw data is huge...')
else:
if msg.topic == 'triggered_calibration_response':
tc_json = json.loads(content)
if tc_json['progress'] == 100.0:
self.tc_done = True
print(f'Received {content} to topic {msg.topic}')
self.locked = False;
def start_client(self):
"""Start the MQTT client."""
self.mqtt_client.loop_start()
self.mqtt_client.subscribe('enumerate_devices_response')
self.mqtt_client.subscribe('get_transformation_response')
self.mqtt_client.subscribe('send_hw_reset_response')
self.mqtt_client.subscribe('send_hwm_command_response')
self.mqtt_client.subscribe('get_device_info_response')
self.mqtt_client.subscribe('get_parameter_response')
self.mqtt_client.subscribe('set_parameter_response')
self.mqtt_client.subscribe('get_frame_response')
self.mqtt_client.subscribe('get_safety_preset_response')
self.mqtt_client.subscribe('set_safety_preset_response')
self.mqtt_client.subscribe('get_safety_interface_config_response')
self.mqtt_client.subscribe('set_safety_interface_config_response')
self.mqtt_client.subscribe('get_calib_config_response')
self.mqtt_client.subscribe('set_calib_config_response')
self.mqtt_client.subscribe('get_application_config_response')
self.mqtt_client.subscribe('set_application_config_response')
self.mqtt_client.subscribe('triggered_calibration_response')
self.mqtt_client.on_message = self.on_message
def stop_client(self):
"""Stop the MQTT client."""
self.mqtt_client.loop_stop()
def enumerate_devices(self, camera_namespace_prefix, camera_name_prefix):
"""
Send a request to enumerate devices.
Args:
camera_namespace_prefix: The prefix of the camera namespace.
camera_name_prefix: The prefix of the camera name.
"""
request_dict = {
'camera_namespace_prefix': camera_namespace_prefix,
'camera_name_prefix': camera_name_prefix
}
j = json.dumps(request_dict)
self.publish(j, 'enumerate_devices_request')
def get_transformation(self, source, destination):
"""
Send a request to find the ROS2 transformation from source frame to destination frame
Args:
source: source frame id.
destination: destination frame id
"""
request_dict = {
'source': source,
'destination': destination
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_transformation_request')
while self.locked:
pass
def send_hw_reset_request(self, camera_namespace, camera_name):
"""
Send a request to reset the device
Args:
camera_namespace
camera_name
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'send_hw_reset_request')
while self.locked:
pass
def send_hwm_command(self, camera_namespace, camera_name, opcode,
param1 = 0, param2 = 0, param3 = 0, param4 = 0,
data = []):
"""
Send a hwm command request
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'opcode' : opcode,
'param1' : param1,
'param2' : param2,
'param3' : param3,
'param4' : param4,
'data': np.array(data).tolist()
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'send_hwm_command_request')
while self.locked:
pass
def get_device_info(self, camera_namespace, camera_name):
"""
Send a request to get device info.
Args:
camera_namespace: The namespace of the camera.
camera_name: The name of the camera.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_device_info_request')
while self.locked:
pass
def set_param(self, camera_namespace, camera_name,
parameter_name, parameter_value, parameter_type):
"""
Send a request to set a parameter.
Args:
camera_namespace: The namespace of the camera.
camera_name: The name of the camera.
parameter_name: The name of the parameter to set.
parameter_value: The value to set for the parameter.
parameter_type: The type of the parameter.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'parameter_name': parameter_name,
'parameter_value': parameter_value,
'parameter_type': parameter_type
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'set_param_request')
while self.locked:
pass
def get_param(self, camera_namespace, camera_name, parameter_name):
"""
Send a request to get a parameter.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
parameter_name (str): The name of the parameter to get.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'parameter_name': parameter_name,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_param_request')
while self.locked:
pass
def get_frame(self, camera_namespace, camera_name, stream_name):
"""
Send a request to get a frame.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
stream_name (str): The name of the stream.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'stream_name': stream_name,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_frame_request')
while self.locked:
pass
def get_safety_preset(self, camera_namespace, camera_name, index):
"""
Send a request to get a safety preset.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
index (int): The index of the safety preset.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'index': index,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_safety_preset_request')
while self.locked:
pass
def set_safety_preset(self, camera_namespace, camera_name, sp, index):
"""
Send a request to set a safety preset.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
sp (str): The safety preset.
index (int): The index of the safety preset.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'safety_preset': sp,
'index': str(index),
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'set_safety_preset_request')
while self.locked:
pass
def get_safety_interface_config(self, camera_namespace, camera_name, index=2):
"""
Send a request to get a safety inteface config.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'calib_location': index
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_safety_interface_config_request')
while self.locked:
pass
def set_safety_interface_config(self, camera_namespace, camera_name, sic):
"""
Send a request to set a safety interface config.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
sic (str): The safety interface config.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'safety_interface_config': sic,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'set_safety_interface_config_request')
while self.locked:
pass
def get_calib_config(self, camera_namespace, camera_name):
"""
Send a request to get a calib config.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_calib_config_request')
while self.locked:
pass
def set_calib_config(self, camera_namespace, camera_name, calib_config):
"""
Send a request to set a calib config.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
calib_config (str): The calib config.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'calib_config': calib_config,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'set_calib_config_request')
while self.locked:
pass
def get_application_config(self, camera_namespace, camera_name):
"""
Send a request to get an application config.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'get_application_config_request')
while self.locked:
pass
def set_application_config(self, camera_namespace, camera_name, application_config):
"""
Send a request to set an application config.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
application_config (str): The application config.
"""
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'application_config': application_config,
}
j = json.dumps(request_dict)
self.locked = True
self.publish(j, 'set_application_config_request')
while self.locked:
pass
def triggered_calibration(self, camera_namespace, camera_name):
"""
Run triggered calibration action.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
"""
self.tc_done = False
request_dict = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'json': 'calib run'
}
j = json.dumps(request_dict)
self.publish(j, 'triggered_calibration_request')
if __name__ == '__main__':
MQTT_BROKER_IP = 'localhost'
MQTT_BROKER_PORT = 1883
jsons_dir = '../../realsense2_camera/examples/d500_tables/'
demo_mqtt_client = DemoMQTTClient(MQTT_BROKER_IP, MQTT_BROKER_PORT)
demo_mqtt_client.start_client()
CAMERA_NAMESPACE_PREFIX = 'robot'
CAMERA_NAME_PREFIX = 'c_'
# enumerate devices
demo_mqtt_client.enumerate_devices(CAMERA_NAMESPACE_PREFIX,
CAMERA_NAME_PREFIX)
# choose specific camera
CAMERA_NAMESPACE = 'robot1'
CAMERA_NAME = 'c_353322320702'
# reset the device
demo_mqtt_client.send_hw_reset_request(CAMERA_NAMESPACE,
CAMERA_NAME)
#needs time to reset
import time
time.sleep(8)
# get device info
demo_mqtt_client.get_device_info(CAMERA_NAMESPACE,
CAMERA_NAME)
# get ROS2 transformation from 'c_353322320702_link' to 'c_353322320702_color_frame'
demo_mqtt_client.get_transformation('c_353322320702_link', 'c_353322320702_color_frame')
# send raw HWM command like GVD
demo_mqtt_client.send_hwm_command(camera_namespace= CAMERA_NAMESPACE,
camera_name = CAMERA_NAME,
opcode = 0x10)
# switch to service mode
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'safety_camera.safety_mode',
'2',
'int')
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'rgb_camera.exposure',
'6012',
'int')
demo_mqtt_client.get_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'rgb_camera.exposure')
demo_mqtt_client.get_frame(CAMERA_NAMESPACE,
CAMERA_NAME,
'color')
###################################################################
################ SAFETY PRESET GET/SET EXAMPLE ####################
demo_mqtt_client.get_safety_preset(CAMERA_NAMESPACE,
CAMERA_NAME,
1)
safety_preset_file = open(jsons_dir + 'safety_preset_example.json',
mode='r',
encoding='utf-8')
safety_preset_json = json.load(safety_preset_file)
SP_ESCAPED = str(safety_preset_json).replace('"', '\"')
SP_ESCAPED = str(safety_preset_json).replace("'", '\"')
demo_mqtt_client.set_safety_preset(CAMERA_NAMESPACE,
CAMERA_NAME,
SP_ESCAPED,
61)
###################################################################
########### SAFETY INTERFACE CONFIG GET/SET EXAMPLE ###############
demo_mqtt_client.get_safety_interface_config(CAMERA_NAMESPACE,
CAMERA_NAME)
safety_interface_config_file = open(jsons_dir + 'safety_interface_config_example.json',
mode='r',
encoding='utf-8')
safety_interface_config_json = json.load(safety_interface_config_file)
SIC_ESCAPED = str(safety_interface_config_json).replace('"', '\"')
SIC_ESCAPED = str(safety_interface_config_json).replace("'", '\"')
demo_mqtt_client.set_safety_interface_config(CAMERA_NAMESPACE,
CAMERA_NAME,
SIC_ESCAPED)
###################################################################
############## CALIB CONFIG GET/SET EXAMPLE #######################
demo_mqtt_client.get_calib_config(CAMERA_NAMESPACE,
CAMERA_NAME)
calib_config_file = open(jsons_dir + 'calib_config_example.json',
mode='r',
encoding='utf-8')
calib_config_json = json.load(calib_config_file)
CALIB_CONFIG_ESCAPED = str(calib_config_json).replace('"', '\"')
CALIB_CONFIG_ESCAPED = str(calib_config_json).replace("'", '\"')
demo_mqtt_client.set_calib_config(CAMERA_NAMESPACE,
CAMERA_NAME,
CALIB_CONFIG_ESCAPED)
##################################################################
########## APPLICATION CONFIG GET/SET EXAMPLE ####################
demo_mqtt_client.get_application_config(CAMERA_NAMESPACE,
CAMERA_NAME)
application_config_file = open(jsons_dir + 'application_config_example.json',
mode='r',
encoding='utf-8')
application_config_json = json.load(application_config_file)
APPLICATION_CONFIG_ESCAPED = str(application_config_json).replace('"', '\"')
APPLICATION_CONFIG_ESCAPED = str(application_config_json).replace("'", '\"')
demo_mqtt_client.set_application_config(CAMERA_NAMESPACE,
CAMERA_NAME,
APPLICATION_CONFIG_ESCAPED)
###################################################################
############## TRIGGERED CALIBRATION EXAMPLE ######################
# setup params for triggered calibration
# we are already in service mode, no need to switch
# switch to visual preset #1
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'depth_module.visual_preset',
'1',
'int')
# enable emitter
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'depth_module.emitter_enabled',
'true',
'bool')
# enable auto exposure
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'depth_module.enable_auto_exposure',
'true',
'bool')
# turn off depth streaming
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'enable_depth',
'false',
'bool')
# turn off infra1 streaming
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'enable_infra1',
'false',
'bool')
# turn off infra2 streaming
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'enable_infra2',
'false',
'bool')
# turn off safety streaming
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'enable_safety',
'false',
'bool')
# turn off occupancy streaming
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'enable_occupancy',
'false',
'bool')
# turn off lpcl streaming
demo_mqtt_client.set_param(CAMERA_NAMESPACE,
CAMERA_NAME,
'enable_labeled_point_cloud',
'false',
'bool')
# call the triggered calibration method
demo_mqtt_client.triggered_calibration(CAMERA_NAMESPACE,
CAMERA_NAME)
# check if TC is done, otherwise sleep for 2 seconds
while not demo_mqtt_client.tc_done:
time.sleep(2)
###################################################################
demo_mqtt_client.stop_client()
print(f'MQTT example completed, stopping the client and exiting')
@@ -0,0 +1,57 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""Launch realsense2_ros_mqtt_bridge node"""
import os
import yaml
from launch import LaunchDescription
import launch_ros.actions
from launch.actions import DeclareLaunchArgument, OpaqueFunction
from launch.substitutions import LaunchConfiguration
configurable_parameters = [
{'name': 'broker_ip', 'default': 'localhost', 'description': 'MQTT broker ip'},
{'name': 'broker_port', 'default': '1883', 'description': 'MQTT port'},
{'name': 'log_level', 'default': 'info', 'description': 'debug log level [DEBUG|INFO|WARN|ERROR|FATAL]'},
]
def declare_configurable_parameters(parameters):
return [DeclareLaunchArgument(param['name'], default_value=param['default'],
description=param['description']) for param in parameters]
def set_configurable_parameters(parameters):
return dict([(param['name'], LaunchConfiguration(param['name'])) for param in parameters])
def yaml_to_dict(path_to_yaml):
with open(path_to_yaml, "r") as f:
return yaml.load(f, Loader=yaml.SafeLoader)
def launch_setup(context, params):
return [
launch_ros.actions.Node(
package='realsense2_ros_mqtt_bridge',
executable='realsense2_ros_mqtt_bridge',
parameters=[params],
output='screen',
arguments=['--ros-args', '--log-level', LaunchConfiguration('log_level')],
emulate_tty=True,
)
]
def generate_launch_description():
return LaunchDescription(declare_configurable_parameters(configurable_parameters) + [
OpaqueFunction(function=launch_setup, kwargs = {'params' : set_configurable_parameters(configurable_parameters)})
])
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>realsense2_ros_mqtt_bridge</name>
<version>4.57.7</version>
<description>ROS-MQTT Brdige for realsense2_camera node</description>
<maintainer email="rsswsdk@realsensecloud.onmicrosoft.com">LibRealSense ROS Team</maintainer>
<maintainer email="nir.azkiel@realsenseai.com">Nir Azkiel</maintainer>
<license>Apache-2.0</license>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
+126
View File
@@ -0,0 +1,126 @@
#!/bin/bash
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
# This script makes sure all files with the following extensions - .h, .hpp, .cpp, .js, .py, .bat, .sh, .txt -
# include the Apache license reference and Intel copyright, as it shown in this file header.
# The script checks also that these files are using Unix line-endings and spaces as delmiters (instead of tabs).
# It is recommended to run this script when adding new files to the project.
# For more info, run ./pr_check.sh --help
set +e
sudo apt-get install dos2unix
ok=0
fixed=0
license_file=$PWD/../LICENSE
year_format="[[:digit:]][[:digit:]][[:digit:]][[:digit:]]"
function check_folder {
for filename in $(find $1 -type f \( -iname \*.cpp -o -iname \*.h -o -iname \*.hpp -o -iname \*.js -o -iname \*.bat -o -iname \*.sh -o -iname \*.txt -o -iname \*.py \)); do
# Skip files of 3rd-party libraries which already have their own licenses and copyrights
if [[ "$filename" == *"pr_check.sh"* ]]; then
continue;
fi
if [[ $(grep -oP "Software License Agreement" $filename | wc -l) -ne 0 ]]; then
echo "[WARNING] $filename contains 3rd-party license agreement"
else
if [[ ! $filename == *"usbhost"* ]]; then
# Only check files that are not .gitignore-d
if [[ $(git check-ignore $filename | wc -l) -eq 0 ]]; then
if [[ $(grep -oP "Copyright $year_format RealSense, Inc. All Rights Reserved" $filename | wc -l) -eq 0 &&
$(grep -oP "Copyright $year_format-$year_format RealSense, Inc. All Rights Reserved" $filename | wc -l) -eq 0 ||
$(grep -oP "Licensed under the Apache License, Version 2.0" $filename | wc -l) -eq 0
]]; then
echo "[ERROR] $filename is missing the copyright/license notice"
ok=$((ok+1))
if [[ $2 == *"fix"* ]]; then
# take last 13 linse from LICENSE file, and put them at beginning of $filename
if [[ $filename == *".h"* || $filename == *".hpp"* || $filename == *".cpp"* || $filename == *".js"* ]]; then
license_str="$(tail -13 ${license_file} | sed -e 's/^ / /' | sed -e 's/^/\/\//')"
fi
if [[ $filename == *".txt"* || $filename == *".py"* ]]; then
license_str="$(tail -13 ${license_file} | sed -e 's/^ / /' | sed -e 's/^/#/')"
fi
echo "Trying to auto-resolve...";
ed -s $filename << END
0i
${license_str}
.
w
q
END
fixed=$((fixed+1))
fi
fi
if [[ $(grep -o -P '\t' $filename | wc -l) -ne 0 ]]; then
echo "[ERROR] $filename has tabs (this project is using spaces as delimiters)"
ok=$((ok+1))
if [[ $2 == *"fix"* ]]; then
echo "Trying to auto-resolve...";
sed -i.bak $'s/\t/ /g' $filename
fixed=$((fixed+1))
fi
fi
if [[ $(file ${filename} | grep -o -P 'CRLF' | wc -l) -ne 0 ]]; then
echo "[ERROR] $filename is using DOS line endings (this project is using Unix line-endings)"
ok=$((ok+1))
if [[ $2 == *"fix"* ]]; then
echo "Trying to auto-resolve...";
dos2unix $filename
fixed=$((fixed+1))
fi
fi
fi
fi
fi
done
}
if [[ $1 == *"help"* ]]; then
echo Pull-Request Check tool
echo "Usage: (run from repo scripts directory)"
echo " ./pr_check.sh [--help] [--fix]"
echo " --fix Try to auto-fix defects"
exit 0
fi
cd ..
check_folder . $1
cd scripts
if [[ ${fixed} -ne 0 ]]; then
echo "Re-running pr_check..."
./pr_check.sh
else
if [[ ${ok} -ne 0 ]]; then
echo Pull-Request check failed, please address ${ok} the errors reported above
exit 1
fi
fi
exit 0
+4
View File
@@ -0,0 +1,4 @@
[develop]
script_dir=$base/lib/realsense2_ros_mqtt_bridge
[install]
install_scripts=$base/lib/realsense2_ros_mqtt_bridge
+42
View File
@@ -0,0 +1,42 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
from glob import glob
import os
from setuptools import find_packages, setup
package_name = 'realsense2_ros_mqtt_bridge'
setup(
name=package_name,
version='4.57.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), glob('launch/*.py')),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Samer Khshiboun',
maintainer_email='samer.khshiboun@intel.com',
description='MQTT <-> ROS brdige for realsense2_camera node',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'realsense2_ros_mqtt_bridge = src.entry_point:main'
],
},
)
@@ -0,0 +1,13 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
@@ -0,0 +1,95 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""docstring."""
import json
from realsense2_camera_msgs.srv import ApplicationConfigRead
from realsense2_camera_msgs.srv import ApplicationConfigWrite
from .service_handler import ServiceHandler
class ApplicationConfigHandler(ServiceHandler):
"""docstring."""
def __init__(self, mqtt_ros_node):
"""docstring."""
super().__init__(mqtt_ros_node)
def handle_get_application_config_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG('get_application_config_request \
message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
service_name = f'/{camera_namespace}/{camera_name}/application_config_read'
ros_client_application_config_read = self.create_ros_client(ApplicationConfigRead, service_name)
if not ros_client_application_config_read:
return
ros_request = ApplicationConfigRead.Request()
future = ros_client_application_config_read.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'application_config': ros_response.application_config,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('get_application_config_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_application_config_response message sent')
ros_client_application_config_read.destroy()
def handle_set_application_config_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG(
'set_application_config_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
application_config = mqtt_request['application_config']
service_name = f'/{camera_namespace}/{camera_name}/application_config_write'
ros_client_application_config_write = self.create_ros_client(ApplicationConfigWrite, service_name)
if not ros_client_application_config_write:
return
ros_request = ApplicationConfigWrite.Request()
ros_request.application_config = application_config
future = ros_client_application_config_write.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('set_application_config_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('set_application_config_response message sent')
ros_client_application_config_write.destroy()
@@ -0,0 +1,95 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""docstring."""
import json
from realsense2_camera_msgs.srv import CalibConfigRead
from realsense2_camera_msgs.srv import CalibConfigWrite
from .service_handler import ServiceHandler
class CalibConfigHandler(ServiceHandler):
"""docstring."""
def __init__(self, mqtt_ros_node):
"""docstring."""
super().__init__(mqtt_ros_node)
def handle_get_calib_config_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG('get_calib_config_request \
message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
service_name = f'/{camera_namespace}/{camera_name}/calib_config_read'
ros_client_calib_config_read = self.create_ros_client(CalibConfigRead, service_name)
if not ros_client_calib_config_read:
return
ros_request = CalibConfigRead.Request()
future = ros_client_calib_config_read.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'calib_config': ros_response.calib_config,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('get_calib_config_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_calib_config_response message sent')
ros_client_calib_config_read.destroy()
def handle_set_calib_config_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG(
'set_calib_config_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
calib_config = mqtt_request['calib_config']
service_name = f'/{camera_namespace}/{camera_name}/calib_config_write'
ros_client_calib_config_write = self.create_ros_client(CalibConfigWrite, service_name)
if not ros_client_calib_config_write:
return
ros_request = CalibConfigWrite.Request()
ros_request.calib_config = calib_config
future = ros_client_calib_config_write.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('set_calib_config_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('set_calib_config_response message sent')
ros_client_calib_config_write.destroy()
@@ -0,0 +1,137 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""DeviceHandler for handling device-related MQTT requests."""
import json
from .service_handler import ServiceHandler
from realsense2_camera_msgs.srv import DeviceInfo
class DeviceHandler(ServiceHandler):
"""
Handler for device-related MQTT requests.
This class processes MQTT requests related to enumerating devices and
retrieves available nodes that match the specified namespace and name
prefixes.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the DeviceHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
def get_available_nodes(self, camera_namespace_prefix, camera_name_prefix):
"""
Get available nodes that match the given namespace and name prefixes.
Args:
camera_namespace_prefix (str): Prefix for the camera namespace.
camera_name_prefix (str): Prefix for the camera name.
Returns:
tuple: A tuple containing the count of available nodes and a string
representation of the available nodes.
"""
available_nodes_count = 0
available_nodes_str = ''
available_nodes = self.mqtt_ros_node.get_node_names_and_namespaces()
for node_name, node_namespace in available_nodes:
if node_namespace[1:].startswith(camera_namespace_prefix) and\
node_name.startswith(camera_name_prefix):
available_nodes_count += 1
available_nodes_str += '{camera_namespace: ' +\
node_namespace[1:] + \
', camera_name: ' + node_name + '}, '
if available_nodes_count:
available_nodes_str = available_nodes_str[:-2]
return available_nodes_count, available_nodes_str
def handle_enumerate_devices_request(self, mqtt_request):
"""
Handle the enumerate devices MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace and name prefixes.
"""
self.mqtt_ros_node.ROS_DEBUG('enumerate_devices_request \
message received')
camera_namespace_prefix = mqtt_request['camera_namespace_prefix']
camera_name_prefix = mqtt_request['camera_name_prefix']
nodes_count, nodes_str = self.get_available_nodes(
camera_namespace_prefix,
camera_name_prefix)
mqtt_response = {
'success': True,
'error_msg': '',
'available_nodes_count': str(nodes_count),
'available_nodes': '[' + nodes_str + ']'
}
self.mqtt_ros_node.mqtt_client.publish('enumerate_devices_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('enumerate_devices_response message sent')
def handle_get_device_info_request(self, mqtt_request):
"""
Handle the get device info MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace and camera name of the device.
"""
self.mqtt_ros_node.ROS_DEBUG('get_device_info message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
service_name = f'/{camera_namespace}/{camera_name}/device_info'
ros_client_get_device_info = self.create_ros_client(DeviceInfo, service_name)
if not ros_client_get_device_info:
return
ros_request = DeviceInfo.Request()
future = ros_client_get_device_info.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'device_name' : ros_response.device_name,
'serial_number': ros_response.serial_number,
'firmware_version': ros_response.firmware_version,
'usb_type_descriptor': ros_response.usb_type_descriptor,
'firmware_update_id': ros_response.firmware_update_id,
'sensors': ros_response.sensors,
'physical_port': ros_response.physical_port
}
self.mqtt_ros_node.mqtt_client.publish('get_device_info_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_device_info_response message sent')
ros_client_get_device_info.destroy()
@@ -0,0 +1,44 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""Main module for running the MQTT <-> ROS bridge node."""
import rclpy
from .mqtt_bridge_node import MQTTBridgeNode
def main(args=None):
"""
Initialize and run the MQTTBridgeNode.
This function initializes the ROS2 system, creates an instance of the
MQTTBridgeNode, spins the node, and shuts down the ROS2 system when the
node is finished.
Args:
args: Command-line arguments (default is None).
"""
try:
rclpy.init(args=args)
mqtt_bridge_node = MQTTBridgeNode()
rclpy.spin(mqtt_bridge_node)
mqtt_bridge_node.destroy_node()
rclpy.shutdown()
except Exception as e:
print(e)
print('Retry launching the node.')
exit(1)
if __name__ == '__main__':
main()
@@ -0,0 +1,135 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""FrameHandler for handling frame-related MQTT requests."""
import json
from functools import partial
from rclpy.qos import QoSDurabilityPolicy
from rclpy.qos import QoSHistoryPolicy
from rclpy.qos import QoSProfile
from rclpy.qos import QoSReliabilityPolicy
from sensor_msgs.msg import Image
class FrameHandler:
"""
Handler for frame-related MQTT requests.
This class processes MQTT requests related to getting frames from a camera
and retrieves the corresponding image frames.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the FrameHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
self.image = None
self.topic_handle = {}
# Create a QoS profile for sensor data
self.sensor_data_qos_profile = QoSProfile(
history=QoSHistoryPolicy.KEEP_LAST,
depth=10,
reliability=QoSReliabilityPolicy.BEST_EFFORT,
durability=QoSDurabilityPolicy.VOLATILE)
def handle_get_frame_request(self, mqtt_request):
"""
Handle the get frame MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace, camera name, and stream name.
"""
self.mqtt_ros_node.ROS_DEBUG('get_frame_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
stream_name = mqtt_request['stream_name']
topic_name = f'{camera_namespace}/{camera_name}/{stream_name}'
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'stream_name': stream_name,
'success': True,
'error_msg': '',
'frame': ''
}
if stream_name not in ['color', 'depth', 'infra1', 'infra2']:
error_msg = f'Unsupported stream type: {stream_name}'
self.mqtt_ros_node.ROS_ERROR(error_msg)
mqtt_response['success'] = False
mqtt_response['error_msg'] = error_msg
self.mqtt_ros_node.mqtt_client.publish('get_frame_response',
json.dumps(mqtt_response),
qos=1)
return
if stream_name == 'depth' or\
stream_name == 'infra1' or\
stream_name == 'infra2':
topic_name += '/image_rect_raw'
elif stream_name == 'color':
topic_name += '/image_raw'
else:
self.mqtt_ros_node.ROS_ERROR('Unsupported stream')
return
if topic_name in self.topic_handle and self.topic_handle[topic_name] is not None:
error_msg = f'One get_frame request is already in progress for {stream_name}, rejecting'
self.mqtt_ros_node.ROS_ERROR(error_msg)
mqtt_response['success'] = False
mqtt_response['error_msg'] = error_msg
self.mqtt_ros_node.mqtt_client.publish('get_frame_response',
json.dumps(mqtt_response),
qos=1)
return
self.topic_handle[topic_name] = self.mqtt_ros_node.create_subscription(Image,
topic_name,
partial(self.image_callback,mqtt_response=mqtt_response,topic_name=topic_name),
self.sensor_data_qos_profile)
def image_callback(self, msg,mqtt_response,topic_name):
"""
Image subscription callback.
Args:
msg (Image): The received image message.
"""
if self.topic_handle[topic_name] is None:
self.mqtt_ros_node.ROS_INFO(f'Ignoring the frame received for stream '+ mqtt_response['stream_name'])
return
status = self.mqtt_ros_node.destroy_subscription(self.topic_handle[topic_name])
if status == False:
self.mqtt_ros_node.ROS_ERROR("Failed to destroy subscription")
self.topic_handle[topic_name] = None
self.image = msg
self.mqtt_ros_node.ROS_INFO(f'Frame received successfully for stream '+ mqtt_response['stream_name'])
mqtt_response['frame'] = str(self.image)
self.mqtt_ros_node.mqtt_client.publish('get_frame_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_frame_response message sent')
@@ -0,0 +1,74 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""HwResetHandler for handling device-related MQTT requests."""
import json
import numpy as np
from .service_handler import ServiceHandler
from std_srvs.srv import Empty
class HwResetHandler(ServiceHandler):
"""
Handler for hardware reset MQTT request.
This class processes MQTT requests related to sending hardware reset request to the device.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the HwResetHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
def handle_hw_reset_send_request(self, mqtt_request):
"""
Handle the get device info MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace and camera name of the device and the hwm command to run
"""
self.mqtt_ros_node.ROS_DEBUG('send_hw_reset_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
service_name = f'/{camera_namespace}/{camera_name}/hw_reset'
ros_client_hw_reset_command_send = self.create_ros_client(Empty, service_name)
if not ros_client_hw_reset_command_send:
return
ros_request = Empty.Request()
future = ros_client_hw_reset_command_send.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
print("Pras", str(ros_response))
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success' : True,
'error_msg': ""
}
self.mqtt_ros_node.mqtt_client.publish('send_hw_reset_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('send_hw_reset_response message sent')
ros_client_hw_reset_command_send.destroy()
@@ -0,0 +1,80 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""HWMCommandHandler for handling device-related MQTT requests."""
import json
import numpy as np
from .service_handler import ServiceHandler
from realsense2_camera_msgs.srv import HardwareMonitorCommandSend
class HWMCommandHandler(ServiceHandler):
"""
Handler for HWM command-related MQTT requests.
This class processes MQTT requests related to sending hardware monitor commands to the device.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the HWMCommandHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
def handle_hwm_command_send_request(self, mqtt_request):
"""
Handle the get device info MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace and camera name of the device and the hwm command to run
"""
self.mqtt_ros_node.ROS_DEBUG('send_hwm_command_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
service_name = f'/{camera_namespace}/{camera_name}/hardware_monitor_command_send'
ros_client_hwm_command_send = self.create_ros_client(HardwareMonitorCommandSend, service_name)
if not ros_client_hwm_command_send:
return
ros_request = HardwareMonitorCommandSend.Request()
# ros_request
ros_request.opcode = mqtt_request['opcode']
ros_request.param1 = mqtt_request.get('param1', 0)
ros_request.param2 = mqtt_request.get('param2', 0)
ros_request.param3 = mqtt_request.get('param3', 0)
ros_request.param4 = mqtt_request.get('param4', 0)
ros_request.data = np.array(mqtt_request.get('data', [])).tolist()
future = ros_client_hwm_command_send.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success' : ros_response.success,
'result': np.array(ros_response.result).tolist(),
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('send_hwm_command_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('hwm_command_send_response message sent')
ros_client_hwm_command_send.destroy()
@@ -0,0 +1,356 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""docstring."""
import json
import random
import paho.mqtt.client as mqtt
from rclpy.node import Node
from .device_handler import DeviceHandler
from .frame_handler import FrameHandler
from .parameter_handler import ParameterHandler
from .safety_preset_handler import SafetyPresetHandler
from .safety_interface_config_handler import SafetyInterfaceConfigHandler
from .calib_config_handler import CalibConfigHandler
from .application_config_handler import ApplicationConfigHandler
from .hw_reset_handler import HwResetHandler
from .hwm_command_handler import HWMCommandHandler
from .triggered_calibration_handler import TriggeredCalibrationHandler
from .transformation_handler import TransformationHandler
class MQTTBridgeNode(Node):
"""
MQTT <-> ROS brdige node.
MQTTBridgeNode is a ROS Node that acts as a MQTT <-> ROS brdige for
RealSense cameras. The node listens to pre-defined MQTT Broker
messages, translates them into a ROS ones, handling these messages against
the realsense2_camera node and then sending the response back to the
MQTT Broker
"""
def __init__(self,
default_mqtt_broker_ip='localhost',
default_mqtt_broker_port=1883):
"""
Initialize the MQTTBridgeNode.
Args:
default_mqtt_broker_ip (str): Default MQTT broker IP address.
default_mqtt_broker_port (int): Default MQTT broker port.
"""
super().__init__('realsense_ros_mqtt_bridge_node')
self.setup_node_parameters(default_mqtt_broker_ip, default_mqtt_broker_port)
self.setup_mqtt_topics()
self.setup_mqtt_handlers()
self.setup_mqtt_connection()
def setup_node_parameters(self, default_mqtt_broker_ip, default_mqtt_broker_port):
"""
Declare and set node parameters.
Args:
default_mqtt_broker_ip (str): Default MQTT broker IP address.
default_mqtt_port (int): Default MQTT broker port.
"""
try:
self.declare_parameter('broker_ip', default_mqtt_broker_ip)
self.broker_ip = str(self.get_parameter(
'broker_ip').get_parameter_value().string_value)
self.declare_parameter('broker_port', default_mqtt_broker_port)
self.broker_port = self.get_parameter(
'broker_port').get_parameter_value().integer_value
except Exception as e:
print(f'An unexpected error occurred: {e}')
def setup_mqtt_connection(self):
"""Create MQTT client and connect it to MQTT broker."""
self.mqtt_client_id = (f'realsense_ros_mqtt_bridge_node-'
f'{random.randint(0, 1000)}')
self.mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1,
self.mqtt_client_id)
self.mqtt_client.on_connect = self.on_mqtt_connect
self.mqtt_client.on_message = self.on_mqtt_message
try:
self.ROS_INFO(f'Trying to connect to MQTT broker at '
f'ip:{self.broker_ip} port:{self.broker_port}')
self.mqtt_client.connect(self.broker_ip, self.broker_port)
self.mqtt_client.loop_start()
self.ROS_INFO('Successfully connected to MQTT Broker')
except Exception as e:
self.ROS_ERROR(f'Cannot connect to Broker ip:\
{self.broker_ip} port:{self.broker_port}')
self.ROS_ERROR(f'An exception occurred: {e}')
raise e
def setup_mqtt_topics(self):
"""Define the topics our MQTTBridgeNode needs to listen to."""
self.mqtt_requests_topics = [
'enumerate_devices_request',
'send_hw_reset_request',
'send_hwm_command_request',
'get_device_info_request',
'get_transformation_request',
'get_param_request',
'set_param_request',
'get_frame_request',
'get_safety_preset_request',
'set_safety_preset_request',
'get_safety_interface_config_request',
'set_safety_interface_config_request',
'get_calib_config_request',
'set_calib_config_request',
'get_application_config_request',
'set_application_config_request',
'hwm_command_send_request',
'triggered_calibration_request'
]
def setup_mqtt_handlers(self):
"""
Create handlers for each supported MQTT request type.
e.g., device_handler will handle enumerate device MQTT requests,
which arrive on the 'enumerate_devices_request' topic
"""
self.device_handler = DeviceHandler(self)
self.transformation_handler = TransformationHandler(self)
self.parameter_handler = ParameterHandler(self)
self.frame_handler = FrameHandler(self)
self.safety_preset_handler = SafetyPresetHandler(self)
self.safety_interface_config_handler = SafetyInterfaceConfigHandler(self)
self.calib_config_handler = CalibConfigHandler(self)
self.application_config_handler = ApplicationConfigHandler(self)
self.hw_reset_handler = HwResetHandler(self)
self.hwm_command_handler = HWMCommandHandler(self)
self.triggered_calibration_handler = TriggeredCalibrationHandler(self)
def ROS_INFO(self, msg): # pylint: disable=invalid-name
"""
Log an info message.
Args:
msg (str): Message to log.
"""
self.get_logger().info(msg)
def ROS_DEBUG(self, msg): # pylint: disable=invalid-name
"""
Log a debug message.
Args:
msg (str): Message to log.
"""
self.get_logger().debug(msg)
def ROS_WARN(self, msg): # pylint: disable=invalid-name
"""
Log a warning message.
Args:
msg (str): Message to log.
"""
self.get_logger().warn(msg)
def ROS_ERROR(self, msg): # pylint: disable=invalid-name
"""
Log an error message.
Args:
msg (str): Message to log.
"""
self.get_logger().error(msg)
def on_mqtt_connect(self, client, userdata, flags, rc):
"""
Callback for when the MQTT client connects to the broker.
Args:
client: The MQTT client instance.
userdata: User-defined data of any type that is
passed to the callbacks.
flags: Response flags sent by the broker.
rc: The connection result.
"""
del client, userdata, flags, rc # delete unsed params
self.ROS_DEBUG('on_mqtt_connect')
self.ROS_INFO('Trying to subscribe to requested MQTT topics')
for topic in self.mqtt_requests_topics:
self.mqtt_client.subscribe(topic)
self.ROS_INFO('Successfully subscribed to requested MQTT topics')
self.ROS_INFO('Waiting for new incoming MQTT requets')
def on_mqtt_message(self, client, userdata, msg):
"""
Callback for when a PUBLISH message is received from the server.
Args:
client: The MQTT client instance.
userdata: User-defined data of any type that is passed
to the callbacks.
msg: An instance of MQTTMessage, which has members
topic, payload, qos, retain.
"""
del client, userdata # delete unsed params
decoded_msg = msg.payload.decode('utf-8')
self.ROS_INFO(f'Received new MQTT request on topic:'
f'{msg.topic} msg:{decoded_msg}')
mqtt_request = json.loads(decoded_msg)
err_msg = ''
response_topic = ''
if msg.topic == 'enumerate_devices_request':
if 'camera_namespace_prefix' not in mqtt_request:
err_msg = "camera_namespace_prefix not found in the mqtt request"
elif 'camera_name_prefix' not in mqtt_request:
err_msg = "camera_name_prefix not found in the mqtt request"
elif msg.topic != 'get_transformation_request':
if 'camera_namespace' not in mqtt_request:
err_msg = "camera_namespace not found in the mqtt request"
elif 'camera_name' not in mqtt_request:
err_msg = "camera_name not found in the mqtt request"
if msg.topic == 'get_param_request':
response_topic = 'get_parameter_response'
if msg.topic == 'set_param_request':
response_topic = 'set_parameter_response'
if err_msg == '':
if msg.topic == 'enumerate_devices_request':
self.device_handler.handle_enumerate_devices_request(mqtt_request)
elif msg.topic == 'get_transformation_request':
if 'source' not in mqtt_request:
err_msg = "source not found in the mqtt request"
elif 'destination' not in mqtt_request:
err_msg = "destination not found in the mqtt request"
else:
self.transformation_handler.handle_get_transformation_request(mqtt_request)
elif msg.topic == 'send_hw_reset_request':
self.hw_reset_handler.handle_hw_reset_send_request(mqtt_request)
elif msg.topic == 'send_hwm_command_request':
if 'opcode' not in mqtt_request:
err_msg = "opcode not found in the mqtt request"
else:
self.hwm_command_handler.handle_hwm_command_send_request(mqtt_request)
elif msg.topic == 'get_device_info_request':
self.device_handler.handle_get_device_info_request(mqtt_request)
elif msg.topic == 'get_param_request':
if 'parameter_name' not in mqtt_request:
err_msg = "parameter_name not found in the mqtt request"
response_topic = 'get_parameter_response'
else:
self.parameter_handler.handle_get_param_request(mqtt_request)
elif msg.topic == 'set_param_request':
response_topic = 'set_parameter_response'
if 'parameter_name' not in mqtt_request:
err_msg = "parameter_name not found in the mqtt request"
elif 'parameter_value' not in mqtt_request:
err_msg = "parameter_value not found in the mqtt request"
elif 'parameter_type' not in mqtt_request:
err_msg = "parameter_type not found in the mqtt request"
else:
self.parameter_handler.handle_set_param_request(mqtt_request)
elif msg.topic == 'get_frame_request':
if 'stream_name' not in mqtt_request:
err_msg = "stream_name not found in the mqtt request"
else:
self.frame_handler.handle_get_frame_request(mqtt_request)
elif msg.topic == 'get_safety_preset_request':
if 'index' not in mqtt_request:
err_msg = "index not found in the mqtt request"
else:
self.safety_preset_handler.handle_get_safety_preset_request(
mqtt_request)
elif msg.topic == 'set_safety_preset_request':
if 'index' not in mqtt_request:
err_msg = "index not found in the mqtt request"
elif 'safety_preset' not in mqtt_request:
err_msg = "preset not found in the mqtt request"
else:
self.safety_preset_handler.handle_set_safety_preset_request(
mqtt_request)
elif msg.topic == 'get_safety_interface_config_request':
if 'calib_location' not in mqtt_request:
mqtt_request['calib_location'] = 2
self.ROS_WARN('calib_location was not found in MQTT request, using the default value 2')
self.safety_interface_config_handler.handle_get_safety_interface_config_request(
mqtt_request)
elif msg.topic == 'set_safety_interface_config_request':
if 'safety_interface_config' not in mqtt_request:
err_msg = "safety_interface_config not found in the mqtt request"
else:
self.safety_interface_config_handler.handle_set_safety_interface_config_request(
mqtt_request)
elif msg.topic == 'get_calib_config_request':
self.calib_config_handler.handle_get_calib_config_request(
mqtt_request)
elif msg.topic == 'set_calib_config_request':
if 'calib_config' not in mqtt_request:
err_msg = "calib_config not found in the mqtt request"
else:
self.calib_config_handler.handle_set_calib_config_request(
mqtt_request)
elif msg.topic == 'get_application_config_request':
self.application_config_handler.handle_get_application_config_request(
mqtt_request)
elif msg.topic == 'set_application_config_request':
if 'application_config' not in mqtt_request:
err_msg = "application_config not found in the mqtt request"
else:
self.application_config_handler.handle_set_application_config_request(
mqtt_request)
elif msg.topic == 'triggered_calibration_request':
#default is 'calib run'
if 'json' not in mqtt_request:
mqtt_request['json'] = 'calib run'
self.triggered_calibration_handler.handle_triggered_calibration_request(
mqtt_request)
else:
err_msg = 'Unsupported MQTT Message'
if err_msg == '':
self.ROS_INFO('MQTT request handling done')
self.ROS_INFO('Waiting for new incoming MQTT requets')
else:
mqtt_response = mqtt_request
mqtt_response['success'] = False
mqtt_response['error_msg'] = err_msg
if response_topic == '':
response_topic = msg.topic.replace('request', 'response')
self.mqtt_client.publish(response_topic,
json.dumps(mqtt_response),
qos=1)
self.ROS_ERROR(err_msg + f' Response in {response_topic}:{mqtt_response}')
def wait_for_service(self, client, service_name, timeout=5.0):
"""
Wait for a ROS service to become available.
Args:
client: The service client instance.
service_name (str): The name of the service to wait for.
timeout (float): Time to wait for the service to
become available.
Returns:
bool: True if the service is available, False otherwise.
"""
if not client.wait_for_service(timeout):
self.ROS_ERROR(f'Service not available: {service_name}')
return False
self.ROS_DEBUG(f'Service available: {service_name}')
return True
@@ -0,0 +1,221 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""ParameterHandler for handling parameter-related MQTT requests."""
import json
from rcl_interfaces.msg import Parameter
from rcl_interfaces.msg import ParameterType
from rcl_interfaces.msg import ParameterValue
from rcl_interfaces.srv import GetParameters
from rcl_interfaces.srv import SetParameters
from .service_handler import ServiceHandler
class ParameterHandler(ServiceHandler):
"""
Handler for parameter-related MQTT requests.
This class processes MQTT requests related to setting and getting parameters
for a camera, and interacts with ROS services to handle these requests.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the ParameterHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
self.parameter_type_map = {
'float': ParameterType.PARAMETER_DOUBLE,
'int': ParameterType.PARAMETER_INTEGER,
'integer': ParameterType.PARAMETER_INTEGER,
'string': ParameterType.PARAMETER_STRING,
'bool': ParameterType.PARAMETER_BOOL,
'boolean': ParameterType.PARAMETER_BOOL
}
self.type_map = {
ParameterType.PARAMETER_BOOL: 'bool',
ParameterType.PARAMETER_INTEGER: 'integer',
ParameterType.PARAMETER_DOUBLE: 'double',
ParameterType.PARAMETER_STRING: 'string'
}
def handle_set_param_request(self, mqtt_request):
"""
Handle the set parameter MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace, camera name, parameter name, parameter value,
and parameter type.
"""
self.mqtt_ros_node.ROS_DEBUG('set_param_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
parameter_name = mqtt_request['parameter_name']
parameter_value = mqtt_request['parameter_value']
parameter_type = mqtt_request['parameter_type'].lower()
service_name = f'/{camera_namespace}/{camera_name}/set_parameters'
ros_client_set_parameter = self.create_ros_client(SetParameters, service_name)
if not ros_client_set_parameter:
return
parameter_value_obj = self.create_parameter_value(parameter_type, parameter_value)
if not parameter_value_obj:
self.mqtt_ros_node.ROS_ERROR(f'unsupported parameter_type: {parameter_type}')
return
set_parameter_request = SetParameters.Request()
set_parameter_request.parameters.append(
Parameter(name=parameter_name, value=parameter_value_obj))
future = ros_client_set_parameter.call_async(set_parameter_request)
self.wait_for_future(future)
mqtt_response = self.create_set_param_response(camera_namespace, camera_name, future.result())
self.mqtt_ros_node.mqtt_client.publish('set_parameter_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('set_param_response message sent')
ros_client_set_parameter.destroy()
def handle_get_param_request(self, mqtt_request):
"""
Handle the get parameter MQTT request.
Args:
mqtt_request (dict): The MQTT request message containing the
camera namespace, camera name, and parameter name.
"""
self.mqtt_ros_node.ROS_DEBUG('get_param_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
parameter_name = mqtt_request['parameter_name']
service_name = f'/{camera_namespace}/{camera_name}/get_parameters'
ros_client_get_parameter = self.create_ros_client(GetParameters, service_name)
if not ros_client_get_parameter:
return
ros_request = GetParameters.Request()
ros_request.names.append(parameter_name)
future = ros_client_get_parameter.call_async(ros_request)
self.wait_for_future(future)
mqtt_response = self.create_get_param_response(camera_namespace, camera_name, parameter_name, future.result())
self.mqtt_ros_node.mqtt_client.publish('get_parameter_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_parameter_response message sent')
ros_client_get_parameter.destroy()
def create_parameter_value(self, parameter_type, parameter_value):
"""
Create a ParameterValue object based on the type and value.
Args:
parameter_type (str): The type of the parameter.
parameter_value: The value of the parameter.
Returns:
A ParameterValue object.
"""
if parameter_type in self.parameter_type_map:
p = ParameterValue(type=self.parameter_type_map[parameter_type])
if parameter_type == 'float':
p.double_value = float(parameter_value)
elif parameter_type in ['int', 'integer']:
p.integer_value = int(parameter_value)
elif parameter_type == 'string':
p.string_value = parameter_value
elif parameter_type == 'bool':
p.bool_value = parameter_value.lower() == 'true'
return p
return None
def create_set_param_response(self, camera_namespace, camera_name, ros_response):
"""
Create a response for the set parameter request.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
ros_response: The ROS response.
Returns:
A dictionary representing the MQTT response.
"""
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name
}
result = ros_response.results[0]
if result.successful:
self.mqtt_ros_node.ROS_DEBUG('Parameter set successfully')
mqtt_response['success'] = True
mqtt_response['error_msg'] = ''
else:
self.mqtt_ros_node.ROS_WARN('Failed to set parameter')
mqtt_response['success'] = False
mqtt_response['error_msg'] = result.reason
return mqtt_response
def create_get_param_response(self, camera_namespace, camera_name, parameter_name, ros_response):
"""
Create a response for the get parameter request.
Args:
camera_namespace (str): The namespace of the camera.
camera_name (str): The name of the camera.
parameter_name (str): The name of the parameter.
ros_response: The ROS response.
Returns:
A dictionary representing the MQTT response.
"""
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'parameter_name': parameter_name,
'success': True,
'error_msg': ''
}
result = ros_response.values[0]
result_type = self.type_map.get(result.type, 'unsupported')
if result_type != 'unsupported':
self.mqtt_ros_node.ROS_DEBUG('Parameter retrieved successfully')
mqtt_response['parameter_type'] = result_type
mqtt_response['parameter_value'] = str(
getattr(result, f'{result_type}_value'))
else:
self.mqtt_ros_node.ROS_WARN('Failed to retrieve parameter')
mqtt_response['success'] = False
mqtt_response['error_msg'] = 'unsupported type'
return mqtt_response
@@ -0,0 +1,94 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""docstring."""
import json
from realsense2_camera_msgs.srv import SafetyInterfaceConfigRead
from realsense2_camera_msgs.srv import SafetyInterfaceConfigWrite
from .service_handler import ServiceHandler
class SafetyInterfaceConfigHandler(ServiceHandler):
"""docstring."""
def __init__(self, mqtt_ros_node):
"""docstring."""
super().__init__(mqtt_ros_node)
def handle_get_safety_interface_config_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG('get_safety_interface_config_request \
message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
service_name = f'/{camera_namespace}/{camera_name}/safety_interface_config_read'
ros_client_safety_interface_config_read = self.create_ros_client(SafetyInterfaceConfigRead, service_name)
if not ros_client_safety_interface_config_read:
return
ros_request = SafetyInterfaceConfigRead.Request(calib_location=mqtt_request['calib_location'])
future = ros_client_safety_interface_config_read.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'safety_interface_config': ros_response.safety_interface_config,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('get_safety_interface_config_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_safety_interface_config_response message sent')
ros_client_safety_interface_config_read.destroy()
def handle_set_safety_interface_config_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG(
'set_safety_interface_config_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
safety_interface_config = mqtt_request['safety_interface_config']
service_name = f'/{camera_namespace}/{camera_name}/safety_interface_config_write'
ros_client_safety_interface_config_write = self.create_ros_client(SafetyInterfaceConfigWrite, service_name)
if not ros_client_safety_interface_config_write:
return
ros_request = SafetyInterfaceConfigWrite.Request()
ros_request.safety_interface_config = safety_interface_config
future = ros_client_safety_interface_config_write.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('set_safety_interface_config_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('set_safety_interface_config_response message sent')
ros_client_safety_interface_config_write.destroy()
@@ -0,0 +1,99 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""docstring."""
import json
from realsense2_camera_msgs.srv import SafetyPresetRead
from realsense2_camera_msgs.srv import SafetyPresetWrite
from .service_handler import ServiceHandler
class SafetyPresetHandler(ServiceHandler):
"""docstring."""
def __init__(self, mqtt_ros_node):
"""docstring."""
super().__init__(mqtt_ros_node)
def handle_get_safety_preset_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG('get_safety_preset_request \
message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
index = mqtt_request['index']
service_name = f'/{camera_namespace}/{camera_name}/safety_preset_read'
ros_client_safety_preset_read = self.create_ros_client(SafetyPresetRead, service_name)
if not ros_client_safety_preset_read:
return
ros_request = SafetyPresetRead.Request()
ros_request.index = int(index)
future = ros_client_safety_preset_read.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'safety_preset': ros_response.safety_preset,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('get_safety_preset_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_safety_preset_response message sent')
ros_client_safety_preset_read.destroy()
def handle_set_safety_preset_request(self, mqtt_request):
"""docstring."""
self.mqtt_ros_node.ROS_DEBUG(
'set_safety_preset_request message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
index = mqtt_request['index']
preset = mqtt_request['safety_preset']
service_name = f'/{camera_namespace}/{camera_name}/safety_preset_write'
ros_client_safety_preset_write = self.create_ros_client(SafetyPresetWrite, service_name)
if not ros_client_safety_preset_write:
return
ros_request = SafetyPresetWrite.Request()
ros_request.index = int(index)
ros_request.safety_preset = preset
future = ros_client_safety_preset_write.call_async(ros_request)
self.wait_for_future(future)
ros_response = future.result()
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success': ros_response.success,
'error_msg': ros_response.error_message
}
self.mqtt_ros_node.mqtt_client.publish('set_safety_preset_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('set_safety_preset_response message sent')
ros_client_safety_preset_write.destroy()
@@ -0,0 +1,64 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""Handler for managing ROS service clients and futures."""
import time
class ServiceHandler:
"""
Handler for managing ROS service clients and futures.
This class provides methods for creating ROS service clients and waiting
for futures to complete.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the ServiceHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
def create_ros_client(self, srv_type, service_name):
"""
Create a ROS service client and wait for the service to be available.
Args:
srv_type: The type of the ROS service.
service_name: The name of the ROS service.
Returns:
The ROS service client if available, otherwise None.
"""
try:
ros_client = self.mqtt_ros_node.create_client(srv_type, service_name)
if not self.mqtt_ros_node.wait_for_service(ros_client, service_name):
return None
return ros_client
except Exception as e:
self.mqtt_ros_node.ROS_ERROR(f'[create_ros_client] An exception occurred: {e}')
return None
def wait_for_future(self, future):
"""
Wait for a future to complete.
Args:
future: The future to wait for.
"""
while not future.done():
time.sleep(0.5)
self.mqtt_ros_node.ROS_DEBUG('Waiting for ROS service call to finish...')
@@ -0,0 +1,84 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
"""Handler for managing ROS service clients and futures."""
import tf2_ros
from rclpy.time import Time
import json
class TransformationHandler:
"""
Handler for managing ROS service clients and futures.
This class provides methods for creating ROS service clients and waiting
for futures to complete.
"""
def __init__(self, mqtt_ros_node):
"""
Initialize the ServiceHandler.
Args:
mqtt_ros_node: Instance of the MQTTBridgeNode.
"""
self.mqtt_ros_node = mqtt_ros_node
# Initialize transform listener
self.mqtt_ros_node.tf_buffer = tf2_ros.Buffer()
self.tf_listener = tf2_ros.TransformListener(self.mqtt_ros_node.tf_buffer, self.mqtt_ros_node)
def handle_get_transformation_request(self, mqtt_request):
self.mqtt_ros_node.ROS_DEBUG('get_transformation_request message received')
source = mqtt_request['source']
destination = mqtt_request['destination']
try:
# get transform from 'base_link' to 'child_link'
transform_stamped = self.mqtt_ros_node.tf_buffer.lookup_transform(source, destination, Time())
translation_dict = {
"x": transform_stamped.transform.translation.x,
"y": transform_stamped.transform.translation.y,
"z": transform_stamped.transform.translation.z
}
rotation_dict = {
"x": transform_stamped.transform.rotation.x,
"y": transform_stamped.transform.rotation.y,
"z": transform_stamped.transform.rotation.z,
"w": transform_stamped.transform.rotation.w
}
self.mqtt_ros_node.get_logger().debug(f'Transform found: {transform_stamped.transform}')
mqtt_response = {
'rotation': rotation_dict,
'translation': translation_dict,
'success': True,
'error_msg': ''
}
except Exception as e:
self.mqtt_ros_node.get_logger().error(f'Could not get transform: {e}')
mqtt_response = {
'rotation': '{}',
'translation': '{}',
'success': False,
'error_msg': f'Could not get transform: {e}'
}
self.mqtt_ros_node.mqtt_client.publish('get_transformation_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('get_transformation_response message sent')
@@ -0,0 +1,142 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import json
from functools import partial
from rclpy.action import ActionClient
from realsense2_camera_msgs.action import TriggeredCalibration
class TriggeredCalibrationHandler:
"""Brdige For Triggered Calibration ROS Action."""
def __init__(self, mqtt_ros_node):
self.mqtt_ros_node = mqtt_ros_node
self.action_clients = {}
self.goal_handles = {}
def get_action_client(self, camera_namespace, camera_name):
client_name = f"{camera_namespace}_{camera_name}"
if client_name in self.action_clients:
return self.action_clients[client_name]
return None
def set_action_client(self, camera_namespace, camera_name, client):
client_name = f"{camera_namespace}_{camera_name}"
self.action_clients[client_name] = client
#assumption is that there is only one goal handle per action client.
def get_goal_handle(self, camera_namespace, camera_name):
client_name = f"{camera_namespace}_{camera_name}"
if client_name in self.goal_handles:
return self.goal_handles[client_name]
return None
def set_goal_handle(self, camera_namespace, camera_name, client):
client_name = f"{camera_namespace}_{camera_name}"
self.goal_handles[client_name] = client
def ros_action_send_goal(self, goal, namespace, name):
goal_msg = TriggeredCalibration.Goal(json=goal)
self.get_action_client(namespace,name).wait_for_server()
self.send_goal_future = self.get_action_client(namespace,name).send_goal_async(goal_msg, feedback_callback=partial(self.ros_action_feedback_callback, namespace=namespace,name=name))
self.send_goal_future.add_done_callback(partial(self.ros_action_goal_response_callback, namespace=namespace,name=name))
def ros_action_goal_response_callback(self, future, namespace,name):
goal_handle = future.result()
self.set_goal_handle(namespace,name, goal_handle)
if not goal_handle.accepted:
self.mqtt_ros_node.ROS_DEBUG('TriggeredCalibrationHandler: Goal rejected')
return
self.mqtt_ros_node.ROS_DEBUG('TriggeredCalibrationHandler: Goal accepted')
self.get_result_future = goal_handle.get_result_async()
self.get_result_future.add_done_callback(partial(self.ros_action_result_callback, namespace=namespace,name=name))
def ros_action_result_callback(self, future, namespace,name):
result = future.result().result
self.mqtt_ros_node.ROS_DEBUG('ros_action_result_callback result{result}')
self.mqtt_ros_node.ROS_DEBUG('Success: {0}'.format(result.success))
self.mqtt_ros_node.ROS_DEBUG('Error: {0}'.format(result.error_msg))
self.mqtt_ros_node.ROS_DEBUG('Calibration: {0}'.format(result.calibration))
self.mqtt_ros_node.ROS_DEBUG('Health: {0}'.format(result.health))
self.mqtt_ros_node.ROS_DEBUG('Progress: 100.0')
mqtt_response = {
'camera_namespace': namespace,
'camera_name': name,
'success': result.success,
'error_msg': result.error_msg,
'calibration': result.calibration,
'health': result.health,
'progress': 100.0
}
self.mqtt_ros_node.mqtt_client.publish('triggered_calibration_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('triggered_calibration_response message sent')
self.get_action_client(namespace,name).destroy()
self.set_action_client(namespace,name, None)
self.set_goal_handle(namespace,name, None)
def ros_action_feedback_callback(self, feedback_msg, namespace,name):
feedback = feedback_msg.feedback
self.mqtt_ros_node.ROS_DEBUG('TriggeredCalibrationHandler: Received feedback: {0}'.format(feedback.progress))
mqtt_response = {
'camera_namespace': namespace,
'camera_name': name,
'success': False,
'error_msg': '',
'calibration': '{}',
'health': '0',
'progress': feedback.progress
}
self.mqtt_ros_node.mqtt_client.publish('triggered_calibration_response',
json.dumps(mqtt_response),
qos=2)
self.mqtt_ros_node.ROS_DEBUG('triggered_calibration_response message sent')
def handle_triggered_calibration_request(self, mqtt_request):
self.mqtt_ros_node.ROS_DEBUG('triggered_calibration_request \
message received')
camera_namespace = mqtt_request['camera_namespace']
camera_name = mqtt_request['camera_name']
if mqtt_request['json'] == 'calib abort':
#received am abort request, check if there was a pending goal.
goal_handle = self.get_goal_handle(mqtt_request['camera_namespace'],mqtt_request['camera_name'])
if self.get_action_client(camera_namespace,camera_name) is None or goal_handle is None:
self.mqtt_ros_node.ROS_DEBUG("No goal in progress, cancelled too early?")
mqtt_response = {
'camera_namespace': camera_namespace,
'camera_name': camera_name,
'success': True,
'error_msg': "No goal in progress, cancelled too early?",
'calibration': '{}',
'health': '0',
'progress': 100.0
}
self.mqtt_ros_node.mqtt_client.publish('triggered_calibration_response',
json.dumps(mqtt_response),
qos=2)
else:
#cancel the goal in progress
self.mqtt_ros_node.ROS_DEBUG("cancelling the goal..")
future = goal_handle.cancel_goal_async()
else:
#all the requests other abort request is passed to the ActionServer
action_name = f'/{camera_namespace}/{camera_name}/triggered_calibration'
self.set_action_client(mqtt_request['camera_namespace'],mqtt_request['camera_name'],ActionClient(self.mqtt_ros_node, TriggeredCalibration, action_name))
self.ros_action_send_goal(mqtt_request['json'], camera_namespace, camera_name)
@@ -0,0 +1,23 @@
# Running the unit tests using pytest
## In one terminal:
```
ros2 run realsense2_ros_mqtt_bridge realsense2_ros_mqtt_bridge
```
## In another terminal:
```
pytest-3 -s --log-cli-level=DEBUG realsense2_ros_mqtt_bridge/test/unittests
```
# Running the system tests using pytest
```
pytest-3 -s --log-cli-level=DEBUG realsense2_ros_mqtt_bridge/test/systemtests
```
Note: To use the log level, user may have to upgrade the pytest to the latest, with the following command
```
python -m pip install -U pytest
```
@@ -0,0 +1,91 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_align_depth_color_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_align_depth_color_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_abort_triggered_calibration(launch_descr_with_parameters):
#initialization starts....
try:
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
sds.prepare_for_calibration(namespace, name)
sds.send_triggered_calibration_request(namespace,
name)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.debug(f"Response: {response}")
if response['progress'] > 25.0:
sds.abort_triggered_calibration_request(namespace, name)
break
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.info(f"Response: {response}")
if response['success'] == True or response['error_msg'] != '':
if response['success'] == False and response['error_msg'] == 'Canceled':
assert response['calibration'] == '{}', "Could not abort the calibration"
LOGGER.info('Calibration abort successful')
else:
assert False, 'Aborting of triggered calibration failed. Response:' +str(response)
break
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.warning("Test failed")
LOGGER.warning(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
finally:
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,105 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import rclpy
import pytest
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
#@pytest.mark.timeout(20)
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_application_config(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_application_config_service()
sds.set_safety_mode(namespace, name, 2)
sds.send_get_application_config_request(namespace,
name)
response = sds.receive_get_application_config_response()
original_data = response['application_config']
import json
Application_data = json.loads(original_data)
if Application_data['application_config']['developer_mode']['hkr'] == 1:
Application_data['application_config']['developer_mode']['hkr'] = 0
else:
Application_data['application_config']['developer_mode']['hkr'] = 1
Application_data1 = json.dumps(Application_data)
sds.send_set_application_config_request(namespace,
name,
Application_data1)
response = sds.receive_set_application_config_response()
sds.send_get_application_config_request(namespace,
name)
response = sds.receive_get_application_config_response()
ac_read = json.loads(response["application_config"])
LOGGER.debug("Application data written: ",Application_data)
LOGGER.debug("Application data readback:",ac_read)
assert ac_read == Application_data, "Written Application config is not matching with the read one"
sds.send_set_application_config_request(namespace,
name,
original_data)
response = sds.receive_set_application_config_response()
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,101 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import numpy as np
import rclpy
import pytest
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
#@pytest.mark.timeout(20)
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_calib_config(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
sds.set_safety_mode(namespace, name, 2)
sds.send_get_calib_config_request(namespace,
name)
response = sds.receive_get_calib_config_response()
print("Response:", response)
original_data = response['calib_config']
import json
original_data = original_data.replace("null",str(np.finfo(np.float32).max))
calib_config = json.loads(original_data)
if calib_config['calibration_config']['roi_0']['vertex_0'][1] == 35:
calib_config['calibration_config']['roi_0']['vertex_0'][1] = 36
else:
calib_config['calibration_config']['roi_0']['vertex_0'][1] = 35
calib_config1 = json.dumps(calib_config)
sds.send_set_calib_config_request(namespace,
name,
calib_config1)
response = sds.receive_set_calib_config_response()
sds.send_get_calib_config_request(namespace,
name)
response = sds.receive_get_calib_config_response()
cc_read = json.loads(response['calib_config'])
LOGGER.debug("calib config data written: ",calib_config)
LOGGER.debug("calib config data readback:",cc_read)
assert cc_read == calib_config, "Written calib config is not matching with the read one"
sds.send_set_calib_config_request(namespace,
name,
original_data)
response = sds.receive_set_calib_config_response()
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,84 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_align_depth_color_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_align_depth_color_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_device_info(launch_descr_with_parameters):
#initialization starts....
try:
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_device_info_service()
sds.send_get_device_info_request(namespace,
name)
response = sds.receive_get_device_info_response()
LOGGER.info(f"response:{response}")
device_info= camera_n_mqtt_nodes.get_camera_device_info(params['device_type'], response['serial_number'])
assert device_info != None, "Could not find the device_info"
for key, value in device_info.items():
LOGGER.info(f"checking:{key}")
assert response[key] == value, f"device_info read returned unexpected value in {key}:Received {response['serial_number']}"
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
finally:
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,94 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_align_depth_color_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_align_depth_color_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_dryrun_calibration(launch_descr_with_parameters):
#initialization starts....
try:
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
sds.prepare_for_calibration(namespace, name)
sds.send_triggered_calibration_request(namespace,
name,
dryrun=True)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.info(f"Response: {response}")
if response['success'] == True or response['error_msg'] != '':
if response['success'] == True:
LOGGER.info('Triggered calibraton was successful')
assert (response['calibration'] != "{}"), "The calibration data received is empty"
assert type(response['calibration']) != list, "The calibration data received is not a list"
LOGGER.info('Triggered calibraton data:' + response['calibration'])
elif response['progress'] == 100.0 and 'Calibration completed but algorithm failed' in response['error_msg']:
#since it's an issue with the camera field of view, treating it as a success with warning.
#Manual adjustment of camera is needed to pass the test.
LOGGER.warning('Triggered calibraton completed, but algorithm failed. This is treated as a successful completion of the test')
else:
assert False, 'Triggered calibration failed with unexpected response:'+str(response)
break
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
finally:
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,101 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import rclpy
import pytest
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
#@pytest.mark.timeout(20)
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_frame_types(launch_descr_with_parameters):
#initialization starts....
try:
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
params = [
{"param_name":'safety_camera.safety_mode', "default_value":0, "param_type":"int"},
]
camera.add_parameters(params)
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
'''safety stream suport is not required
LOGGER.info("Testing safety_frame...")
sds.start_stop_safety_stream(namespace, name, True)
frame = sds.get_frame_msg(namespace, name, "safety")
LOGGER.debug(frame)
'''
LOGGER.info("Testing color_frame...")
sds.start_stop_color_stream(namespace, name, True)
frame = sds.get_frame_msg(namespace, name, "color")
LOGGER.debug(frame)
sds.start_stop_color_stream(namespace, name, False)
LOGGER.info("Testing depth_frame...")
sds.start_stop_depth_stream(namespace, name, True)
frame = sds.get_frame_msg(namespace, name, "depth")
LOGGER.debug(frame)
sds.start_stop_depth_stream(namespace, name, False)
LOGGER.info("Testing infra1_frame...")
sds.start_stop_infra1_stream(namespace, name, True)
frame = sds.get_frame_msg(namespace, name, "infra1")
LOGGER.debug(frame)
LOGGER.info("Testing infra2_frame...")
sds.start_stop_infra2_stream(namespace, name, True)
frame = sds.get_frame_msg(namespace, name, "infra2")
LOGGER.debug(frame)
except:
raise
finally:
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,116 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import json
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_hmc_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_hmc_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_hmc_commands(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
links = [
{"opcode":0x76, "datasize_expected":8}, #GETRGBAEROI
{"opcode":0x2A, "param1": 0, "datasize_expected":20}, #GTEMP
{"opcode":0x2A, "param1": 1, "datasize_expected":20}, #GTEMP
{"opcode":0x2A, "param1": 2, "datasize_expected":20}, #GTEMP
{"opcode":0xAC, "datasize_expected":240}, #HEALTH Status
{"opcode":0x76,"datasize_expected":8},#GETRGBAEROI
{"opcode":0x75, "param1": 0x1c4, "param2": 0x2a0, "param3": 0x36d, "param4": 0x0ad},#SETRGBAEROI
{"opcode":0x76,"datasize_expected":8},#GETRGBAEROI
]
for cmd in links:
param1 = cmd.get("param1", None)
param2 = cmd.get("param2", None)
param3 = cmd.get("param3", None)
param4 = cmd.get("param4", None)
data = cmd.get("data", None)
val = sds.send_hwm_command(namespace,name, cmd['opcode'], param1=param1, param2=param2, param3=param3, param4=param4, data=data)
LOGGER.info(f"Data received {val['result']}")
size = cmd.get("datasize_expected", 0)
if size:
in_data = val['result'][4:]
assert val['result'] is not None, f"Expected {size} of data, but didn't get any"
assert len(in_data) == size, f"Expected {size} of data, but got {len(in_data)}" #1st 4 bytes contain the opcode
else:
assert len(val['result']) == 4, f"Expected {size} of data, but got {len(val['result'])}" #1st 4 bytes contain the opcode
header = int.from_bytes(val['result'][0:3], byteorder='little', signed=False)
assert header == cmd['opcode'], f"Didn't get the opcode back, got {header} instead of {cmd['opcode']}"
val = sds.send_hwm_command(namespace,name, 0xBC, param1=0)
LOGGER.debug(val)
data = val['result'][4:]
data[3] =1
sds.set_safety_mode(namespace, name, 2)
val = sds.send_hwm_command(namespace,name, 0xBC, param1=1,data=data)
val = sds.send_hwm_command(namespace,name, 0xBC, param1=0)
data = val['result'][4:]
LOGGER.debug(val)
assert data[3] ==1, "Error: couldn't read back the written data"
val = sds.send_hwm_command(namespace,name,0x76)#GETRGBAEROI
val = sds.send_hwm_command(namespace,name,0x75, param1=0x1c2, param2= 0x2a0, param3 = 0x36d, param4 = 0x0ad)#SETRGBAEROI
val = sds.send_hwm_command(namespace,name,0x76)#GETRGBAEROI
param1l = val['result'][4:6]
param1 = int.from_bytes(param1l, byteorder='little', signed=False)
assert param1 == 0x1c2, "Error: written data is not matching with the read one"
''' #taking too much of time and inconsistent
val = sds.send_hwm_command(namespace,name,32, param1=1) #SoC reset
import time
time.sleep(15)
mode = sds.get_integer_param(namespace, name, 'safety_camera.safety_mode')
print("Mode:", mode)
time.sleep(2)
'''
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,75 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import json
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_hmc_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
'rgb_camera.color_profile': '640x360x30',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_hmc_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_hw_reset(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
sds.set_string_param(namespace, name,'rgb_camera.color_profile', '640x360x5')
param = sds.get_string_param(namespace, name, 'rgb_camera.color_profile')
assert param == '640x360x5', "Couldn't set the parameter"
val = sds.send_hw_reset(namespace,name)
import time
#can't use the enumerate_devices for this purpose, so banking on sleep
time.sleep(5)
param = sds.get_string_param(namespace, name, 'rgb_camera.color_profile')
assert param == '640x360x30', "Reset didn't work it seems. Please check the logs manually for reset..."
time.sleep(2)
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,730 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import json
import numpy as np
import rclpy
import pytest
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
#@pytest.mark.timeout(20)
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_negative_json_strings(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
'''
request_dict = {
'camera_name_prefix': name
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'enumerate_devices_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name_prefix in mqtt_request"
request_dict = {
'camera_namespace_prefix': namespace
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'enumerate_devices_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace_prefix in mqtt_request"
'''
request_dict = {
'camera_namespace_prefix': namespace,
'camera_name_prefix': name
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'enumerate_devices_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] == True, "Expected a success message for a proper mqtt_request"
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'parameter_name': 'enable_safety',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'parameter_name': 'enable_safety',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'parameter_name': 'enable_safety',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of parameter_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'parameter_name': 'enable_safety',
'parameter_value': False,
'parameter_type': 'bool'
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'parameter_name': 'enable_safety',
'parameter_value': False,
'parameter_type': 'bool'
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'parameter_name': 'enable_safety',
'parameter_value': False,
'parameter_type': 'bool'
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of parameter_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
'parameter_name': 'enable_safety',
#'parameter_value': False,
'parameter_type': 'bool'
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of parameter_value in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
'parameter_name': 'enable_safety',
'parameter_value': False,
#'parameter_type': 'bool'
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_param_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of parameter_type in mqtt_request"
#get_frame request
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'stream_name': 'color',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_frame_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'stream_name': 'color',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_frame_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'stream_name': 'color',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_frame_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of stream_name in mqtt_request"
#get_safety_preset
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'index': 0,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'index': 0,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'index': 0,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of index in mqtt_request"
#set_safety_preset
preset_response = sds.get_safety_preset(namespace, name, 1)
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'index': 1,
'safety_preset': preset_response['safety_preset']
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'index': 1,
'safety_preset': preset_response['safety_preset']
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'index': 1,
'safety_preset': preset_response['safety_preset']
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of index in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
'index': 1,
#'safety_preset': preset_response['safety_preset']
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_preset_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of preset in mqtt_request"
#get_safety_interface_config
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'calib_location': 2,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_safety_interface_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'calib_location': 2,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_safety_interface_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'calib_location': 2,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_safety_interface_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] == True, "Expected a success message for the absence of calib_location in mqtt_request, it should use the default value 2"
#set_safety_interface_config
si_cfg = response['safety_interface_config']
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'safety_interface_config':si_cfg
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_interface_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'safety_interface_config':si_cfg
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_interface_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'safety_interface_config':si_cfg
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_safety_interface_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of safety_interface_config in mqtt_request"
#get_calib_config
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_calib_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_calib_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
sds.set_safety_mode(namespace, name, 2)
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_calib_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] == True, "Expected a success message for calib_config:" + response["error_msg"]
original_data = response['calib_config'].replace("null",str(np.finfo(np.float32).max))
#set_calib_config_request
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'calib_config':original_data
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_calib_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'calib_config':original_data
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_calib_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'calib_config':original_data
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_calib_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of calib_config in mqtt_request"
#get_application_config
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_application_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_application_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'get_application_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] == True, "Expected a a successful application_config read"
original_data = response['application_config']
#set_application_config
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'application_config':original_data
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_application_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'application_config':original_data
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_application_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'set_application_config_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of application_config in mqtt_request"
#triggered calibration
sds.prepare_for_calibration(namespace, name)
request_dict = {
#'camera_namespace': namespace,
'camera_name': name,
'json':'calib run',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'triggered_calibration_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_namespace in mqtt_request"
request_dict = {
'camera_namespace': namespace,
#'camera_name': name,
'json':'calib run',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'triggered_calibration_request')
msg = sds.get_message()
response = json.loads(msg.payload)
assert response["success"] != True, "Expected a failure message for the absence of camera_name in mqtt_request"
request_dict = {
'camera_namespace': namespace,
'camera_name': name,
#'json':'calib run',
}
j = json.dumps(request_dict)
sds.msg = None
sds.locked = True
sds.publish(j, 'triggered_calibration_request')
msg = sds.get_message()
sds.locked = True
response = json.loads(msg.payload)
assert response["error_msg"] == "", "default triggered calibration request is calib run, but failed with error:"+response["error_msg"]
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.info(f"Response: {response}")
if response['success'] == True or response['error_msg'] != '':
if response['success'] == True:
LOGGER.info('Triggered calibraton was successful')
assert (response['calibration'] != "{}"), "The calibration data received is empty"
assert type(response['calibration']) != list, "The calibration data received is not a list"
LOGGER.info('Triggered calibraton data:' + response['calibration'])
elif response['progress'] == 100.0 and 'Calibration completed but algorithm failed' in response['error_msg']:
#since it's an issue with the camera field of view, treating it as a success with warning.
#Manual adjustment of camera is needed to pass the test.
LOGGER.warning('Triggered calibraton completed, but algorithm failed. This is treated as a successful completion of the test')
else:
assert False, 'Triggered calibration failed with unexpected response:'+str(response)
break
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,106 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
import rclpy
import pytest
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
#@pytest.mark.timeout(20)
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_safety_interface_config(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_safety_interface_config_service()
#just to ensure no streams are running
sds.set_safety_mode(namespace, name, 2)
#safety interface config read works only for RAM and FLASH.
for index in [1,2]:
sds.send_get_safety_interface_config_request(namespace,
name,
index)
response = sds.receive_get_safety_interface_config_response()
original_data = response["safety_interface_config"]
import json
sc_data = json.loads(original_data)
LOGGER.info(f"safety interface config {index} first read: {sc_data}")
if sc_data["safety_interface_config"]['smcu_arbitration_params']['l_0_total_threshold'] == 10000:
sc_data["safety_interface_config"]['smcu_arbitration_params']['l_0_total_threshold'] = 10
else:
sc_data["safety_interface_config"]['smcu_arbitration_params']['l_0_total_threshold'] = 10000
sc_data1 = json.dumps(sc_data)
LOGGER.debug(f"safety interface config written: {sc_data}")
#change sp
sds.send_set_safety_interface_config_request(namespace,
name,
sc_data1)
response = sds.receive_set_safety_interface_config_response()
sds.send_get_safety_interface_config_request(namespace,
name,
index)
response = sds.receive_get_safety_interface_config_response()
sc_read = json.loads(response["safety_interface_config"])
LOGGER.info(f"safety interface config {index} second read: {sc_read}")
assert sc_read == sc_data, "Written safety interface config is not matching with the read one"
sds.send_set_safety_interface_config_request(namespace,
name,
original_data)
response = sds.receive_set_safety_interface_config_response()
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,84 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_tf_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
'publish_tf': 'true',
'tf_publish_rate': '1.1',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_tf_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_tf(launch_descr_with_parameters):
#initialization starts....
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
links = {
name+'_link':name+'_depth_frame',
name+'_depth_frame':name+'_depth_optical_frame',
name+'_link':name+'_color_frame',
name+'_color_frame':name+'_color_optical_frame',
}
for source, destination in links.items():
val = sds.get_transformation(namespace, name, source, destination)
assert 'rotation' in val, "rotation values not found"
assert 'x' in val['rotation'], "x in rotation values not found"
assert 'y' in val['rotation'], "y in rotation values not found"
assert 'z' in val['rotation'], "z in rotation values not found"
assert 'w' in val['rotation'], "w in rotation values not found"
assert 'translation' in val, "translation values not found"
assert 'x' in val['translation'], "x in translation values not found"
assert 'y' in val['translation'], "y in translation values not found"
assert 'z' in val['translation'], "z in translation values not found"
#cleanup starts....
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,93 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_n_mqtt_nodes import CameraNMqttNodes as RSCameraSimulator
from camera_n_mqtt_nodes import launch_descr_with_parameters
import camera_n_mqtt_nodes
from mqtt_client_simulator import MQTTClientSimulator
import logging
import pytest
import rclpy
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
test_params_align_depth_color_d585s = {
'camera_name': 'D585S',
'device_type': 'D585S',
}
@pytest.mark.parametrize("launch_descr_with_parameters",[
pytest.param(test_params_align_depth_color_d585s, marks=pytest.mark.d585s),
]
,indirect=True)
@pytest.mark.launch(fixture=launch_descr_with_parameters)
def test_system_triggered_calibration(launch_descr_with_parameters):
#initialization starts....
try:
rclpy.init()
params = launch_descr_with_parameters[1]
namespace = 'camera'
name = params['camera_name']
camera = RSCameraSimulator(namespace, name)
camera.start()
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
sds.prepare_for_calibration(namespace, name)
sds.send_triggered_calibration_request(namespace,
name)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.info(f"Response: {response}")
if response['success'] == True or response['error_msg'] != '':
if response['success'] == True:
LOGGER.info('Triggered calibraton was successful')
assert (response['calibration'] != "{}"), "The calibration data received is empty"
assert type(response['calibration']) != list, "The calibration data received is not a list"
LOGGER.info('Triggered calibraton data:' + response['calibration'])
elif response['progress'] == 100.0 and 'Calibration completed but algorithm failed' in response['error_msg']:
#since it's an issue with the camera field of view, treating it as a success with warning.
#Manual adjustment of camera is needed to pass the test.
LOGGER.warning('Triggered calibraton completed, but algorithm failed. This is treated as a successful completion of the test')
else:
assert False, 'Triggered calibration failed with unexpected response:'+str(response)
break
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
finally:
camera.stop()
rclpy.shutdown()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,85 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
import pytest
#@pytest.mark.skip(reason="under development")
def test_triggered_calibration():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_triggered_calibration_action()
#just call abort without a calibration run
sds.abort_triggered_calibration_request(namespace, name)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.debug(f"Response: {response}")
if response['success'] == True:
break
assert response['error_msg'] == "No goal in progress, cancelled too early?", 'Unexpected error message received ' + response['error_msg']
sds.send_triggered_calibration_request(namespace,
name)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.debug(f"Response: {response}")
if response['progress'] > 2.0:
sds.abort_triggered_calibration_request(namespace, name)
break
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.debug(f"Response: {response}")
if response['success'] == True or response['error_msg'] != "":
break
assert response['success'] == False, "Response.success received as True, expecting False"
assert response['error_msg'] == "Canceled", 'Unexpected error message received ' + response['error_msg']
assert response['calibration'] == "{}", 'Unexpected calibration value received ' + response['calibration']
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,89 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_all_param_types():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
params = [
{"param_name":'safety_camera.safety_mode', "default_value":0, "param_type":"int", "alternate_value":2},
{"param_name":'accel_info_qos ', "default_value":"accel_info_qos", "param_type":"string", "alternate_value":'hello'},
{"param_name":'align_depth.enable','default_value':True,'param_type': 'bool', "alternate_value":'False'},
{"param_name":'align_depth.disable','default_value':False,'param_type': 'bool', "alternate_value":'True'}
]
camera.add_parameters(params)
for param in params:
LOGGER.info("Testing get_param for type " + param["param_type"])
sds.send_get_param_request(namespace,
name,
param['param_name'])
response = sds.receive_get_param_response()
assert str(response["parameter_value"]) == str(param['default_value']), "default value was not set for for param " + param['param_name']
LOGGER.info("Testing set_param for type " + param["param_type"])
sds.send_set_param_request(namespace,
name,
param['param_name'],
param['alternate_value'],
param['param_type'])
response = sds.receive_set_param_response()
response = sds.get_param_msg(namespace,
name,
param['param_name'])
assert response["parameter_value"] == str(param['alternate_value']), "Get or Set param failed, didn't get the written value for param " + param['param_name']
#cleanup starts....
except Exception as e:
raise
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,76 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_application_config():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_application_config_service()
sds.send_get_application_config_request(namespace,
name)
response = sds.receive_get_application_config_response()
Application_data = "Application data"
sds.send_set_application_config_request(namespace,
name,
Application_data)
response = sds.receive_set_application_config_response()
sds.send_get_application_config_request(namespace,
name)
response = sds.receive_get_application_config_response()
assert response["application_config"] == Application_data, "Written Application config is not matching with the read one"
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
#LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,90 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_bridge_instatiation():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
params = [
{"param_name":'safety_camera.safety_mode', "default_value":0, "param_type":"int"},
]
camera.add_parameters(params)
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
LOGGER.info("Testing get_param...")
sds.send_get_param_request(namespace,
name,
'safety_camera.safety_mode')
response = sds.receive_get_param_response()
LOGGER.debug("safety_camera.safety_mode Param received: " + str(response["parameter_value"]))
LOGGER.info("Testing set_param...")
sds.send_set_param_request(namespace,
name,
'safety_camera.safety_mode',
'2',
'int')
response = sds.receive_set_param_response()
response = sds.get_param(namespace,
name,
'safety_camera.safety_mode')
assert int(response["parameter_value"]) == 2, "Get or Set param failed, didn't get the written value"
LOGGER.debug("safety_camera.safety_mode Param received: " + str(response))
LOGGER.info("Testing get_frame...")
camera.start_publish_color_frame()
frame = sds.get_frame(namespace, name, "color")
LOGGER.debug(frame)
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,76 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_calib_config():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_calib_config_service()
sds.send_get_calib_config_request(namespace,
name)
response = sds.receive_get_calib_config_response()
calib_data = "Calibration data"
assert response['calib_config'] == 'Uninitialized', "Calib config read returned unexpected value: " + response['calib_config']
sds.send_set_calib_config_request(namespace,
name,
calib_data)
response = sds.receive_set_calib_config_response()
sds.send_get_calib_config_request(namespace,
name)
response = sds.receive_get_calib_config_response()
assert response["calib_config"] == calib_data, "Written calib config is not matching with the read one"
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,66 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_device_info():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_device_info_service()
sds.send_get_device_info_request(namespace,
name)
response = sds.receive_get_device_info_response()
assert response['device_name'] == 'Camera', "device_info read returned unexpected value in device_name: " + response['device_name']
assert response['serial_number'] == '1234', "device_info read returned unexpected value in serial_number: " + response['serial_number']
LOGGER.debug("response:", response)
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,72 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
import pytest
#@pytest.mark.skip(reason="under development")
def test_triggered_calibration():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_triggered_calibration_action()
sds.send_triggered_calibration_request(namespace,
name, dryrun=True)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.debug(f"Response: {response}")
if response['progress'] == 100.0:
#should we check for health?
assert response['success'] == True, 'Triggered calibraton was not successful'
assert response['calibration'] == "calib dry run", 'Unexpected calibration value received'
break
#print(response.payload)
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
finally:
#cleanup starts....
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,93 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_dual_camera_device_info():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
namespace1 = 'camera1'
name1 = 'camera1'
camera1 = RSCameraSimulator(namespace1, name1)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
camera1.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, f"Enumerate device failed, couldn't find the device {name}"
sds.send_enumerate_devices_request(namespace1, name1)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, f"Enumerate device failed, couldn't find the device {name1}"
camera.create_device_info_service()
camera1.create_device_info_service()
sds.send_get_device_info_request(namespace,
name)
response = sds.receive_get_device_info_response()
assert response['device_name'] == 'Camera', "device_info read returned unexpected value in device_name: " + response['device_name']
assert response['serial_number'] == '1234', "device_info read returned unexpected value in serial_number: " + response['serial_number']
LOGGER.debug("response:", response)
sds.send_get_device_info_request(namespace1,
name1)
response = sds.receive_get_device_info_response()
assert response['device_name'] == 'Camera', "device_info read returned unexpected value in device_name: " + response['device_name']
assert response['serial_number'] == '1234', "device_info read returned unexpected value in serial_number: " + response['serial_number']
sds.send_get_device_info_request(namespace,
name)
response = sds.receive_get_device_info_response()
assert response['device_name'] == 'Camera', "device_info read returned unexpected value in device_name: " + response['device_name']
assert response['serial_number'] == '1234', "device_info read returned unexpected value in serial_number: " + response['serial_number']
LOGGER.debug("response:", response)
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
camera1.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,98 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_dual_camera_triggered_calibration():
#initialization starts....
try:
namespace = 'camera'
name = 'camera2'
camera2 = RSCameraSimulator(namespace, name)
name1 = 'camera1'
camera1 = RSCameraSimulator(namespace, name1)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera2.start()
camera1.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, f"Enumerate device failed, couldn't find the device {name}"
sds.send_enumerate_devices_request(namespace, name1)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, f"Enumerate device failed, couldn't find the device {name1}"
camera2.create_triggered_calibration_action()
camera1.create_triggered_calibration_action()
sds.send_triggered_calibration_request(namespace,
name)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.info(f"Response: {response}")
if response['progress'] > 5.0:
break
sds.send_triggered_calibration_request(namespace,
name1)
camera1_response = False
camera2_response = False
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.info(f"Response: {response}")
if response['progress'] == 100.0 and response['camera_name'] == 'camera2':
assert response['success'] == True, 'Triggered calibraton was not successful'
assert response['calibration'] == "calib run", 'Unexpected calibration value received'
camera2_response = True
if camera1_response:
break
if response['progress'] == 100.0 and response['camera_name'] == 'camera1':
assert response['success'] == True, 'Triggered calibraton was not successful'
assert response['calibration'] == "calib run", 'Unexpected calibration value received'
camera1_response = True
if camera2_response:
break
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.warning("Test failed")
LOGGER.warning(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera2.stop()
camera1.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,93 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_frame_types():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
params = [
{"param_name":'safety_camera.safety_mode', "default_value":0, "param_type":"int"},
]
camera.add_parameters(params)
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
LOGGER.info("Testing color_frame...")
camera.start_publish_color_frame()
response = sds.get_frame_msg(namespace, name, "color")
assert response['success'] == True, 'Receiving color frame was not successful'
assert response['frame'] != "", 'Invalid frame received'
LOGGER.info("Testing depth_frame...")
camera.start_publish_depth_frame()
response = sds.get_frame_msg(namespace, name, "depth")
assert response['success'] == True, 'Receiving depth frame was not successful'
assert response['frame'] != "", 'Invalid frame received'
LOGGER.info("Testing infra1_frame...")
camera.start_publish_infra1_frame()
response = sds.get_frame_msg(namespace, name, "infra1")
assert response['success'] == True, 'Receiving infra1 frame was not successful'
assert response['frame'] != "", 'Invalid frame received'
LOGGER.info("Testing infra2_frame...")
camera.start_publish_infra2_frame()
response = sds.get_frame_msg(namespace, name, "infra2")
assert response['success'] == True, 'Receiving infra2 frame was not successful'
assert response['frame'] != "", 'Invalid frame received'
LOGGER.info("Testing invalid stream...")
response = sds.get_frame_msg(namespace, name, "invalid")
assert response['success'] != True, 'Expected a failure, but received frame'
assert response['frame'] == "", 'Frame received for invalid stream'
LOGGER.info("Testing depth_frame...")
camera.start_publish_depth_frame()
response = sds.get_frame_msg(namespace, name, "depth")
assert response['success'] == True, 'Receiving depth frame was not successful'
assert response['frame'] != "", 'Invalid frame received'
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,75 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_safety_interface_config():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_safety_interface_config_service()
for index in range(0,3):
sds.send_get_safety_interface_config_request(namespace,
name,
index)
response = sds.receive_get_safety_interface_config_response()
sp = "Index is " + str(index)
sds.send_set_safety_interface_config_request(namespace,
name,
sp)
response = sds.receive_set_safety_interface_config_response()
sds.send_get_safety_interface_config_request(namespace,
name,
index)
response = sds.receive_get_safety_interface_config_response()
assert response["safety_interface_config"] == sp, "Written safety interface config is not matching with the read one"
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,78 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
def test_safety_preset():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_safety_preset_service()
#for index in range(0,63):
for index in [0,1,10,36,62,63]:
sds.send_get_safety_preset_request(namespace,
name,
index)
response = sds.receive_get_safety_preset_response()
assert response["safety_preset"] == "Uninitialized", "Safety preset read failed"
sp = "Index is " + str(index)
sds.send_set_safety_preset_request(namespace,
name,
sp,
index)
response = sds.receive_set_safety_preset_response()
sds.send_get_safety_preset_request(namespace,
name,
index)
response = sds.receive_get_safety_preset_response()
assert response["safety_preset"] == sp, "Written safety preset is not matching with the read one"
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error("Test failed")
LOGGER.error(e)
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,69 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)+"/../utils"))
from camera_node_simulator import RSCameraSimulator
from mqtt_client_simulator import MQTTClientSimulator
import logging
#LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger()
import pytest
#@pytest.mark.skip(reason="under development")
def test_triggered_calibration():
#initialization starts....
try:
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
sds = MQTTClientSimulator("localhost", 1883)
sds.start_client()
camera.start()
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
os.system("ros2 node list")
#initialization ends....
LOGGER.info("Testing enumerate_devices")
sds.send_enumerate_devices_request(namespace, name)
response = sds.get_enumerate_devices_response()
assert int(response["available_nodes_count"]) > 0, "Enumerate device failed, couldn't find the device"
camera.create_triggered_calibration_action()
sds.send_triggered_calibration_request(namespace,
name)
while True:
response = sds.receive_triggered_calibration_response()
LOGGER.debug(f"Response: {response}")
if response['progress'] == 100.0:
assert response['success'] == True, 'Triggered calibraton was not successful'
assert response['calibration'] == "calib run", 'Unexpected calibration value received'
break
#cleanup starts....
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
LOGGER.error(exc_type, fname, exc_tb.tb_lineno)
LOGGER.error("Test failed")
LOGGER.error(e)
camera.stop()
LOGGER.info("Test completed")
#cleanup ends....
@@ -0,0 +1,250 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import os
import sys
import time
import threading
from launch import LaunchDescription
from launch.substitutions import LaunchConfiguration
from launch.actions import DeclareLaunchArgument
from ament_index_python.packages import get_package_share_directory
import launch_ros
from launch import LaunchDescription
from launch import LaunchService
import launch_pytest
import rclpy
from rclpy.node import Node
import logging
LOGGER = logging.getLogger()
import rs_launch
class CameraNMqttNodes(Node):#, threading.Thread):
def __init__(self, namespace="camera", name='camera', device_type_="D6585S"):
super().__init__(name)
self.dummy = True
return
def run(self):
return
def wait_for_node(self, node_name, timeout=8.0):
start = time.time()
flag = False
print('Waiting for node... ' + node_name)
while time.time() - start < timeout:
print(node_name + ": waiting for the node to come up")
flag = node_name in self.get_node_names()
if flag:
return True, ""
time.sleep(timeout/5)
return False, "Timed out waiting for "+ str(timeout)+ "seconds"
def start(self):
if self.dummy == True:
self.wait_for_node("realsense_ros_mqtt_bridge_node")
return
#start of dummy functions
def stop(self):
return
def create_device_info_service(self):
return
def add_parameters(self,params):
return
def start_publish_color_frame(self):
return
def create_application_config_service(self):
return
def create_calib_config_service(self):
return
def create_safety_interface_config_service(self):
return
#end of dummy functions
def get_camera_device_info(device_type, serial_no):
short_data = os.popen("rs-enumerate-devices -S").read().splitlines()
print(serial_no)
line_found = 0
found = False
for line in short_data:
print(line)
if device_type in line:
if serial_no in line:
found = True
break
line_found += 1
if found == True:
'''
rs-enumerate-devices -S
Device Name Serial Number Firmware Version
Intel RealSense D585S 416422320067 8.17.17151.218
Device info:
Name : Intel RealSense D585S
Serial Number : 416422320067
Firmware Version : 8.17.17151.218
Physical Port : /sys/devices/pci0000:00/0000:00:14.0/usb2/2-5/2-5:1.0/video4linux/video0
Debug Op Code : 180
Advanced Mode : NO
Product Id : 0B6B
Camera Locked : YES
Usb Type Descriptor : 3.2
Product Line : D500
Firmware Update Id : 416422320067
Smcu Fw Version : 2.0.6.76
'''
#if this order changes, test will fail
length = len(short_data)
LOGGER.debug(device_type + " with serial_no " + serial_no +" found in " + short_data[line_found])
for i in range(line_found, line_found+9):
LOGGER.debug(short_data[i])
device_info = {}
#3rd line contains serial no
line_no = line_found+3
if not "Serial Number" in short_data[line_no]:
return None
split_line = short_data[line_no].split()
device_info['serial_number'] = split_line[3]
#4th line contains Firmware Version
line_no = line_found+4
if not "Firmware Version" in short_data[line_no]:
return None
split_line = short_data[line_no].split()
device_info['firmware_version'] = split_line[3]
#5th line contains physical port
line_no = line_found+5
if not "Physical Port" in short_data[line_no]:
return None
split_line = short_data[line_no].split()
device_info['physical_port'] = split_line[3]
#10th line contains Usb Type Descriptor
line_no = line_found+10
if not "Usb Type Descriptor" in short_data[line_no]:
return None
split_line = short_data[line_no].split()
device_info['usb_type_descriptor'] = split_line[4]
#12th line contains Usb Type Descriptor
line_no = line_found+12
if not "Firmware Update Id" in short_data[line_no]:
return None
split_line = short_data[line_no].split()
device_info['firmware_update_id'] = split_line[4]
return device_info
return None
'''
get the default parameters from the launch script so that the test doesn't have to
get updated for each change to the parameter or default values
'''
def get_default_params():
params = {}
for param in rs_launch.configurable_parameters:
params[param['name']] = param['default']
return params
'''
The format used by rs_launch.py and the LuanchConfiguration yaml files are different,
so the params reused from the rs_launch has to be reformated to be added to yaml file.
'''
def convert_params(params):
cparams = {}
def strtobool (val):
val = val.lower()
if val == 'true':
return True
elif val == 'false':
return False
else:
raise ValueError("invalid truth value %r" % (val,))
for key, value in params.items():
try:
cparams[key] = int(value)
except ValueError:
try:
cparams[key] = float(value)
except ValueError:
try:
cparams[key] = strtobool(value)
except ValueError:
cparams[key] = value.replace("'","")
return cparams
def get_rs_node_description(params):
import tempfile
import yaml
tmp_yaml = tempfile.NamedTemporaryFile(prefix='launch_rs_',delete=False)
params = convert_params(params)
ros_params = {"ros__parameters":params}
camera_params = {params['camera_namespace'] +"/"+params['camera_name']: ros_params}
with open(tmp_yaml.name, 'w') as f:
yaml.dump(camera_params, f)
'''
comment out the '#prefix' line, if you like gdb and want to debug the code, you may have to do more
if you have more than one rs node.
'''
loglevel = "info"
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
loglevel = "debug"
print("loglevel", loglevel)
return launch_ros.actions.Node(
package='realsense2_camera',
namespace=params["camera_namespace"],
name=params["camera_name"],
#prefix=['xterm -e gdb -ex=run --args'],
executable='realsense2_camera_node',
parameters=[tmp_yaml.name],
output='screen',
arguments=['--ros-args', '--log-level', loglevel],
emulate_tty=True,
)
@launch_pytest.fixture
def launch_descr_with_parameters(request):
params = request.param
changed_params = request.param
params = get_default_params()
for key, value in changed_params.items():
params[key] = value
if 'camera_name' not in changed_params:
params['camera_name'] = 'camera_with_params'
device_type = LaunchConfiguration('device_type', default=params['device_type'])
loglevel = "info"
if LOGGER.getEffectiveLevel() <= logging.DEBUG:
loglevel = "debug"
ld = LaunchDescription([
launch_ros.actions.Node(
package='realsense2_ros_mqtt_bridge',
executable='realsense2_ros_mqtt_bridge',
name='realsense2_ros_mqtt_bridge',
arguments=['--ros-args', '--log-level', loglevel],
output='screen',
respawn=True,
respawn_delay=1,
),
get_rs_node_description(params),
DeclareLaunchArgument(
'device_type',
default_value=device_type,
description='Specifying the device type'),
launch_pytest.actions.ReadyToTest(),
]),params
return ld
@@ -0,0 +1,429 @@
# Copyright 2024 RealSense, Inc. All Rights Reserved.
#
# 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.
import collections
import time
import threading
import numpy as np
from rclpy.node import Node
import rclpy
from rcl_interfaces.msg import SetParametersResult
from sensor_msgs.msg import Image
from realsense2_camera_msgs.srv import SafetyPresetRead
from realsense2_camera_msgs.srv import SafetyPresetWrite
from realsense2_camera_msgs.srv import SafetyInterfaceConfigRead
from realsense2_camera_msgs.srv import SafetyInterfaceConfigWrite
from realsense2_camera_msgs.srv import CalibConfigRead
from realsense2_camera_msgs.srv import CalibConfigWrite
from realsense2_camera_msgs.srv import ApplicationConfigRead
from realsense2_camera_msgs.srv import ApplicationConfigWrite
from realsense2_camera_msgs.srv import DeviceInfo
from realsense2_camera_msgs.action import TriggeredCalibration
from rclpy.action import ActionServer, CancelResponse, GoalResponse
from rclpy.callback_groups import ReentrantCallbackGroup
from rclpy.executors import ExternalShutdownException
from rclpy.executors import MultiThreadedExecutor
import logging
LOGGER = logging.getLogger()
'''
This is that holds the test node that listens to a subscription created by a test.
'''
class RSCameraSimulator(Node, threading.Thread):
def __init__(self, namespace="camera", name='RSCameraSimulator'):
LOGGER.debug('Creating node... /' + namespace + '/' + name)
if not rclpy.ok():
rclpy.init()
Node.__init__(self,namespace=namespace, node_name=name)
threading.Thread.__init__(self)
self._stop_event = threading.Event()
self.add_on_set_parameters_callback(self.parameter_callback)
self.color_frame = None
self.depth_frame = None
self.infra1_frame = None
self.infra2_frame = None
self.namespace = namespace
self.name = name
self.goal_queue = collections.deque()
self.goal_queue_lock = threading.Lock()
self.current_goal = None
def run(self):
LOGGER.debug("Thread started...")
loop_count = 0
executor = MultiThreadedExecutor()
while(self._stop_event.is_set() == False):
if loop_count > 10:
LOGGER.debug("Spinning...")
loop_count = 0
if self.color_frame != None:
self.publish_color_frame()
if self.depth_frame != None:
self.publish_depth_frame()
if self.infra1_frame != None:
self.publish_infra1_frame()
if self.infra2_frame != None:
self.publish_infra2_frame()
rclpy.spin_once(self, timeout_sec=0.01, executor=executor)
LOGGER.info("destroying the publisher")
if self.color_frame != None:
self.destroy_publisher(self.color_frame)
if self.depth_frame != None:
self.destroy_publisher(self.depth_frame)
if self.infra1_frame != None:
self.destroy_publisher(self.infra1_frame)
if self.infra2_frame != None:
self.destroy_publisher(self.infra2_frame)
self.destroy_node()
def stop(self):
LOGGER.debug("Setting the stop event...")
self._stop_event.set()
self.join()
def publish_color_frame(self):
msg = Image()
frame = np.zeros((4,4,0), dtype=int)
msg.header.stamp = Node.get_clock(self).now().to_msg()
msg.header.frame_id = 'test'
msg.height = np.shape(frame)[0]
msg.width = np.shape(frame)[1]
msg.encoding = "rgb"
msg.is_bigendian = False
msg.step = np.shape(frame)[2] * np.shape(frame)[1]
msg.data = np.array(frame).tobytes()
# publishes message
# LOGGER.debug("Publishing color frame...")
self.color_frame.publish(msg)
def publish_depth_frame(self):
msg = Image()
frame = np.zeros((4,4,0), dtype=int)
msg.header.stamp = Node.get_clock(self).now().to_msg()
msg.header.frame_id = 'test'
msg.height = np.shape(frame)[0]
msg.width = np.shape(frame)[1]
msg.encoding = "rgb"
msg.is_bigendian = False
msg.step = np.shape(frame)[2] * np.shape(frame)[1]
msg.data = np.array(frame).tobytes()
# publishes message
# LOGGER.debug("Publishing depth frame...")
self.depth_frame.publish(msg)
def publish_infra1_frame(self):
msg = Image()
frame = np.zeros((4,4,0), dtype=int)
msg.header.stamp = Node.get_clock(self).now().to_msg()
msg.header.frame_id = 'test'
msg.height = np.shape(frame)[0]
msg.width = np.shape(frame)[1]
msg.encoding = "rgb"
msg.is_bigendian = False
msg.step = np.shape(frame)[2] * np.shape(frame)[1]
msg.data = np.array(frame).tobytes()
# publishes message
# LOGGER.debug("Publishing infra1 frame...")
self.infra1_frame.publish(msg)
def publish_infra2_frame(self):
msg = Image()
frame = np.zeros((4,4,0), dtype=int)
msg.header.stamp = Node.get_clock(self).now().to_msg()
msg.header.frame_id = 'test'
msg.height = np.shape(frame)[0]
msg.width = np.shape(frame)[1]
msg.encoding = "rgb"
msg.is_bigendian = False
msg.step = np.shape(frame)[2] * np.shape(frame)[1]
msg.data = np.array(frame).tobytes()
# publishes message
# LOGGER.debug("Publishing infra2 frame...")
self.infra2_frame.publish(msg)
def start_publish_color_frame(self):
if self.color_frame != None:
LOGGER.warning(f'Color frame is already being published..')
return
queue = 1
self.color_frame = self.create_publisher(Image, '/' + self.namespace + '/' + self.name + '/color/image_raw', queue)
def start_publish_depth_frame(self):
if self.depth_frame != None:
LOGGER.warning(f'Depth frame is already being published..')
return
queue = 1
self.depth_frame = self.create_publisher(Image, '/' + self.namespace + '/' + self.name + '/depth/image_rect_raw', queue)
def start_publish_infra1_frame(self):
if self.infra1_frame != None:
LOGGER.warning(f'Infra1 frame is already being published..')
return
queue = 1
self.infra1_frame = self.create_publisher(Image, '/' + self.namespace + '/' + self.name + '/infra1/image_rect_raw', queue)
def start_publish_infra2_frame(self):
if self.infra2_frame != None:
LOGGER.warning(f'Infra2 frame is already being published..')
return
queue = 1
self.infra2_frame = self.create_publisher(Image, '/' + self.namespace + '/' + self.name + '/infra2/image_rect_raw', queue)
def add_parameters(self, params):
try:
for param in params:
self.declare_parameter(param['param_name'], param['default_value'])
except Exception as e:
LOGGER.warning(f'An unexpected error occurred: {e}')
def parameter_callback(self, params):
LOGGER.debug("Params changed: " + str(params))
for param in params:
if param.type_ == rclpy.Parameter.Type.INTEGER:
if param.name == 'safety_camera.safety_mode':
self.safety_camera_safety_mode = param.value
LOGGER.info("Safety mode changed to " + str(self.safety_camera_safety_mode))
else:
LOGGER.info(param.name + " (int)value changed to " + str(param.value))
elif param.type_ == rclpy.Parameter.Type.STRING:
LOGGER.info(param.name + " (string)value changed to " + str(param.value))
elif param.type_ == rclpy.Parameter.Type.BOOL:
LOGGER.info(param.name + " (bool)value changed to " + str(param.value))
else:
LOGGER.warning("Unexpected param type: " + str(param.type_) + ". "+ param.name + " (bool)value changed to " + str(param.value))
return SetParametersResult(successful=False)
return SetParametersResult(successful=True)
def create_safety_preset_service(self):
service_name = f'/{self.namespace}/{self.name}/safety_preset_read'
self.safety_preset_read_srv = self.create_service(SafetyPresetRead, service_name, self.safety_preset_read_cb)
service_name = f'/{self.namespace}/{self.name}/safety_preset_write'
self.safety_preset_write_srv = self.create_service(SafetyPresetWrite, service_name, self.safety_preset_write_cb)
self.safety_preset = ["Uninitialized"] * 64
def safety_preset_read_cb(self, request, response):
LOGGER.info(f'Safety preset read for index {request.index}')
response.success = True
response.error_message = ''
if self.safety_preset[request.index] == None:
response.safety_preset = str(request.index)
else:
response.safety_preset = self.safety_preset[request.index]
return response
def safety_preset_write_cb(self, request, response):
LOGGER.info(f'Safety preset write for index {request.index} with data {request.safety_preset}')
response.success = True
response.error_message = ''
self.safety_preset[request.index] = request.safety_preset
return response
def create_safety_interface_config_service(self):
service_name = f'/{self.namespace}/{self.name}/safety_interface_config_read'
self.safety_interface_config_read_srv = self.create_service(SafetyInterfaceConfigRead, service_name, self.safety_interface_config_read_cb)
service_name = f'/{self.namespace}/{self.name}/safety_interface_config_write'
self.safety_interface_config_write_srv = self.create_service(SafetyInterfaceConfigWrite, service_name, self.safety_interface_config_write_cb)
self.safety_interface_config = ["Uninitialized"] * 3
def safety_interface_config_read_cb(self, request, response):
LOGGER.info(f'Safety interface config read for calib location {request.calib_location}')
response.success = True
response.error_message = ''
response.safety_interface_config = self.safety_interface_config[request.calib_location]
return response
def safety_interface_config_write_cb(self, request, response):
LOGGER.info(f'Safety interface config write with data {request.safety_interface_config}')
response.success = True
response.error_message = ''
self.safety_interface_config[2] = request.safety_interface_config
return response
def create_calib_config_service(self):
service_name = f'/{self.namespace}/{self.name}/calib_config_read'
self.calib_config_read_srv = self.create_service(CalibConfigRead, service_name, self.calib_config_read_cb)
service_name = f'/{self.namespace}/{self.name}/calib_config_write'
self.calib_config_srv = self.create_service(CalibConfigWrite, service_name, self.calib_config_write_cb)
self.calib_config = "Uninitialized"
def calib_config_read_cb(self, request, response):
LOGGER.info(f'Calib config read called {response}')
response.success = True
response.error_message = ''
response.calib_config = self.calib_config
return response
def calib_config_write_cb(self, request, response):
LOGGER.info(f'Calibration config write data {request.calib_config}')
response.success = True
response.error_message = ''
self.calib_config = request.calib_config
return response
def create_application_config_service(self):
service_name = f'/{self.namespace}/{self.name}/application_config_read'
self.application_config_read_srv = self.create_service(ApplicationConfigRead, service_name, self.application_config_read_cb)
service_name = f'/{self.namespace}/{self.name}/application_config_write'
self.application_config_srv = self.create_service(ApplicationConfigWrite, service_name, self.application_config_write_cb)
self.application_config = "Uniinitialized"
def application_config_read_cb(self, request, response):
LOGGER.info(f'Application config read called')
response.success = True
response.error_message = ''
response.application_config = self.application_config
return response
def application_config_write_cb(self, request, response):
LOGGER.info(f'Application config write with data {request.application_config}')
response.success = True
response.error_message = ''
self.application_config = request.application_config
return response
def create_device_info_service(self):
LOGGER.info(f'Created the device info service')
service_name = f'/{self.namespace}/{self.name}/device_info'
self.device_info_srv = self.create_service(DeviceInfo, service_name, self.device_info_read_cb)
def device_info_read_cb(self, request, response):
LOGGER.debug(f'DeviceInfo read called \n{request} \n{response}')
response.device_name = "Camera" #string device_name
response.serial_number = "1234" #string serial_number
response.firmware_version = "1.2.3" #string firmware_version
response.usb_type_descriptor = "USB 3.1" #string usb_type_descriptor
response.firmware_update_id = "1" #string firmware_update_id
response.sensors = "color"#string sensors
response.physical_port = "1" #string physical_port
LOGGER.info(f'DeviceInfo response: {response}')
return response
def create_triggered_calibration_action(self):
action_name = f'/{self.namespace}/{self.name}/triggered_calibration'
'''
self._action_server = ActionServer(
self,
TriggeredCalibration,
action_name,
self.triggered_calibration_handler)
'''
self._action_server = ActionServer(
self,
TriggeredCalibration,
action_name,
handle_accepted_callback=self.handle_accepted_callback,
execute_callback=self.triggered_calibration_handler,
goal_callback=self.goal_callback,
cancel_callback=self.cancel_callback,
callback_group=ReentrantCallbackGroup())
def handle_accepted_callback(self, goal_handle):
"""Start or defer execution of an already accepted goal."""
with self.goal_queue_lock:
if self.current_goal is not None:
# Put incoming goal in the queue
self.goal_queue.append(goal_handle)
LOGGER.info('Goal put in the queue')
else:
# Start goal execution right away
self.current_goal = goal_handle
LOGGER.info('Start the execution')
self.current_goal.execute()
def goal_callback(self, goal_request):
"""Accept or reject a client request to begin an action."""
LOGGER.info(f'Received goal request {goal_request}')
return GoalResponse.ACCEPT
def cancel_callback(self, goal_handle):
"""Accept or reject a client request to cancel an action."""
LOGGER.info('Received cancel request')
return CancelResponse.ACCEPT
def triggered_calibration_handler(self, goal_handle):
LOGGER.info(f'Calibration request: {goal_handle.request.json}')
"""Execute a goal."""
try:
LOGGER.info('Executing goal...')
# Append the seeds for the Fibonacci sequence
feedback_msg = TriggeredCalibration.Feedback()
# Start executing the action
for i in range(1, 100):
if goal_handle.is_cancel_requested:
goal_handle.canceled()
LOGGER.info('Goal canceled')
result = TriggeredCalibration.Result()
result.success = False
result.error_msg = 'Canceled'
result.calibration = '{}'
LOGGER.warning('Calibration aborted and senting status success and error message aborted. This needs to be ratified after understanding the actual implementation')
return result
# Update Fibonacci sequence
feedback_msg.progress = float(i)
LOGGER.info(
'Publishing feedback: {0}'.format(feedback_msg.progress))
# Publish the feedback
goal_handle.publish_feedback(feedback_msg)
# Sleep for demonstration purposes
time.sleep(0.01)
goal_handle.succeed()
# Populate result message
result = TriggeredCalibration.Result()
result.success = True
result.calibration = f'{goal_handle.request.json}'
LOGGER.info(
'Returning result: {0}'.format(result))
return result
finally:
with self.goal_queue_lock:
try:
# Start execution of the next goal in the queue.
self.current_goal = self.goal_queue.popleft()
LOGGER.info('Next goal pulled from the queue')
self.current_goal.execute()
except IndexError:
# No goal in the queue.
self.current_goal = None
if __name__ == '__main__':
rclpy.init()
import os
namespace = 'camera'
name = 'camera'
camera = RSCameraSimulator(namespace, name)
camera.start()
os.system("ros2 node list")
import time
rclpy.spin(camera)
time.sleep(10)
rclpy.shutdown()
camera.stop()
LOGGER.info("Test completed")
File diff suppressed because it is too large Load Diff