Skip to content

Commit cd4350e

Browse files
committed
integrate_database.py migration with test robot sync
1 parent 00a0a67 commit cd4350e

File tree

5 files changed

+193
-82
lines changed

5 files changed

+193
-82
lines changed

mocha_core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ if(BUILD_TESTING)
6363
ament_add_pytest_test(test_zmq_comm_node test/test_zmq_comm_node.py)
6464
ament_add_pytest_test(test_synchronize_channel test/test_synchronize_channel.py)
6565
ament_add_pytest_test(test_database_server test/test_database_server.py)
66+
ament_add_pytest_test(test_delay_2robots test/test_delay_2robots.py)
67+
ament_add_pytest_test(test_multiple_robots_sync test/test_multiple_robots_sync.py)
6668
endif()
6769

6870
ament_package()

mocha_core/mocha_core/integrate_database.py

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import traceback
88
from functools import partial
99

10-
import roslaunch
11-
import rospkg
12-
import rospy
10+
import rclpy
11+
import rclpy.logging
12+
import rclpy.time
1313
import yaml
1414
import std_msgs.msg
1515
import subprocess
@@ -31,35 +31,53 @@ def ping(host):
3131

3232

3333
class IntegrateDatabase:
34-
def __init__(self):
35-
rospy.init_node("integrate_database", anonymous=False)
36-
37-
self.this_robot = rospy.get_param("~robot_name")
38-
self.rssi_threshold = rospy.get_param("~rssi_threshold", 20)
34+
def __init__(self, node=None):
35+
# Initialize ROS2 if not already done
36+
if not rclpy.ok():
37+
rclpy.init()
38+
39+
# Create or use provided node
40+
if node is None:
41+
self.node = rclpy.create_node("integrate_database")
42+
else:
43+
self.node = node
44+
45+
self.logger = self.node.get_logger()
46+
47+
# In ROS2, parameters need to be declared before use
48+
self.node.declare_parameter("robot_name", "")
49+
self.node.declare_parameter("rssi_threshold", 20)
50+
self.node.declare_parameter("client_timeout", 6.0)
51+
self.node.declare_parameter("robot_configs", "")
52+
self.node.declare_parameter("radio_configs", "")
53+
self.node.declare_parameter("topic_configs", "")
54+
55+
self.this_robot = self.node.get_parameter("robot_name").get_parameter_value().string_value
56+
self.rssi_threshold = self.node.get_parameter("rssi_threshold").get_parameter_value().integer_value
3957
self.all_channels = []
40-
rospy.loginfo(f"{self.this_robot} - Integrate - " +
41-
f"RSSI threshold: {self.rssi_threshold}")
42-
self.client_timeout = rospy.get_param("~client_timeout", 6.)
43-
rospy.loginfo(f"{self.this_robot} - Integrate - " +
44-
f"Client timeout: {self.client_timeout}")
58+
self.logger.info(f"{self.this_robot} - Integrate - " +
59+
f"RSSI threshold: {self.rssi_threshold}")
60+
self.client_timeout = self.node.get_parameter("client_timeout").get_parameter_value().double_value
61+
self.logger.info(f"{self.this_robot} - Integrate - " +
62+
f"Client timeout: {self.client_timeout}")
4563

4664
# Load and check robot configs
47-
self.robot_configs_file = rospy.get_param("~robot_configs")
65+
self.robot_configs_file = self.node.get_parameter("robot_configs").get_parameter_value().string_value
4866
with open(self.robot_configs_file, "r") as f:
4967
self.robot_configs = yaml.load(f, Loader=yaml.FullLoader)
5068
if self.this_robot not in self.robot_configs.keys():
5169
self.shutdown("Robot not in config file")
5270

5371
# Load and check radio configs
54-
self.radio_configs_file = rospy.get_param("~radio_configs")
72+
self.radio_configs_file = self.node.get_parameter("radio_configs").get_parameter_value().string_value
5573
with open(self.radio_configs_file, "r") as f:
5674
self.radio_configs = yaml.load(f, Loader=yaml.FullLoader)
5775
self.radio = self.robot_configs[self.this_robot]["using-radio"]
5876
if self.radio not in self.radio_configs.keys():
5977
self.shutdown("Radio not in config file")
6078

