Skip to content

Commit

Permalink
➕ Implemented mqtt status polling approach as alternative to previous…
Browse files Browse the repository at this point in the history
… status polling.
  • Loading branch information
cardboardcode committed Jan 2, 2025
1 parent eb76540 commit cca67bb
Show file tree
Hide file tree
Showing 12 changed files with 574 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scripts/*
door_adapter_megazo/config.yaml
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM ros:humble-ros-base
RUN apt-get update && apt-get install -y --no-install-recommends \
ros-humble-rmf-door-msgs \
python3-pip && \
pip3 install websockets websocket-client requests && \
pip3 install websockets websocket-client requests gmqtt && \
rm -rf /var/lib/apt/lists/*

# Clone the repository
Expand Down
9 changes: 7 additions & 2 deletions door_adapter_megazo/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
doors:
"Lab_D01": # door name and id
door_auto_closes: False # Set to True if door remains closed until requested open, and automatically closes if it does not receive subsequent open requests.
door_signal_period: 3.0 # Time taken for door signal to be effective, in seconds.
door_signal_period: 10.0 # Time taken for door signal to be effective, in seconds.
continuous_status_polling: False # Whether to keep checking door state when there are no requests
enable_mqtt_status_polling: True # Whether to use MQTT method of polling as alternative to ICEDList status polling.

door_subscriber:
topic_name: "adapter_door_requests"
request_topic_name: "adapter_door_requests"
mqtt_topic_name: "mqtt_door_status"


door_publisher:
topic_name: "door_states"
Expand All @@ -19,5 +22,7 @@ mock: False

# Sample creds:
api_endpoint: "http://icad.megazo.io:8181"
mqtt_broker: "icad.megazo.io"
mqtt_port: 1883
header_key: "INSERT MEGAZO USER HERE"
header_value: "INSERT MEGAZO PASSWORD HERE"
30 changes: 26 additions & 4 deletions door_adapter_megazo/door_adapter_megazo/DoorClientAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ def sign_in_project(self, projectID: str):
except requests.exceptions.ConnectionError as connection_error:
self.ros_logger.warn(f'Connection error: {connection_error}')
return None
except requests.exceptions.HTTPError as http_error:
self.ros_logger.warn(f'HTTP error: {http_error} - Renewing token...')
self.token = self.get_token()
except HTTPError as http_err:
self.ros_logger.warn(f'HTTP error: {http_err}')
return None
Expand Down Expand Up @@ -197,13 +200,21 @@ def get_iced_list_deviceID(self, door_id: str):
except requests.exceptions.ConnectionError as connection_error:
self.ros_logger.warn(f'Connection error: {connection_error}')
return None
except requests.exceptions.HTTPError as http_error:
self.ros_logger.warn(f'HTTP error: {http_error} - Renewing token...')
self.token = self.get_token()
except HTTPError as http_err:
self.ros_logger.warn(f'HTTP error: {http_err}')
return None

def open_door(self, door_id):
"""Return True if the door API server is successful receive open door command."""
device_id, project_id = self.get_iced_list_deviceID(door_id)
try:
device_id, project_id = self.get_iced_list_deviceID(door_id)
except TypeError as e:
self.ros_logger.warn(f'Encountered {e}. Failed to get device_id' +
' or project_id. Returning false.')
return False

if device_id is None or project_id is None:
self.ros_logger.error("Unable to retrieve Device/Project ID. Returning False")
Expand Down Expand Up @@ -250,13 +261,21 @@ def open_door(self, door_id):
except requests.exceptions.ConnectionError as connection_error:
self.ros_logger.warn(f'Connection error: {connection_error}')
return False
except requests.exceptions.HTTPError as http_error:
self.ros_logger.warn(f'HTTP error: {http_error} - Renewing token...')
self.token = self.get_token()
except HTTPError as http_err:
self.ros_logger.warn(f'HTTP error: {http_err}')
return False

def close_door(self, door_id):
"""Return True if the door API server is successful receive open door command."""
device_id, project_id = self.get_iced_list_deviceID(door_id)
try:
device_id, project_id = self.get_iced_list_deviceID(door_id)
except TypeError as e:
self.ros_logger.warn(f'Encountered {e}. Failed to get device_id' +
' or project_id. Returning false.')
return False

if device_id is None or project_id is None:
self.ros_logger.error("Unable to retrieve Device/Project ID. Returning False")
Expand All @@ -270,14 +289,14 @@ def close_door(self, door_id):

path = self.config["api_endpoint"] + "/API/Device/ICED/ControlDoor"

FORCED_SHUTDOWN = 4
NORMAL_SHUTDOWN = 2

payload = {
'UserID': self.config["header_key"],
'Token': self.token,
'data': {
'IDs': [device_id],
'Operate': FORCED_SHUTDOWN
'Operate': NORMAL_SHUTDOWN
}
}
requestHeaders = {'Content-Type': 'application/json'}
Expand All @@ -303,6 +322,9 @@ def close_door(self, door_id):
except requests.exceptions.ConnectionError as connection_error:
self.ros_logger.warn(f'Connection error: {connection_error}')
return False
except requests.exceptions.HTTPError as http_error:
self.ros_logger.warn(f'HTTP error: {http_error} - Renewing token...')
self.token = self.get_token()
except HTTPError as http_err:
self.ros_logger.warn(f'HTTP error: {http_err}')
return False
Expand Down
43 changes: 34 additions & 9 deletions door_adapter_megazo/door_adapter_megazo/door_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ def __init__(self,
door_id,
door_auto_closes,
door_signal_period,
continuous_status_polling):
continuous_status_polling,
enable_mqtt_status_polling):
self.id = door_id
self.door_mode = DoorMode.MODE_CLOSED
self.open_door = False
self.check_status = None # set to None if not enabled
self.door_auto_closes = door_auto_closes
self.door_signal_period = door_signal_period
self.enable_mqtt_status_polling = enable_mqtt_status_polling
if continuous_status_polling:
self.check_status = False

Expand All @@ -49,7 +51,7 @@ def __init__(self,
class DoorAdapter(Node):
"""A Module that bridges between ROS 2 and Megazo Door API."""

def __init__(self, config_yaml):
def __init__(self, config_yaml, is_mqtt_enabled):
super().__init__('door_adapter_megazo')
self.get_logger().info('Initialising [door_adapter_megazo]...')

Expand All @@ -59,6 +61,8 @@ def __init__(self, config_yaml):
door_pub = config_yaml['door_publisher']
door_sub = config_yaml['door_subscriber']
self.mock_adapter = config_yaml.get('mock', False)
self.enable_mqtt = False
self.periodic_timer = None

# Connect to doors
if not self.mock_adapter:
Expand All @@ -77,19 +81,24 @@ def __init__(self, config_yaml):
auto_close = not door_data['door_close_feature']
assert auto_close is not None

if door_data.get('enable_mqtt_status_polling', False):
self.enable_mqtt = True

self.doors[door_id] = Door(door_id,
auto_close,
door_data['door_signal_period'],
door_data.get('continuous_status_polling', False))
door_data.get('continuous_status_polling', False),
door_data.get('enable_mqtt_status_polling', False))

self.door_states_pub = self.create_publisher(
DoorState, door_pub['topic_name'], 100)

self.door_request_sub = self.create_subscription(
DoorRequest, door_sub['topic_name'], self.door_request_cb, 100)
DoorRequest, door_sub['request_topic_name'], self.door_request_cb, 100)

self.periodic_timer = self.create_timer(
self.door_state_publish_period, self.time_cb)
if not self.enable_mqtt:
self.periodic_timer = self.create_timer(
self.door_state_publish_period, self.time_cb)

def door_open_command_request(self, door_data: Door):
"""
Expand Down Expand Up @@ -235,16 +244,32 @@ def main(argv=sys.argv):
parser = argparse.ArgumentParser(
prog="door_adapter",
description="Configure and spin up door adapter for door ")
parser.add_argument("-c", "--config_file", type=str, required=True,
help="Path to the config.yaml file for this door adapter")
parser.add_argument(
"-c", "--config_file",
type=str,
required=True,
help="Path to the config.yaml file for this door adapter"
)
parser.add_argument(
'--enable_mqtt',
type=str,
default=False,
help='Enable or disable the MQTT status polling (true/false)'
)

args = parser.parse_args(args_without_ros[1:])
config_path = args.config_file

print(f"Feature enabled: {args.enable_mqtt}")
is_mqtt_enabled = False
if args.enable_mqtt == "True":
is_mqtt_enabled = True

# Load config and nav graph yamls
with open(config_path, "r", encoding="utf-8") as f:
config_yaml = yaml.safe_load(f)

door_adapter = DoorAdapter(config_yaml)
door_adapter = DoorAdapter(config_yaml, is_mqtt_enabled)
rclpy.spin(door_adapter)

door_adapter.destroy_node()
Expand Down
Loading

0 comments on commit cca67bb

Please # to comment.