6179
# Load and check topic configs
62-
self.topic_configs_file = rospy.get_param("~topic_configs")
80+
self.topic_configs_file = self.node.get_parameter("topic_configs").get_parameter_value().string_value
6381
with open(self.topic_configs_file, "r") as f:
6482
self.topic_configs = yaml.load(f, Loader=yaml.FullLoader)
6583
self.node_type = self.robot_configs[self.this_robot]["node-type"]
@@ -69,36 +87,36 @@ def __init__(self):
6987
# Check that we can ping the radios
7088
ip = self.robot_configs[self.this_robot]["IP-address"]
7189
if not ping(ip):
72-
rospy.logerr(f"{self.this_robot} - Integrate - " +
73-
f"Cannot ping self {ip}. Is the radio on?")
74-
rospy.signal_shutdown("Cannot ping self")
75-
rospy.spin()
90+
self.logger.error(f"{self.this_robot} - Integrate - " +
91+
f"Cannot ping self {ip}. Is the radio on?")
92+
self.shutdown("Cannot ping self")
93+
return
7694

77-
# Create a database server object
95+
# Create a database server object with ROS2 node
7896
self.DBServer = ds.DatabaseServer(self.robot_configs,
79-
self.topic_configs)
97+
self.topic_configs, node=self.node)
8098

8199
self.num_robot_in_comm = 0
82100

83101
# Handle possible interruptions
84102
self.interrupted = False
85103

86104
def signal_handler(sig, frame):
87-
rospy.logwarn(f"{self.this_robot} - Integrate - " +
88-
f"Got signal. Killing comm nodes.")
105+
self.logger.warning(f"{self.this_robot} - Integrate - " +
106+
f"Got signal. Killing comm nodes.")
89107
self.interrupted = True
90-
rospy.signal_shutdown("Killed by user")
108+
self.shutdown("Killed by user")
91109
signal.signal(signal.SIGINT, signal_handler)
92110

93-
rospy.loginfo(f"{self.this_robot} - Integrate - " +
94-
"Created all communication channels!")
111+
self.logger.info(f"{self.this_robot} - Integrate - " +
112+
"Created all communication channels!")
95113

96114
# Start comm channels with other robots
97115
self.other_robots = [i for i in list(self.robot_configs.keys()) if i !=
98116
self.this_robot]
99117
for other_robot in self.other_robots:
100118
if other_robot not in self.robot_configs[self.this_robot]["clients"]:
101-
rospy.logwarn(
119+
self.logger.warning(
102120
f"{self.this_robot} - Integrate - "+
103121
f"Skipping channel {self.this_robot}->{other_robot} " +
104122
"as it is not in the graph of this robot"
@@ -115,40 +133,48 @@ def signal_handler(sig, frame):
115133
# Attach a radio trigger to each channel. This will be triggered
116134
# when the RSSI is high enough. You can use another approach here
117135
# such as using a timer to periodically trigger the sync
118-
rospy.Subscriber('ddb/rajant/rssi/' + other_robot,
119-
std_msgs.msg.Int32,
120-
self.rssi_cb,
121-
channel)
136+
def make_callback(ch):
137+
return lambda msg: self.rssi_cb(msg, ch)
138+
139+
self.node.create_subscription(
140+
std_msgs.msg.Int32,
141+
'ddb/rajant/rssi/' + other_robot,
142+
make_callback(channel),
143+
10
144+
)
122145

123146
# Wait for all the robots to start
124147
for _ in range(100):
125-
if self.interrupted or rospy.is_shutdown():
148+
if self.interrupted or not rclpy.ok():
126149
self.shutdown("Killed while waiting for other robots")
127150
return
128-
rospy.sleep(.1)
151+
time.sleep(0.1)
129152

130153
# Spin!
131-
rospy.spin()
154+
try:
155+
rclpy.spin(self.node)
156+
except KeyboardInterrupt:
157+
pass
132158

133159
def shutdown(self, reason):
134160
assert isinstance(reason, str)
135-
rospy.logerr(f"{self.this_robot} - Integrate - " + reason)
161+
self.logger.error(f"{self.this_robot} - Integrate - " + reason)
136162
for channel in self.all_channels:
137163
channel.comm_node.terminate()
138164
self.all_channels.remove(channel)
139-
rospy.logwarn(f"{self.this_robot} - Integrate - " + "Killed Channels")
165+
self.logger.warning(f"{self.this_robot} - Integrate - " + "Killed Channels")
140166
self.DBServer.shutdown()
141-
rospy.logwarn(f"{self.this_robot} - Integrate - " + "Killed DB")
142-
rospy.signal_shutdown(reason)
143-
rospy.logwarn(f"{self.this_robot} - Integrate - " + "Shutting down")
144-
rospy.spin()
167+
self.logger.warning(f"{self.this_robot} - Integrate - " + "Killed DB")
168+
if rclpy.ok():
169+
rclpy.shutdown()
170+
self.logger.warning(f"{self.this_robot} - Integrate - " + "Shutting down")
145171

146172
def rssi_cb(self, data, comm_node):
147173
rssi = data.data
148174
if rssi > self.rssi_threshold:
149175
self.num_robot_in_comm += 1
150176
try:
151-
rospy.loginfo(f"{self.this_robot} <- {comm_node.target_robot}: Triggering comms")
177+
self.logger.info(f"{self.this_robot} <- {comm_node.target_robot}: Triggering comms")
152178
comm_node.trigger_sync()
153179
except:
154180
traceback.print_exception(*sys.exc_info())

mocha_core/test/sample_db.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
import copy
33
from pprint import pprint
44
import sys
5-
sys.path.append('..')
5+
import os
6+
7+
# Add the mocha_core module path for imports
8+
current_dir = os.path.dirname(os.path.abspath(__file__))
9+
mocha_core_path = os.path.join(current_dir, "..", "mocha_core")
10+
sys.path.append(mocha_core_path)
11+
612
import hash_comm
713
import database as db
814
import database_utils as du

mocha_core/test/test_delay_2robots.py

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,66 @@
66
import time
77
from pprint import pprint
88
import multiprocessing
9-
sys.path.append('..')
10-
import get_sample_db as sdb
9+
import os
10+
import rclpy
11+
import rclpy.time
12+
import yaml
13+
14+
# Add the mocha_core module path for imports
15+
current_dir = os.path.dirname(os.path.abspath(__file__))
16+
mocha_core_path = os.path.join(current_dir, "..", "mocha_core")
17+
sys.path.append(mocha_core_path)
18+
19+
import sample_db as sdb
1120
import synchronize_channel as sync
12-
import synchronize_utils as su
13-
import database_server_utils as du
21+
import database as db
22+
import database_utils as du
1423
from colorama import Fore, Style
1524

16-
CONFIG_FILE = "testConfigs/robotConfigs.yml"
25+
# Load robot configs for ROS2
26+
current_dir = os.path.dirname(os.path.abspath(__file__))
27+
robot_configs_path = os.path.join(
28+
current_dir, "..", "config", "testConfigs", "robot_configs.yaml"
29+
)
30+
with open(robot_configs_path, "r") as f:
31+
robot_configs = yaml.load(f, Loader=yaml.FullLoader)
32+
33+
client_timeout = 6.0
1734

1835
class test(unittest.TestCase):
1936
def setUp(self):
2037
test_name = self._testMethodName
2138
print("\n", Fore.RED, 20*"=", test_name, 20*"=", Style.RESET_ALL)
39+
40+
# Initialize ROS2 if not already done
41+
if not rclpy.ok():
42+
rclpy.init()
43+
44+
# Create a ROS2 node for testing
45+
self.node = rclpy.create_node('test_delay_2robots_node')
46+
47+
def tearDown(self):
48+
# Shutdown ROS2 node
49+
if hasattr(self, 'node'):
50+
self.node.destroy_node()
51+
52+
time.sleep(1)
53+
super().tearDown()
2254

2355
def test_delay_run(self):
2456
self.maxDiff = None
2557
db_gs = sdb.get_sample_dbl()
2658
db_r1 = sdb.get_sample_dbl()
2759
db_r2 = sdb.get_sample_dbl()
2860

29-
node_gs_r1 = sync.Channel(db_gs, 'groundstation', 'charon', CONFIG_FILE)
30-
node_gs_r2 = sync.Channel(db_gs, 'groundstation', 'styx', CONFIG_FILE)
61+
node_gs_r1 = sync.Channel(db_gs, 'basestation', 'charon', robot_configs, client_timeout, self.node)
62+
node_gs_r2 = sync.Channel(db_gs, 'basestation', 'styx', robot_configs, client_timeout, self.node)
3163

32-
node_r1_gs = sync.Channel(db_r1, 'charon', 'groundstation', CONFIG_FILE)
33-
node_r2_gs = sync.Channel(db_r2, 'styx', 'groundstation', CONFIG_FILE)
64+
node_r1_gs = sync.Channel(db_r1, 'charon', 'basestation', robot_configs, client_timeout, self.node)
65+
node_r2_gs = sync.Channel(db_r2, 'styx', 'basestation', robot_configs, client_timeout, self.node)
3466

35-
node_r1_r2 = sync.Channel(db_r1, 'charon', 'styx', CONFIG_FILE)
36-
node_r2_r1 = sync.Channel(db_r2, 'styx', 'charon', CONFIG_FILE)
67+
node_r1_r2 = sync.Channel(db_r1, 'charon', 'styx', robot_configs, client_timeout, self.node)
68+
node_r2_r1 = sync.Channel(db_r2, 'styx', 'charon', robot_configs, client_timeout, self.node)
3769

3870
pprint(db_gs.db)
3971
pprint(db_r1.db)
@@ -42,17 +74,17 @@ def test_delay_run(self):
4274
node_gs_r1.run()
4375
node_gs_r2.run()
4476

45-
dbm = su.DBMessage(1, 'Node_1', 1, 2, time.time(), bytes('r1_data', 'utf-8'), False)
46-
du.add_modify_data_dbl(db_r1, dbm)
77+
dbm = db.DBMessage(1, 1, 2, 1, rclpy.time.Time(seconds=int(time.time())), bytes('r1_data', 'utf-8'))
78+
db_r1.add_modify_data(dbm)
4779

4880
node_gs_r1.trigger_sync()
4981
time.sleep(4)
5082

5183
node_r1_gs.run()
5284
node_r2_gs.run()
5385

54-
dbm = su.DBMessage(2, 'Node_2', 2, 1, time.time(), bytes('r2_data', 'utf-8'), False)
55-
du.add_modify_data_dbl(db_r2, dbm)
86+
dbm = db.DBMessage(2, 2, 1, 2, rclpy.time.Time(seconds=int(time.time())), bytes('r2_data', 'utf-8'))
87+
db_r2.add_modify_data(dbm)
5688

5789
node_r1_r2.run()
5890

@@ -85,14 +117,14 @@ def test_multiple_trigger_sync(self):
85117
db_r1 = sdb.get_sample_dbl()
86118
db_r2 = sdb.get_sample_dbl()
87119

88-
node_gs_r1 = sync.Channel(db_gs, 'groundstation', 'charon', CONFIG_FILE)
89-
node_gs_r2 = sync.Channel(db_gs, 'groundstation', 'styx', CONFIG_FILE)
120+
node_gs_r1 = sync.Channel(db_gs, 'basestation', 'charon', robot_configs, client_timeout, self.node)
121+
node_gs_r2 = sync.Channel(db_gs, 'basestation', 'styx', robot_configs, client_timeout, self.node)
90122

91-
node_r1_gs = sync.Channel(db_r1, 'charon', 'groundstation', CONFIG_FILE)
92-
node_r2_gs = sync.Channel(db_r2, 'styx', 'groundstation', CONFIG_FILE)
123+
node_r1_gs = sync.Channel(db_r1, 'charon', 'basestation', robot_configs, client_timeout, self.node)
124+
node_r2_gs = sync.Channel(db_r2, 'styx', 'basestation', robot_configs, client_timeout, self.node)
93125

94-
node_r1_r2 = sync.Channel(db_r1, 'charon', 'styx', CONFIG_FILE)
95-
node_r2_r1 = sync.Channel(db_r2, 'styx', 'charon', CONFIG_FILE)
126+
node_r1_r2 = sync.Channel(db_r1, 'charon', 'styx', robot_configs, client_timeout, self.node)
127+
node_r2_r1 = sync.Channel(db_r2, 'styx', 'charon', robot_configs, client_timeout, self.node)
96128

97129
# pprint(db_gs)
98130
# pprint(db_r1)
@@ -101,8 +133,8 @@ def test_multiple_trigger_sync(self):
101133
# node_r1_r2.run()
102134
node_r2_r1.run()
103135

104-
dbm = su.DBMessage(1, 'Node_1', 1, 2, time.time(), bytes('R1_data', 'utf-8'), False)
105-
du.add_modify_data_dbl(db_r1, dbm)
136+
dbm = db.DBMessage(1, 1, 2, 1, rclpy.time.Time(seconds=int(time.time())), bytes('R1_data', 'utf-8'))
137+
db_r1.add_modify_data(dbm)
106138

107139
node_r2_r1.trigger_sync()
108140
time.sleep(5)

0 commit comments

Comments
 (0)