From 54c7e22131a48557eb865b16a47784fa5fc31e2b Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 9 Mar 2026 23:06:11 -0400 Subject: [PATCH 1/3] added missing rajant and parse files --- interface_rajant/thirdParty/rajant_parser.py | 161 +++++++++++++++ interface_rajant/thirdParty/rajant_query.py | 203 +++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 interface_rajant/thirdParty/rajant_parser.py create mode 100644 interface_rajant/thirdParty/rajant_query.py diff --git a/interface_rajant/thirdParty/rajant_parser.py b/interface_rajant/thirdParty/rajant_parser.py new file mode 100644 index 0000000..cb52a11 --- /dev/null +++ b/interface_rajant/thirdParty/rajant_parser.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 + +import ast +import yaml +import os +import re +import threading +import pprint +import sys +import pdb + +import rclpy +from rclpy.node import Node +from std_msgs.msg import String +from std_msgs.msg import Int32 +from ament_index_python.packages import get_package_share_directory + +class RajantParser(Node): + def __init__(self, this_robot, robot_configs, radio_configs): + super().__init__('rajant_parser') + + # Check input args + assert isinstance(this_robot, str) + assert isinstance(robot_configs, dict) + assert isinstance(radio_configs, dict) + self.MAC_DICT = {} + + self.this_robot = this_robot + self.robot_cfg = robot_configs + self.radio_cfg = radio_configs + + self.get_logger().info(f"{self.this_robot} - Rajant API Parser - Starting") + + # Generate a standard configuration with a RSSI of -1 + for radio in self.radio_cfg.keys(): + for address in self.radio_cfg[radio]['MAC-address']: + self.MAC_DICT[address] = {} + self.MAC_DICT[address]['rssi'] = -20 + self.MAC_DICT[address]['timestamp'] = self.get_clock().now() + self.MAC_DICT[address]['radio'] = radio + self.MAC_DICT[address]['publisher'] = None + + # Generate publishers for each item in the dict + for mac in self.MAC_DICT.keys(): + for robot in self.robot_cfg.keys(): + if self.MAC_DICT[mac]['radio'] == self.robot_cfg[robot]['using-radio'] and robot != self.this_robot: + self.MAC_DICT[mac]['publisher'] = self.create_publisher(Int32, 'mocha/rajant/rssi/' + robot, 10) + + # Create subscriber + self.subscription = self.create_subscription( + String, + 'mocha/rajant/log', + self.update_dict, + 10 + ) + + + def update_dict(self, data): + # If we did not receive an update after dt, drop the RSSI to -1 + no_rssi = -1 + dt = rclpy.duration.Duration(seconds=20.0) + + # Evaluate the input data as a dictionary + alldata = data.data + data_dict = ast.literal_eval(data.data) + + state = data_dict['watchResponse']['state'] + + # Update the RSSI + for wireless_channel in state.keys(): + for wireless_keys in state[wireless_channel].keys(): + if wireless_keys[0:4] == 'peer': + peer = wireless_keys + if 'rssi' in state[wireless_channel][peer].keys(): + mac = state[wireless_channel][peer]['mac'] + if mac not in self.MAC_DICT.keys(): + self.get_logger().error(f"MAC: {mac} is not in the list of knowns MACs. Is your radio_configs.yaml file correct?") + continue + rssi = state[wireless_channel][peer]['rssi'] + self.MAC_DICT[mac]['rssi'] = rssi + self.MAC_DICT[mac]['timestamp'] = self.get_clock().now() + # Only publish if the publisher is not None + # This avoids an error for a radio that is connected but that is not + # actively used by any robot + if self.MAC_DICT[mac]['publisher'] is not None: + msg = Int32() + msg.data = rssi + self.MAC_DICT[mac]['publisher'].publish(msg) + else: + self.get_logger().debug(f"{self.this_robot} - Rajant API Parser - " + + f"active radio {self.MAC_DICT[mac]['radio']} not assigned to any robot") + elif 'mac' in state[wireless_channel][peer].keys() and 'rssi' not in state[wireless_channel][peer].keys(): + mac = state[wireless_channel][peer]['mac'] + if mac not in self.MAC_DICT.keys(): + self.get_logger().error(f"MAC: {mac} is not in the list of knowns MACs. Is your radio_configs.yaml file correct?") + continue + if self.get_clock().now() - self.MAC_DICT[mac]['timestamp'] > dt: + self.MAC_DICT[mac]['rssi'] = no_rssi + # Only publish if the publisher is not None + # See comment above + if self.MAC_DICT[mac]['publisher'] is not None: + msg = Int32() + msg.data = no_rssi + self.MAC_DICT[mac]['publisher'].publish(msg) + else: + self.get_logger().debug(f"{self.this_robot} - Rajant API Parser - " + + f"active radio {self.MAC_DICT[mac]['radio']} not assigned to any robot") + + +def main(args=None): + rclpy.init(args=args) + + # Create a temporary node to get parameters + temp_node = Node('temp_rajant_parser') + + # Declare parameters + temp_node.declare_parameter('robot_name', 'charon') + temp_node.declare_parameter('robot_configs', '') + temp_node.declare_parameter('radio_configs', '') + + # Get parameters + robot_name = temp_node.get_parameter('robot_name').get_parameter_value().string_value + robot_configs_file = temp_node.get_parameter('robot_configs').get_parameter_value().string_value + radio_configs_file = temp_node.get_parameter('radio_configs').get_parameter_value().string_value + + # Load robot configs + with open(robot_configs_file, "r") as f: + robot_configs = yaml.load(f, Loader=yaml.FullLoader) + if robot_name not in robot_configs.keys(): + temp_node.get_logger().error("Robot not in config file") + temp_node.destroy_node() + rclpy.shutdown() + return + + # Load radio configs + with open(radio_configs_file, "r") as f: + radio_configs = yaml.load(f, Loader=yaml.FullLoader) + radio = robot_configs[robot_name]["using-radio"] + if radio not in radio_configs.keys(): + temp_node.get_logger().error("Radio not in config file") + temp_node.destroy_node() + rclpy.shutdown() + return + + # Clean up temp node + temp_node.destroy_node() + + # Create the actual parser node + rajant_parser = RajantParser(robot_name, robot_configs, radio_configs) + + try: + rclpy.spin(rajant_parser) + except KeyboardInterrupt: + pass + finally: + rajant_parser.destroy_node() + rclpy.shutdown() + + +if __name__ == '__main__': + main() diff --git a/interface_rajant/thirdParty/rajant_query.py b/interface_rajant/thirdParty/rajant_query.py new file mode 100644 index 0000000..2185506 --- /dev/null +++ b/interface_rajant/thirdParty/rajant_query.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +import sys +import subprocess +from threading import Thread +from queue import Queue, Empty +from pprint import pprint +import sys +import os +import time +import yaml +import re +import pdb +import string +import hashlib +import random + +import rclpy +from rclpy.node import Node +from std_msgs.msg import String +from ament_index_python.packages import get_package_share_directory + +def randomNumber(stringLength=4): + """Generate a random string of fixed length """ + number = random.randint(1000, 9999) + return str(number) + + +def enqueue_output(out, queue): + """ Saves the output of the process in a queue to be parsed + afterwards """ + for line in iter(out.readline, b''): + queue.put(line) + out.close() + + +def ping_ip(ip_address): + try: + # Run the ping command with a single ping packet (-c 1) and a timeout of 1 second (-W 1) + result = subprocess.run(["ping", "-c", "1", "-W", "1", ip_address], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True) + return result.returncode == 0 + except subprocess.CalledProcessError: + # An error occurred (ping failed) + return False + + +def line_parser(line_bytes): + """ Returns parsed str version of bytes input line + This is quite magic: rajant output is not yaml but it is very + yamlish. If we replace the { with :, we remove }, and we do some + minor modifications everything works out of the box!""" + line_str = line_bytes.decode('unicode-escape') + line_str = line_str.replace("{", ":") + line_str = line_str.replace("}", "") + # random numbers are added to avoid overwriting the key on the yaml + line_str = re.sub("wireless", + "wireless-" + randomNumber(), line_str) + line_str = re.sub("peer", + "peer-" + randomNumber(), line_str) + # MACs are a little bit more tricky + if line_str.replace(" ", "")[:4] == "mac:": + separator = line_str.find(":") + 2 + mac_str = line_str[separator:] + mac_bytes = bytes(mac_str, 'raw_unicode_escape') + mac_decoded = ":".join(["%02x" % c for c in mac_bytes[1:-2]]) + line_str = line_str[:separator] + mac_decoded + "\n" + return line_str + + +ON_POSIX = 'posix' in sys.builtin_module_names + + +class RajantQueryNode(Node): + def __init__(self): + super().__init__('rajant_query') + + # Declare parameters + self.declare_parameter('robot_name', 'charon') + self.declare_parameter('robot_configs', '') + self.declare_parameter('radio_configs', '') + + # Get parameters + self.robot_name = self.get_parameter('robot_name').get_parameter_value().string_value + robot_configs_file = self.get_parameter('robot_configs').get_parameter_value().string_value + radio_configs_file = self.get_parameter('radio_configs').get_parameter_value().string_value + + # Load robot configs + with open(robot_configs_file, "r") as f: + robot_configs = yaml.load(f, Loader=yaml.FullLoader) + if self.robot_name not in robot_configs.keys(): + self.get_logger().error("Robot not in config file") + return + + # Load radio configs + with open(radio_configs_file, "r") as f: + radio_configs = yaml.load(f, Loader=yaml.FullLoader) + radio = robot_configs[self.robot_name]["using-radio"] + if radio not in radio_configs.keys(): + self.get_logger().error("Radio not in config file") + return + + # Get target IP + rajant_name = robot_configs[self.robot_name]['using-radio'] + if rajant_name in radio_configs.keys(): + self.target_ip = radio_configs[rajant_name]['computed-IP-address'] + else: + self.get_logger().error(f"Radio {rajant_name} for robot {self.robot_name} not found in configs") + return + + # Create ROS publisher + self.pub = self.create_publisher(String, 'mocha/rajant/log', 10) + + # Get package path + try: + ros_path = get_package_share_directory('interface_rajant') + except: + self.get_logger().error("Could not find interface_rajant package") + return + + # Java binary path + self.java_bin = os.path.join(ros_path, 'scripts', + 'thirdParty/watchstate/bcapi-watchstate-11.19.0-SNAPSHOT-jar-with-dependencies.jar') + + # Initialize subprocess variables + self.p = None + self.q = None + self.t = None + + # Start the Java process + self.start_java_process() + + # Go + self.get_logger().info(f"{self.robot_name} - Rajant API Query - Starting on {rajant_name}") + + # Ping the assigned radio + if ping_ip(self.target_ip): + self.get_logger().info(f"{self.robot_name} - Rajant API Query - ping success") + else: + self.get_logger().error(f"{self.robot_name} - Rajant API Query - Rajant ping failed") + return + + # Create timer for main processing loop + self.timer = self.create_timer(1.0, self.process_rajant_data) + + def start_java_process(self): + """Start or restart the Java process""" + self.p = subprocess.Popen(['java', + '-jar', + self.java_bin, + self.target_ip], stdout=subprocess.PIPE, close_fds=ON_POSIX) + self.q = Queue() + self.t = Thread(target=enqueue_output, args=(self.p.stdout, self.q)) + self.t.daemon = True # thread dies with the program + self.t.start() + + def process_rajant_data(self): + """Main processing loop - called by timer""" + if self.t is not None and not self.t.is_alive(): + self.get_logger().error(f'{self.robot_name}: Rajant Java process died! Restarting...') + self.start_java_process() + + try: + line = self.q.get_nowait() + except Empty: + # No output yet + return + else: # got line + answ_array = line_parser(line) + while True: + try: + newline = self.q.get_nowait() + except Empty: + break + else: + answ_array += line_parser(newline) + try: + yaml_res = yaml.load(answ_array, Loader=yaml.Loader) + if type(yaml_res) == type({}): + msg = String() + msg.data = str(yaml_res) + self.pub.publish(msg) + else: + self.get_logger().error(f"{self.robot_name}: YAML from Rajant did not look like an object!") + except yaml.scanner.ScannerError: + self.get_logger().error(f"{self.robot_name}: Could not parse YAML from Rajant!") + + +def main(args=None): + rclpy.init(args=args) + + rajant_query_node = RajantQueryNode() + + try: + rclpy.spin(rajant_query_node) + except KeyboardInterrupt: + pass + finally: + rajant_query_node.destroy_node() + rclpy.shutdown() + + +if __name__ == "__main__": + main() From 9b49ff1c43c94bdc7b971b4fb6f7bcf3f37d475a Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 9 Mar 2026 23:14:24 -0400 Subject: [PATCH 2/3] brought in interface_rajant from zacs configs --- interface_rajant/CMakeLists.txt | 26 +- interface_rajant/interface_rajant/__init__.py | 0 .../interface_rajant/rajant_peer_rssi.py | 357 ------------------ .../launch/rajant_nodes.launch.py | 19 +- interface_rajant/package.xml | 3 - interface_rajant/resource/interface_rajant | 0 .../{thirdParty => scripts}/rajant_parser.py | 0 .../{thirdParty => scripts}/rajant_query.py | 0 .../{ => scripts}/thirdParty/.gitignore | 0 interface_rajant/setup.py | 27 -- interface_rajant/test/test_samples/no_clients | 11 - .../test_samples/single_client_single_iface | 66 ---- .../test/test_samples/single_client_two_iface | 66 ---- .../single_client_two_iface_disconnect | 71 ---- .../single_client_two_iface_reconnect | 40 -- .../test/test_samples/two_clients_two_iface | 92 ----- 16 files changed, 23 insertions(+), 755 deletions(-) delete mode 100644 interface_rajant/interface_rajant/__init__.py delete mode 100755 interface_rajant/interface_rajant/rajant_peer_rssi.py delete mode 100644 interface_rajant/resource/interface_rajant rename interface_rajant/{thirdParty => scripts}/rajant_parser.py (100%) mode change 100644 => 100755 rename interface_rajant/{thirdParty => scripts}/rajant_query.py (100%) mode change 100644 => 100755 rename interface_rajant/{ => scripts}/thirdParty/.gitignore (100%) delete mode 100644 interface_rajant/setup.py delete mode 100644 interface_rajant/test/test_samples/no_clients delete mode 100644 interface_rajant/test/test_samples/single_client_single_iface delete mode 100644 interface_rajant/test/test_samples/single_client_two_iface delete mode 100644 interface_rajant/test/test_samples/single_client_two_iface_disconnect delete mode 100644 interface_rajant/test/test_samples/single_client_two_iface_reconnect delete mode 100644 interface_rajant/test/test_samples/two_clients_two_iface diff --git a/interface_rajant/CMakeLists.txt b/interface_rajant/CMakeLists.txt index df6572d..f2de889 100644 --- a/interface_rajant/CMakeLists.txt +++ b/interface_rajant/CMakeLists.txt @@ -7,26 +7,24 @@ endif() # find dependencies find_package(ament_cmake REQUIRED) -find_package(ament_cmake_python REQUIRED) -find_package(rclpy REQUIRED) - -# Install Python modules -ament_python_install_package(${PROJECT_NAME}) +# uncomment the following section in order to fill in +# further dependencies manually. +# find_package( REQUIRED) +# Install launch files +install(DIRECTORY launch + DESTINATION share/${PROJECT_NAME} +) -# Install executables +# Install Python executables install(PROGRAMS - interface_rajant/rajant_peer_rssi.py + scripts/rajant_query.py + scripts/rajant_parser.py DESTINATION lib/${PROJECT_NAME} ) -# Install launch files -install(DIRECTORY launch/ - DESTINATION share/${PROJECT_NAME}/launch -) - # Install thirdParty directory (contains Java JAR file) -install(DIRECTORY thirdParty/ - DESTINATION share/${PROJECT_NAME}/thirdParty +install(DIRECTORY scripts/thirdParty + DESTINATION share/${PROJECT_NAME}/scripts ) if(BUILD_TESTING) diff --git a/interface_rajant/interface_rajant/__init__.py b/interface_rajant/interface_rajant/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/interface_rajant/interface_rajant/rajant_peer_rssi.py b/interface_rajant/interface_rajant/rajant_peer_rssi.py deleted file mode 100755 index 01a3cb2..0000000 --- a/interface_rajant/interface_rajant/rajant_peer_rssi.py +++ /dev/null @@ -1,357 +0,0 @@ -#!/usr/bin/env python3 -from collections import deque -import os -import queue -import signal -import subprocess -import sys -import threading -import time - -import rclpy -from rclpy.executors import MultiThreadedExecutor -from rclpy.node import Node -import std_msgs.msg -import yaml - -ON_POSIX = 'posix' in sys.builtin_module_names - - -def ping(host): - command = ['ping', '-c', '1', host] - try: - result = subprocess.run(command, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - return result.returncode == 0 - except Exception as e: - print(f'Error pinging {host}: {e}') - return False - - -class PeerPublisher(): - # QUEUE_LENGTH is used to filter repeated RSSI - QUEUE_LENGTH = 3 - - def __init__(self, target_name, ros_node): - assert ros_node is not None - assert target_name is not None and isinstance(target_name, str) - - self.target_name = target_name - self.ros_node = ros_node - - self.last_registered_timestamp = None - self.rssi_ts = [] - - self.published_rssi_queue = deque(maxlen=self.QUEUE_LENGTH) - self.repeated_counter = 0 - - # Create a publisher for the node - topic_name = f'mocha/rajant/rssi/{self.target_name}' - self.ros_node.get_logger().info( - f'{ros_node.this_robot} - Rajant Peer RSSI - Topic /{topic_name} created') - self.pub = self.ros_node.create_publisher(std_msgs.msg.Int32, - topic_name, 10) - - def filter_rssi(self, ts, rssi): - if self.last_registered_timestamp is None: - self.last_registered_timestamp = ts - self.rssi_ts.append((ts, rssi)) - - def publish_all(self, ts): - # Skip if we did not collect info for this node in this session - if self.last_registered_timestamp is None: - return - # Check that current end timestamp and msg timestamps agree - if ts != self.last_registered_timestamp: - self.ros_node.get_logger().error( - f'{self.ros_node.this_robot} - Rajant Peer RSSI' - ' - Timestamp of end message different than' - ' last registered timestamp in publish_all') - return - # Verify that all the timestamps are the same for all the radios - all_ts = [i[0] for i in self.rssi_ts] - if not len(all_ts): - self.ros_node.get_logger().error( - f'{self.ros_node.this_robot} - Rajant Peer RSSI' - ' - Empty list of timestamps in publish_all.') - return - if len(set(all_ts)) != 1: - self.ros_node.get_logger().error( - f'{self.ros_node.this_robot} - Rajant Peer RSSI' - ' - Multiple different timestamps for the' - ' same group in publish_all.') - return - - # Find out the largest RSSI and sum of RSSI - all_rssi = [i[1] for i in self.rssi_ts] - max_rssi = max(all_rssi) - sum_rssi = sum(all_rssi) - - # Clear lists - self.last_registered_timestamp = None - self.rssi_ts = [] - - self.published_rssi_queue.append(sum_rssi) - - # If we published the same sum of RSSI for the last QUEUE_LENGTH times we may drop - # the subscription. This may happen for two reasons: - # - It is a dead radio - # - The RSSI has just not changed - # - # As it is difficult to disambiguate one from the other one, a - # compromise solution is to throttle the topic where we observe this - # behavior by 1/4. - # - # With the current configuration it takes about 30 seconds for a node to - # disconnect, so this would publish one bad message only - # - # print(self.published_rssi_queue, set(self.published_rssi_queue)) - if len(set(self.published_rssi_queue)) == 1 and \ - len(self.published_rssi_queue) == self.QUEUE_LENGTH: - if self.repeated_counter < 4: - self.repeated_counter += 1 - self.ros_node.get_logger().debug( - f'{self.ros_node.this_robot} - Rajant Peer' - f' RSSI - Repeated RSSI for {self.target_name}' - f' for the last {self.QUEUE_LENGTH*3} seconds.' - f' Throttling counter {self.repeated_counter}') - return - - self.ros_node.get_logger().debug( - f'{self.ros_node.this_robot} - Rajant Peer RSSI - Publishing {self.target_name}') - self.repeated_counter = 0 - msg = std_msgs.msg.Int32() - msg.data = max_rssi - self.pub.publish(msg) - - -class RajantPeerRSSI(Node): - def __init__(self): - super().__init__('rajant_peer_rssi') - self.logger = self.get_logger() - - # Handle shutdown signal - self.shutdownTriggered = threading.Event() - self.shutdownTriggered.clear() - - def signal_handler(sig, frame): - if not self.shutdownTriggered.is_set(): - self.logger.warning( - f'{self.this_robot} - Rajant Peer RSSI - Got SIGINT. Triggering shutdown.') - self.shutdown() - signal.signal(signal.SIGINT, signal_handler) - - # Declare parameters - self.declare_parameter('robot_name', '') - self.declare_parameter('robot_configs', '') - self.declare_parameter('radio_configs', '') - self.declare_parameter('bcapi_jar_file', '') - - self.this_robot = self.get_parameter('robot_name').get_parameter_value().string_value - - if len(self.this_robot) == 0: - self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - Empty robot name') - raise ValueError('Empty robot name') - - # Load and check robot configs - self.robot_configs_file = self.get_parameter( - 'robot_configs').get_parameter_value().string_value - try: - with open(self.robot_configs_file, 'r') as f: - self.robot_configs = yaml.load(f, Loader=yaml.FullLoader) - except Exception as e: - self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - robot_configs file') - raise e - if self.this_robot not in self.robot_configs.keys(): - self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - robot_configs file') - raise ValueError('Robot not in config file') - - # Load and check radio configs - self.radio_configs_file = self.get_parameter( - 'radio_configs').get_parameter_value().string_value - try: - with open(self.radio_configs_file, 'r') as f: - self.radio_configs = yaml.load(f, Loader=yaml.FullLoader) - except Exception as e: - self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - radio_configs file') - raise e - self.radio = self.robot_configs[self.this_robot]['using-radio'] - if self.radio not in self.radio_configs.keys(): - self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - radio_configs file') - raise ValueError('Radio {self.radio} not in config file') - - # Get the location of the jar file for bcapi - self.bcapi_jar_file = self.get_parameter( - 'bcapi_jar_file').get_parameter_value().string_value - if not (os.path.isfile(self.bcapi_jar_file) and - self.bcapi_jar_file.endswith('.jar')): - self.get_logger().error( - f'{self.this_robot} - Rajant Peer RSSI - Erroneous BCAPI jar file') - raise ValueError('Erroneous BCAPI file') - - # Get the target ip for the local rajant - rajant_name = self.robot_configs[self.this_robot]['using-radio'] - if rajant_name in self.radio_configs.keys(): - self.target_ip = self.radio_configs[rajant_name]['computed-IP-address'] - else: - self.get_logger().error( - f'{self.this_robot} - Rajant Peer RSSI' - f' - Radio {rajant_name} for robot' - f' {self.this_robot} not found in configs') - raise ValueError( - f'Radio {rajant_name} for robot' - f' {self.this_robot} not found in configs') - - # Ping the local rajant - if not ping(self.target_ip): - self.get_logger().error( - f'{self.this_robot} - Rajant Peer RSSI - Failed to ping {self.target_ip}') - raise ValueError(f'Failed to ping {self.target_ip}') - - # Create the publishers for the peers - self.peers = {} - for peer in self.robot_configs[self.this_robot]['clients']: - # Index peer by computed IP - peer_radio = self.robot_configs[peer]['using-radio'] - computed_ip = self.radio_configs[peer_radio]['computed-IP-address'] - self.peers[computed_ip] = PeerPublisher(peer, self) - - # Invert radio lookup - self.ip_to_radio = {self.radio_configs[radio]['computed-IP-address']: radio - for radio in self.radio_configs} - - # Start the java program and thread to read output - self.start_java_process_and_queue() - - # Thread for processing results - self.process_thread_shutdown = threading.Event() - self.process_thread_shutdown.clear() - self.process_thread = threading.Thread(target=self.process_output, args=()) - self.process_thread.start() - - def shutdown(self): - if self.shutdownTriggered.is_set(): - return - self.shutdownTriggered.set() - # Stop the process_output thread - self.process_thread_shutdown.set() - self.process_thread.join() - # Kill the subprocess - self.java_process.terminate() - # This should end the thread for enqueue_output - self.enqueue_thread.join() - - def enqueue_output(self): - """Save the output of the process in a queue to be parsed afterwards.""" - for line in self.java_process.stdout: - self.process_queue.put(line) - # If the java process dies, we will reach the end of the thread - self.java_process.stdout.close() - - def process_output(self): - restart_count = 0 - while not self.process_thread_shutdown.is_set(): - if self.enqueue_thread is not None and not self.enqueue_thread.is_alive(): - time.sleep(1) - self.get_logger().error( - f'{self.this_robot} - Rajant Peer RSSI - Java process died, restarting') - self.start_java_process_and_queue() - restart_count += 1 - if restart_count == 5: - # shutdown - self.get_logger().error( - f'{self.this_robot} - Rajant Peer RSSI' - ' - Java process died too many times.' - ' Killing node.') - sys.exit(1) - continue - try: - data = self.process_queue.get(timeout=1) - except queue.Empty: - # No data, just continue the loop - continue - # Data comes in lines. Decide what to do based on the output - if data == '\n': - continue - elif data == 'ERR\n': - self.java_process.terminate() - continue - elif 'END,' in data: - # End of transmission, send messages - data = data.replace('END,', '') - data = data.replace(';\n', '') - end_ts = int(data) - for peer in self.peers: - self.peers[peer].publish_all(end_ts) - continue - # Process regular messages - data = data.replace(';\n', '') # Remove end line - ts, iface, peer, rssi = data.split(',') - # Cleanup things - ts = int(ts.replace('Ts:', '')) - iface = iface.replace('Iface:', '') - peer = peer.replace('Peer:', '') - rssi = int(rssi.replace('RSSI:', '')) - # Let the right peer handle it - if peer in self.peers: - self.peers[peer].filter_rssi(ts, rssi) - else: - # Discover the rogue peer with the radio - if peer in self.ip_to_radio: - rogue_radio = self.ip_to_radio[peer] - self.get_logger().debug( - f'{self.this_robot} - Rajant Peer RSSI' - f' - Peer with radio {rogue_radio}' - ' not assigned to any robot') - else: - self.get_logger().debug( - f'{self.this_robot} - Rajant Peer RSSI' - f' - Peer with IP {peer}' - ' not assigned to any robot') - - def start_java_process_and_queue(self): - # Subprocess to run the java BCAPI interface - self.get_logger().info( - f'{self.this_robot} - Rajant Peer RSSI' - f' - Starting Java Process for IP {self.target_ip}') - # Run process in its own separate process group so it does not get - # SIGINT. We will handle that ourselves - popen_kwargs = {} - popen_kwargs['preexec_fn'] = os.setpgrp - self.java_process = subprocess.Popen( - ['java', '-jar', self.bcapi_jar_file, self.target_ip], - stdout=subprocess.PIPE, close_fds=ON_POSIX, - text=True, **popen_kwargs) - self.process_queue = queue.Queue() - self.enqueue_thread = threading.Thread(target=self.enqueue_output, args=()) - self.enqueue_thread.start() - - -def main(args=None): - rclpy.init(args=args) - - try: - rajant_peer_rssi_node = RajantPeerRSSI() - except Exception as e: - print(f'Node initialization failed: {e}') - rclpy.shutdown() - return - - # Use mt executor - mtexecutor = MultiThreadedExecutor(num_threads=4) - mtexecutor.add_node(rajant_peer_rssi_node) - - # Context manager for clean shutdown - try: - while rclpy.ok() and not rajant_peer_rssi_node.shutdownTriggered.is_set(): - mtexecutor.spin_once(timeout_sec=0.1) - except KeyboardInterrupt: - rajant_peer_rssi_node.shutdown() - print('Keyboard interrupt') - except Exception as e: - print(f'Exception: {e}') - rajant_peer_rssi_node.shutdown() - - -if __name__ == '__main__': - main() diff --git a/interface_rajant/launch/rajant_nodes.launch.py b/interface_rajant/launch/rajant_nodes.launch.py index dfb3934..bdc90cd 100644 --- a/interface_rajant/launch/rajant_nodes.launch.py +++ b/interface_rajant/launch/rajant_nodes.launch.py @@ -8,14 +8,17 @@ def generate_launch_description(): - """Launch Rajant interface nodes (query and parser).""" + """ + Launch Rajant interface nodes (query and parser) + """ + # Declare launch arguments robot_name_arg = DeclareLaunchArgument( 'robot_name', default_value='charon', description='Name of the robot' ) - + robot_configs_arg = DeclareLaunchArgument( 'robot_configs', default_value=PathJoinSubstitution([ @@ -24,7 +27,7 @@ def generate_launch_description(): ]), description='Path to robot configuration file' ) - + radio_configs_arg = DeclareLaunchArgument( 'radio_configs', default_value=PathJoinSubstitution([ @@ -33,12 +36,12 @@ def generate_launch_description(): ]), description='Path to radio configuration file' ) - + # Get launch configurations robot_name = LaunchConfiguration('robot_name') robot_configs = LaunchConfiguration('robot_configs') radio_configs = LaunchConfiguration('radio_configs') - + # Define nodes rajant_query_node = Node( package='interface_rajant', @@ -51,7 +54,7 @@ def generate_launch_description(): 'radio_configs': radio_configs }] ) - + rajant_parser_node = Node( package='interface_rajant', executable='rajant_parser.py', @@ -63,11 +66,11 @@ def generate_launch_description(): 'radio_configs': radio_configs }] ) - + return LaunchDescription([ robot_name_arg, robot_configs_arg, radio_configs_arg, rajant_query_node, rajant_parser_node - ]) + ]) \ No newline at end of file diff --git a/interface_rajant/package.xml b/interface_rajant/package.xml index 2cfe84e..6a1fc7e 100644 --- a/interface_rajant/package.xml +++ b/interface_rajant/package.xml @@ -8,9 +8,6 @@ BSD 3-Clause License ament_cmake - ament_cmake_python - - rclpy ament_lint_auto ament_lint_common diff --git a/interface_rajant/resource/interface_rajant b/interface_rajant/resource/interface_rajant deleted file mode 100644 index e69de29..0000000 diff --git a/interface_rajant/thirdParty/rajant_parser.py b/interface_rajant/scripts/rajant_parser.py old mode 100644 new mode 100755 similarity index 100% rename from interface_rajant/thirdParty/rajant_parser.py rename to interface_rajant/scripts/rajant_parser.py diff --git a/interface_rajant/thirdParty/rajant_query.py b/interface_rajant/scripts/rajant_query.py old mode 100644 new mode 100755 similarity index 100% rename from interface_rajant/thirdParty/rajant_query.py rename to interface_rajant/scripts/rajant_query.py diff --git a/interface_rajant/thirdParty/.gitignore b/interface_rajant/scripts/thirdParty/.gitignore similarity index 100% rename from interface_rajant/thirdParty/.gitignore rename to interface_rajant/scripts/thirdParty/.gitignore diff --git a/interface_rajant/setup.py b/interface_rajant/setup.py deleted file mode 100644 index 5b4cb26..0000000 --- a/interface_rajant/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -from setuptools import setup - -package_name = 'interface_rajant' - -setup( - name=package_name, - version='0.0.0', - packages=[package_name], - data_files=[ - ('share/ament_index/resource_index/packages', ['resource/' + package_name]), - ('share/' + package_name, ['package.xml']), - ('share/' + package_name + '/launch', ['launch/rajant_nodes.launch.py']), - ('share/' + package_name + '/thirdParty', ['thirdParty/PeerRSSI-bcapi-11.26.1.jar']), - ], - install_requires=['setuptools'], - zip_safe=True, - maintainer='Fernando Cladera', - maintainer_email='fclad@seas.upenn.edu', - description='The interface_rajant package', - license='BSD 3-Clause License', - tests_require=['pytest'], - entry_points={ - 'console_scripts': [ - 'rajant_peer_rssi = interface_rajant.rajant_peer_rssi:main', - ], - }, -) diff --git a/interface_rajant/test/test_samples/no_clients b/interface_rajant/test/test_samples/no_clients deleted file mode 100644 index b20a70b..0000000 --- a/interface_rajant/test/test_samples/no_clients +++ /dev/null @@ -1,11 +0,0 @@ -END,1756520217550; -END,1756520220634; -END,1756520223717; -END,1756520226798; -END,1756520229880; -END,1756520232961; -END,1756520236039; -END,1756520239119; -END,1756520242199; -END,1756520245274; -END,1756520248354; diff --git a/interface_rajant/test/test_samples/single_client_single_iface b/interface_rajant/test/test_samples/single_client_single_iface deleted file mode 100644 index f53440f..0000000 --- a/interface_rajant/test/test_samples/single_client_single_iface +++ /dev/null @@ -1,66 +0,0 @@ -Ts:1756520588373,Iface:wlan1,Peer:10.6.141.1,RSSI:39; -END,1756520588373; -Ts:1756520591454,Iface:wlan1,Peer:10.6.141.1,RSSI:39; -END,1756520591454; -Ts:1756520594534,Iface:wlan1,Peer:10.6.141.1,RSSI:39; -END,1756520594534; -Ts:1756520597614,Iface:wlan1,Peer:10.6.141.1,RSSI:39; -END,1756520597614; -Ts:1756520600694,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520600694; -Ts:1756520603774,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520603774; -Ts:1756520606851,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520606851; -Ts:1756520609928,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520609928; -Ts:1756520613003,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520613003; -Ts:1756520616081,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520616081; -Ts:1756520619161,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520619161; -Ts:1756520622237,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520622237; -Ts:1756520625316,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520625316; -Ts:1756520628397,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520628397; -Ts:1756520631476,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520631476; -Ts:1756520634555,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520634555; -Ts:1756520637634,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520637634; -Ts:1756520640713,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520640713; -Ts:1756520643792,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520643792; -Ts:1756520646872,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520646872; -Ts:1756520649951,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520649951; -Ts:1756520653026,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520653026; -Ts:1756520656103,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520656103; -Ts:1756520659183,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520659183; -Ts:1756520662262,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520662262; -Ts:1756520665339,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520665339; -Ts:1756520668418,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520668418; -Ts:1756520671497,Iface:wlan1,Peer:10.6.141.1,RSSI:40; -END,1756520671497; -Ts:1756520674575,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520674575; -Ts:1756520677654,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520677654; -Ts:1756520680732,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520680732; -Ts:1756520683810,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520683810; -Ts:1756520686888,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520686888; diff --git a/interface_rajant/test/test_samples/single_client_two_iface b/interface_rajant/test/test_samples/single_client_two_iface deleted file mode 100644 index 3992774..0000000 --- a/interface_rajant/test/test_samples/single_client_two_iface +++ /dev/null @@ -1,66 +0,0 @@ -Ts:1756520257596,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520257596,Iface:wlan1,Peer:10.149.180.1,RSSI:56; -END,1756520257596; -Ts:1756520260678,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520260678,Iface:wlan1,Peer:10.149.180.1,RSSI:56; -END,1756520260678; -Ts:1756520263761,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520263761,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520263761; -Ts:1756520266844,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520266844,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520266844; -Ts:1756520269926,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520269926,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520269926; -Ts:1756520273006,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520273006,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520273006; -Ts:1756520276086,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520276086,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520276086; -Ts:1756520279167,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520279167,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520279167; -Ts:1756520282250,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520282250,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520282250; -Ts:1756520285334,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520285334,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520285334; -Ts:1756520288418,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520288418,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520288418; -Ts:1756520291499,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520291499,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520291499; -Ts:1756520294583,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520294583,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520294583; -Ts:1756520297664,Iface:wlan0,Peer:10.149.180.1,RSSI:54; -Ts:1756520297664,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520297664; -Ts:1756520300748,Iface:wlan0,Peer:10.149.180.1,RSSI:54; -Ts:1756520300748,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520300748; -Ts:1756520303829,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520303829,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520303829; -Ts:1756520306909,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520306909,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520306909; -Ts:1756520309989,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520309989,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520309989; -Ts:1756520313068,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520313068,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520313068; -Ts:1756520316150,Iface:wlan0,Peer:10.149.180.1,RSSI:54; -Ts:1756520316150,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520316150; -Ts:1756520319232,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520319232,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520319232; -Ts:1756520322313,Iface:wlan0,Peer:10.149.180.1,RSSI:53; -Ts:1756520322313,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520322313; diff --git a/interface_rajant/test/test_samples/single_client_two_iface_disconnect b/interface_rajant/test/test_samples/single_client_two_iface_disconnect deleted file mode 100644 index 06ccd89..0000000 --- a/interface_rajant/test/test_samples/single_client_two_iface_disconnect +++ /dev/null @@ -1,71 +0,0 @@ -Ts:1756520789697,Iface:wlan0,Peer:10.149.180.1,RSSI:51; -Ts:1756520789697,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520789697; -Ts:1756520792780,Iface:wlan0,Peer:10.149.180.1,RSSI:51; -Ts:1756520792780,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520792780; -Ts:1756520795861,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520795861,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520795861; -Ts:1756520798940,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520798940,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520798940; -Ts:1756520802017,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520802017,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520802017; -Ts:1756520805097,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520805097,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520805097; -Ts:1756520808179,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520808179,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520808179; -Ts:1756520811260,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520811260,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520811260; -Ts:1756520814340,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520814340,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520814340; -Ts:1756520817420,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520817420,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520817420; -Ts:1756520820500,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520820500,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520820500; -Ts:1756520823582,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520823582,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520823582; -# Disconnect -Ts:1756520826663,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520826663,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520826663; -Ts:1756520829745,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520829745,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520829745; -Ts:1756520832826,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520832826,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520832826; -Ts:1756520835906,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520835906,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520835906; -Ts:1756520838986,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520838986,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520838986; -Ts:1756520842068,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520842068,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520842068; -Ts:1756520845148,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520845148,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520845148; -Ts:1756520848229,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520848229,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520848229; -Ts:1756520851307,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520851307,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520851307; -Ts:1756520854388,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520854388,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520854388; -Ts:1756520857468,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520857468; -END,1756520860547; - diff --git a/interface_rajant/test/test_samples/single_client_two_iface_reconnect b/interface_rajant/test/test_samples/single_client_two_iface_reconnect deleted file mode 100644 index 3bc2e6f..0000000 --- a/interface_rajant/test/test_samples/single_client_two_iface_reconnect +++ /dev/null @@ -1,40 +0,0 @@ -END,1756520900407; -END,1756520903491; -END,1756520906573; -END,1756520909656; -END,1756520912741; -END,1756520915822; -END,1756520918903; -END,1756520921984; -END,1756520925063; -END,1756520928142; -END,1756520931223; -END,1756520934304; -END,1756520937383; -END,1756520940462; -END,1756520943541; -END,1756520946632; -END,1756520949712; -END,1756520952794; -END,1756520955874; -END,1756520958954; -END,1756520962033; -END,1756520965114; -END,1756520968196; -END,1756520971277; -END,1756520974358; -END,1756520977439; -END,1756520980523; -END,1756520983603; -END,1756520986684; -Ts:1756520989764,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520989764,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -END,1756520989764; -Ts:1756520992848,Iface:wlan0,Peer:10.149.180.1,RSSI:47; -Ts:1756520992848,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520992848; -Ts:1756520995930,Iface:wlan0,Peer:10.149.180.1,RSSI:45; -Ts:1756520995930,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -END,1756520995930; -Ts:1756520999011,Iface:wlan0,Peer:10.149.180.1,RSSI:45; -Ts:1756520999011,Iface:wlan1,Peer:10.149.180.1,RSSI:54; diff --git a/interface_rajant/test/test_samples/two_clients_two_iface b/interface_rajant/test/test_samples/two_clients_two_iface deleted file mode 100644 index b4618ed..0000000 --- a/interface_rajant/test/test_samples/two_clients_two_iface +++ /dev/null @@ -1,92 +0,0 @@ -Ts:1756520436245,Iface:wlan0,Peer:10.149.180.1,RSSI:51; -Ts:1756520436245,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -Ts:1756520436245,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520436245; -Ts:1756520439330,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520439330,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520439330,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520439330; -Ts:1756520442415,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520442415,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520442415,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520442415; -Ts:1756520445498,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520445498,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520445498,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520445498; -Ts:1756520448583,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520448583,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520448583,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520448583; -Ts:1756520451665,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520451665,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520451665,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520451665; -Ts:1756520454746,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520454746,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520454746,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520454746; -Ts:1756520457829,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520457829,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520457829,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520457829; -Ts:1756520460911,Iface:wlan0,Peer:10.149.180.1,RSSI:50; -Ts:1756520460911,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520460911,Iface:wlan1,Peer:10.6.141.1,RSSI:42; -END,1756520460911; -Ts:1756520463993,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520463993,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520463993,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520463993; -Ts:1756520467074,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520467074,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520467074,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520467074; -Ts:1756520470153,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520470153,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520470153,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520470153; -Ts:1756520473234,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520473234,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520473234,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520473234; -Ts:1756520476313,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520476313,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520476313,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520476313; -Ts:1756520479393,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520479393,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520479393,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520479393; -Ts:1756520482475,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520482475,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520482475,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520482475; -Ts:1756520485553,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520485553,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520485553,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520485553; -Ts:1756520488633,Iface:wlan0,Peer:10.149.180.1,RSSI:48; -Ts:1756520488633,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520488633,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520488633; -Ts:1756520491715,Iface:wlan0,Peer:10.149.180.1,RSSI:51; -Ts:1756520491715,Iface:wlan1,Peer:10.149.180.1,RSSI:54; -Ts:1756520491715,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520491715; -Ts:1756520494793,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520494793,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520494793,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520494793; -Ts:1756520497870,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520497870,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520497870,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520497870; -Ts:1756520500948,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520500948,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520500948,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520500948; -Ts:1756520504028,Iface:wlan0,Peer:10.149.180.1,RSSI:49; -Ts:1756520504028,Iface:wlan1,Peer:10.149.180.1,RSSI:55; -Ts:1756520504028,Iface:wlan1,Peer:10.6.141.1,RSSI:41; -END,1756520504028; From 7a897479b4b97c3a8f7ca3d68dd7ceeb4fb05326 Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 9 Mar 2026 23:46:58 -0400 Subject: [PATCH 3/3] preserved ros2 structure and tested on dtc basestation --- interface_rajant/CMakeLists.txt | 28 +- interface_rajant/interface_rajant/__init__.py | 0 .../rajant_parser.py | 0 .../interface_rajant/rajant_peer_rssi.py | 357 ++++++++++++++++++ .../rajant_query.py | 2 +- .../launch/rajant_nodes.launch.py | 19 +- interface_rajant/package.xml | 3 + interface_rajant/resource/interface_rajant | 0 interface_rajant/setup.py | 29 ++ interface_rajant/test/test_samples/no_clients | 11 + .../test_samples/single_client_single_iface | 66 ++++ .../test/test_samples/single_client_two_iface | 66 ++++ .../single_client_two_iface_disconnect | 71 ++++ .../single_client_two_iface_reconnect | 40 ++ .../test/test_samples/two_clients_two_iface | 92 +++++ .../{scripts => }/thirdParty/.gitignore | 0 16 files changed, 760 insertions(+), 24 deletions(-) create mode 100644 interface_rajant/interface_rajant/__init__.py rename interface_rajant/{scripts => interface_rajant}/rajant_parser.py (100%) create mode 100755 interface_rajant/interface_rajant/rajant_peer_rssi.py rename interface_rajant/{scripts => interface_rajant}/rajant_query.py (99%) create mode 100644 interface_rajant/resource/interface_rajant create mode 100644 interface_rajant/setup.py create mode 100644 interface_rajant/test/test_samples/no_clients create mode 100644 interface_rajant/test/test_samples/single_client_single_iface create mode 100644 interface_rajant/test/test_samples/single_client_two_iface create mode 100644 interface_rajant/test/test_samples/single_client_two_iface_disconnect create mode 100644 interface_rajant/test/test_samples/single_client_two_iface_reconnect create mode 100644 interface_rajant/test/test_samples/two_clients_two_iface rename interface_rajant/{scripts => }/thirdParty/.gitignore (100%) diff --git a/interface_rajant/CMakeLists.txt b/interface_rajant/CMakeLists.txt index f2de889..4fa417e 100644 --- a/interface_rajant/CMakeLists.txt +++ b/interface_rajant/CMakeLists.txt @@ -7,24 +7,28 @@ endif() # find dependencies find_package(ament_cmake REQUIRED) -# uncomment the following section in order to fill in -# further dependencies manually. -# find_package( REQUIRED) -# Install launch files -install(DIRECTORY launch - DESTINATION share/${PROJECT_NAME} -) +find_package(ament_cmake_python REQUIRED) +find_package(rclpy REQUIRED) + +# Install Python modules +ament_python_install_package(${PROJECT_NAME}) -# Install Python executables +# Install executables install(PROGRAMS - scripts/rajant_query.py - scripts/rajant_parser.py + interface_rajant/rajant_peer_rssi.py + interface_rajant/rajant_query.py + interface_rajant/rajant_parser.py DESTINATION lib/${PROJECT_NAME} ) +# Install launch files +install(DIRECTORY launch/ + DESTINATION share/${PROJECT_NAME}/launch +) + # Install thirdParty directory (contains Java JAR file) -install(DIRECTORY scripts/thirdParty - DESTINATION share/${PROJECT_NAME}/scripts +install(DIRECTORY thirdParty/ + DESTINATION share/${PROJECT_NAME}/thirdParty ) if(BUILD_TESTING) diff --git a/interface_rajant/interface_rajant/__init__.py b/interface_rajant/interface_rajant/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/interface_rajant/scripts/rajant_parser.py b/interface_rajant/interface_rajant/rajant_parser.py similarity index 100% rename from interface_rajant/scripts/rajant_parser.py rename to interface_rajant/interface_rajant/rajant_parser.py diff --git a/interface_rajant/interface_rajant/rajant_peer_rssi.py b/interface_rajant/interface_rajant/rajant_peer_rssi.py new file mode 100755 index 0000000..01a3cb2 --- /dev/null +++ b/interface_rajant/interface_rajant/rajant_peer_rssi.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +from collections import deque +import os +import queue +import signal +import subprocess +import sys +import threading +import time + +import rclpy +from rclpy.executors import MultiThreadedExecutor +from rclpy.node import Node +import std_msgs.msg +import yaml + +ON_POSIX = 'posix' in sys.builtin_module_names + + +def ping(host): + command = ['ping', '-c', '1', host] + try: + result = subprocess.run(command, stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + return result.returncode == 0 + except Exception as e: + print(f'Error pinging {host}: {e}') + return False + + +class PeerPublisher(): + # QUEUE_LENGTH is used to filter repeated RSSI + QUEUE_LENGTH = 3 + + def __init__(self, target_name, ros_node): + assert ros_node is not None + assert target_name is not None and isinstance(target_name, str) + + self.target_name = target_name + self.ros_node = ros_node + + self.last_registered_timestamp = None + self.rssi_ts = [] + + self.published_rssi_queue = deque(maxlen=self.QUEUE_LENGTH) + self.repeated_counter = 0 + + # Create a publisher for the node + topic_name = f'mocha/rajant/rssi/{self.target_name}' + self.ros_node.get_logger().info( + f'{ros_node.this_robot} - Rajant Peer RSSI - Topic /{topic_name} created') + self.pub = self.ros_node.create_publisher(std_msgs.msg.Int32, + topic_name, 10) + + def filter_rssi(self, ts, rssi): + if self.last_registered_timestamp is None: + self.last_registered_timestamp = ts + self.rssi_ts.append((ts, rssi)) + + def publish_all(self, ts): + # Skip if we did not collect info for this node in this session + if self.last_registered_timestamp is None: + return + # Check that current end timestamp and msg timestamps agree + if ts != self.last_registered_timestamp: + self.ros_node.get_logger().error( + f'{self.ros_node.this_robot} - Rajant Peer RSSI' + ' - Timestamp of end message different than' + ' last registered timestamp in publish_all') + return + # Verify that all the timestamps are the same for all the radios + all_ts = [i[0] for i in self.rssi_ts] + if not len(all_ts): + self.ros_node.get_logger().error( + f'{self.ros_node.this_robot} - Rajant Peer RSSI' + ' - Empty list of timestamps in publish_all.') + return + if len(set(all_ts)) != 1: + self.ros_node.get_logger().error( + f'{self.ros_node.this_robot} - Rajant Peer RSSI' + ' - Multiple different timestamps for the' + ' same group in publish_all.') + return + + # Find out the largest RSSI and sum of RSSI + all_rssi = [i[1] for i in self.rssi_ts] + max_rssi = max(all_rssi) + sum_rssi = sum(all_rssi) + + # Clear lists + self.last_registered_timestamp = None + self.rssi_ts = [] + + self.published_rssi_queue.append(sum_rssi) + + # If we published the same sum of RSSI for the last QUEUE_LENGTH times we may drop + # the subscription. This may happen for two reasons: + # - It is a dead radio + # - The RSSI has just not changed + # + # As it is difficult to disambiguate one from the other one, a + # compromise solution is to throttle the topic where we observe this + # behavior by 1/4. + # + # With the current configuration it takes about 30 seconds for a node to + # disconnect, so this would publish one bad message only + # + # print(self.published_rssi_queue, set(self.published_rssi_queue)) + if len(set(self.published_rssi_queue)) == 1 and \ + len(self.published_rssi_queue) == self.QUEUE_LENGTH: + if self.repeated_counter < 4: + self.repeated_counter += 1 + self.ros_node.get_logger().debug( + f'{self.ros_node.this_robot} - Rajant Peer' + f' RSSI - Repeated RSSI for {self.target_name}' + f' for the last {self.QUEUE_LENGTH*3} seconds.' + f' Throttling counter {self.repeated_counter}') + return + + self.ros_node.get_logger().debug( + f'{self.ros_node.this_robot} - Rajant Peer RSSI - Publishing {self.target_name}') + self.repeated_counter = 0 + msg = std_msgs.msg.Int32() + msg.data = max_rssi + self.pub.publish(msg) + + +class RajantPeerRSSI(Node): + def __init__(self): + super().__init__('rajant_peer_rssi') + self.logger = self.get_logger() + + # Handle shutdown signal + self.shutdownTriggered = threading.Event() + self.shutdownTriggered.clear() + + def signal_handler(sig, frame): + if not self.shutdownTriggered.is_set(): + self.logger.warning( + f'{self.this_robot} - Rajant Peer RSSI - Got SIGINT. Triggering shutdown.') + self.shutdown() + signal.signal(signal.SIGINT, signal_handler) + + # Declare parameters + self.declare_parameter('robot_name', '') + self.declare_parameter('robot_configs', '') + self.declare_parameter('radio_configs', '') + self.declare_parameter('bcapi_jar_file', '') + + self.this_robot = self.get_parameter('robot_name').get_parameter_value().string_value + + if len(self.this_robot) == 0: + self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - Empty robot name') + raise ValueError('Empty robot name') + + # Load and check robot configs + self.robot_configs_file = self.get_parameter( + 'robot_configs').get_parameter_value().string_value + try: + with open(self.robot_configs_file, 'r') as f: + self.robot_configs = yaml.load(f, Loader=yaml.FullLoader) + except Exception as e: + self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - robot_configs file') + raise e + if self.this_robot not in self.robot_configs.keys(): + self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - robot_configs file') + raise ValueError('Robot not in config file') + + # Load and check radio configs + self.radio_configs_file = self.get_parameter( + 'radio_configs').get_parameter_value().string_value + try: + with open(self.radio_configs_file, 'r') as f: + self.radio_configs = yaml.load(f, Loader=yaml.FullLoader) + except Exception as e: + self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - radio_configs file') + raise e + self.radio = self.robot_configs[self.this_robot]['using-radio'] + if self.radio not in self.radio_configs.keys(): + self.logger.error(f'{self.this_robot} - Rajant Peer RSSI - radio_configs file') + raise ValueError('Radio {self.radio} not in config file') + + # Get the location of the jar file for bcapi + self.bcapi_jar_file = self.get_parameter( + 'bcapi_jar_file').get_parameter_value().string_value + if not (os.path.isfile(self.bcapi_jar_file) and + self.bcapi_jar_file.endswith('.jar')): + self.get_logger().error( + f'{self.this_robot} - Rajant Peer RSSI - Erroneous BCAPI jar file') + raise ValueError('Erroneous BCAPI file') + + # Get the target ip for the local rajant + rajant_name = self.robot_configs[self.this_robot]['using-radio'] + if rajant_name in self.radio_configs.keys(): + self.target_ip = self.radio_configs[rajant_name]['computed-IP-address'] + else: + self.get_logger().error( + f'{self.this_robot} - Rajant Peer RSSI' + f' - Radio {rajant_name} for robot' + f' {self.this_robot} not found in configs') + raise ValueError( + f'Radio {rajant_name} for robot' + f' {self.this_robot} not found in configs') + + # Ping the local rajant + if not ping(self.target_ip): + self.get_logger().error( + f'{self.this_robot} - Rajant Peer RSSI - Failed to ping {self.target_ip}') + raise ValueError(f'Failed to ping {self.target_ip}') + + # Create the publishers for the peers + self.peers = {} + for peer in self.robot_configs[self.this_robot]['clients']: + # Index peer by computed IP + peer_radio = self.robot_configs[peer]['using-radio'] + computed_ip = self.radio_configs[peer_radio]['computed-IP-address'] + self.peers[computed_ip] = PeerPublisher(peer, self) + + # Invert radio lookup + self.ip_to_radio = {self.radio_configs[radio]['computed-IP-address']: radio + for radio in self.radio_configs} + + # Start the java program and thread to read output + self.start_java_process_and_queue() + + # Thread for processing results + self.process_thread_shutdown = threading.Event() + self.process_thread_shutdown.clear() + self.process_thread = threading.Thread(target=self.process_output, args=()) + self.process_thread.start() + + def shutdown(self): + if self.shutdownTriggered.is_set(): + return + self.shutdownTriggered.set() + # Stop the process_output thread + self.process_thread_shutdown.set() + self.process_thread.join() + # Kill the subprocess + self.java_process.terminate() + # This should end the thread for enqueue_output + self.enqueue_thread.join() + + def enqueue_output(self): + """Save the output of the process in a queue to be parsed afterwards.""" + for line in self.java_process.stdout: + self.process_queue.put(line) + # If the java process dies, we will reach the end of the thread + self.java_process.stdout.close() + + def process_output(self): + restart_count = 0 + while not self.process_thread_shutdown.is_set(): + if self.enqueue_thread is not None and not self.enqueue_thread.is_alive(): + time.sleep(1) + self.get_logger().error( + f'{self.this_robot} - Rajant Peer RSSI - Java process died, restarting') + self.start_java_process_and_queue() + restart_count += 1 + if restart_count == 5: + # shutdown + self.get_logger().error( + f'{self.this_robot} - Rajant Peer RSSI' + ' - Java process died too many times.' + ' Killing node.') + sys.exit(1) + continue + try: + data = self.process_queue.get(timeout=1) + except queue.Empty: + # No data, just continue the loop + continue + # Data comes in lines. Decide what to do based on the output + if data == '\n': + continue + elif data == 'ERR\n': + self.java_process.terminate() + continue + elif 'END,' in data: + # End of transmission, send messages + data = data.replace('END,', '') + data = data.replace(';\n', '') + end_ts = int(data) + for peer in self.peers: + self.peers[peer].publish_all(end_ts) + continue + # Process regular messages + data = data.replace(';\n', '') # Remove end line + ts, iface, peer, rssi = data.split(',') + # Cleanup things + ts = int(ts.replace('Ts:', '')) + iface = iface.replace('Iface:', '') + peer = peer.replace('Peer:', '') + rssi = int(rssi.replace('RSSI:', '')) + # Let the right peer handle it + if peer in self.peers: + self.peers[peer].filter_rssi(ts, rssi) + else: + # Discover the rogue peer with the radio + if peer in self.ip_to_radio: + rogue_radio = self.ip_to_radio[peer] + self.get_logger().debug( + f'{self.this_robot} - Rajant Peer RSSI' + f' - Peer with radio {rogue_radio}' + ' not assigned to any robot') + else: + self.get_logger().debug( + f'{self.this_robot} - Rajant Peer RSSI' + f' - Peer with IP {peer}' + ' not assigned to any robot') + + def start_java_process_and_queue(self): + # Subprocess to run the java BCAPI interface + self.get_logger().info( + f'{self.this_robot} - Rajant Peer RSSI' + f' - Starting Java Process for IP {self.target_ip}') + # Run process in its own separate process group so it does not get + # SIGINT. We will handle that ourselves + popen_kwargs = {} + popen_kwargs['preexec_fn'] = os.setpgrp + self.java_process = subprocess.Popen( + ['java', '-jar', self.bcapi_jar_file, self.target_ip], + stdout=subprocess.PIPE, close_fds=ON_POSIX, + text=True, **popen_kwargs) + self.process_queue = queue.Queue() + self.enqueue_thread = threading.Thread(target=self.enqueue_output, args=()) + self.enqueue_thread.start() + + +def main(args=None): + rclpy.init(args=args) + + try: + rajant_peer_rssi_node = RajantPeerRSSI() + except Exception as e: + print(f'Node initialization failed: {e}') + rclpy.shutdown() + return + + # Use mt executor + mtexecutor = MultiThreadedExecutor(num_threads=4) + mtexecutor.add_node(rajant_peer_rssi_node) + + # Context manager for clean shutdown + try: + while rclpy.ok() and not rajant_peer_rssi_node.shutdownTriggered.is_set(): + mtexecutor.spin_once(timeout_sec=0.1) + except KeyboardInterrupt: + rajant_peer_rssi_node.shutdown() + print('Keyboard interrupt') + except Exception as e: + print(f'Exception: {e}') + rajant_peer_rssi_node.shutdown() + + +if __name__ == '__main__': + main() diff --git a/interface_rajant/scripts/rajant_query.py b/interface_rajant/interface_rajant/rajant_query.py similarity index 99% rename from interface_rajant/scripts/rajant_query.py rename to interface_rajant/interface_rajant/rajant_query.py index 2185506..7e3e54b 100755 --- a/interface_rajant/scripts/rajant_query.py +++ b/interface_rajant/interface_rajant/rajant_query.py @@ -118,7 +118,7 @@ def __init__(self): return # Java binary path - self.java_bin = os.path.join(ros_path, 'scripts', + self.java_bin = os.path.join(ros_path, 'thirdParty/watchstate/bcapi-watchstate-11.19.0-SNAPSHOT-jar-with-dependencies.jar') # Initialize subprocess variables diff --git a/interface_rajant/launch/rajant_nodes.launch.py b/interface_rajant/launch/rajant_nodes.launch.py index bdc90cd..dfb3934 100644 --- a/interface_rajant/launch/rajant_nodes.launch.py +++ b/interface_rajant/launch/rajant_nodes.launch.py @@ -8,17 +8,14 @@ def generate_launch_description(): - """ - Launch Rajant interface nodes (query and parser) - """ - + """Launch Rajant interface nodes (query and parser).""" # Declare launch arguments robot_name_arg = DeclareLaunchArgument( 'robot_name', default_value='charon', description='Name of the robot' ) - + robot_configs_arg = DeclareLaunchArgument( 'robot_configs', default_value=PathJoinSubstitution([ @@ -27,7 +24,7 @@ def generate_launch_description(): ]), description='Path to robot configuration file' ) - + radio_configs_arg = DeclareLaunchArgument( 'radio_configs', default_value=PathJoinSubstitution([ @@ -36,12 +33,12 @@ def generate_launch_description(): ]), description='Path to radio configuration file' ) - + # Get launch configurations robot_name = LaunchConfiguration('robot_name') robot_configs = LaunchConfiguration('robot_configs') radio_configs = LaunchConfiguration('radio_configs') - + # Define nodes rajant_query_node = Node( package='interface_rajant', @@ -54,7 +51,7 @@ def generate_launch_description(): 'radio_configs': radio_configs }] ) - + rajant_parser_node = Node( package='interface_rajant', executable='rajant_parser.py', @@ -66,11 +63,11 @@ def generate_launch_description(): 'radio_configs': radio_configs }] ) - + return LaunchDescription([ robot_name_arg, robot_configs_arg, radio_configs_arg, rajant_query_node, rajant_parser_node - ]) \ No newline at end of file + ]) diff --git a/interface_rajant/package.xml b/interface_rajant/package.xml index 6a1fc7e..2cfe84e 100644 --- a/interface_rajant/package.xml +++ b/interface_rajant/package.xml @@ -8,6 +8,9 @@ BSD 3-Clause License ament_cmake + ament_cmake_python + + rclpy ament_lint_auto ament_lint_common diff --git a/interface_rajant/resource/interface_rajant b/interface_rajant/resource/interface_rajant new file mode 100644 index 0000000..e69de29 diff --git a/interface_rajant/setup.py b/interface_rajant/setup.py new file mode 100644 index 0000000..b92d096 --- /dev/null +++ b/interface_rajant/setup.py @@ -0,0 +1,29 @@ +from setuptools import setup + +package_name = 'interface_rajant' + +setup( + name=package_name, + version='0.0.0', + packages=[package_name], + data_files=[ + ('share/ament_index/resource_index/packages', ['resource/' + package_name]), + ('share/' + package_name, ['package.xml']), + ('share/' + package_name + '/launch', ['launch/rajant_nodes.launch.py']), + ('share/' + package_name + '/thirdParty', ['thirdParty/PeerRSSI-bcapi-11.26.1.jar']), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='Fernando Cladera', + maintainer_email='fclad@seas.upenn.edu', + description='The interface_rajant package', + license='BSD 3-Clause License', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'rajant_peer_rssi = interface_rajant.rajant_peer_rssi:main', + 'rajant_query = interface_rajant.rajant_query:main', + 'rajant_parser = interface_rajant.rajant_parser:main' + ], + }, +) diff --git a/interface_rajant/test/test_samples/no_clients b/interface_rajant/test/test_samples/no_clients new file mode 100644 index 0000000..b20a70b --- /dev/null +++ b/interface_rajant/test/test_samples/no_clients @@ -0,0 +1,11 @@ +END,1756520217550; +END,1756520220634; +END,1756520223717; +END,1756520226798; +END,1756520229880; +END,1756520232961; +END,1756520236039; +END,1756520239119; +END,1756520242199; +END,1756520245274; +END,1756520248354; diff --git a/interface_rajant/test/test_samples/single_client_single_iface b/interface_rajant/test/test_samples/single_client_single_iface new file mode 100644 index 0000000..f53440f --- /dev/null +++ b/interface_rajant/test/test_samples/single_client_single_iface @@ -0,0 +1,66 @@ +Ts:1756520588373,Iface:wlan1,Peer:10.6.141.1,RSSI:39; +END,1756520588373; +Ts:1756520591454,Iface:wlan1,Peer:10.6.141.1,RSSI:39; +END,1756520591454; +Ts:1756520594534,Iface:wlan1,Peer:10.6.141.1,RSSI:39; +END,1756520594534; +Ts:1756520597614,Iface:wlan1,Peer:10.6.141.1,RSSI:39; +END,1756520597614; +Ts:1756520600694,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520600694; +Ts:1756520603774,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520603774; +Ts:1756520606851,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520606851; +Ts:1756520609928,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520609928; +Ts:1756520613003,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520613003; +Ts:1756520616081,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520616081; +Ts:1756520619161,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520619161; +Ts:1756520622237,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520622237; +Ts:1756520625316,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520625316; +Ts:1756520628397,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520628397; +Ts:1756520631476,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520631476; +Ts:1756520634555,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520634555; +Ts:1756520637634,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520637634; +Ts:1756520640713,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520640713; +Ts:1756520643792,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520643792; +Ts:1756520646872,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520646872; +Ts:1756520649951,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520649951; +Ts:1756520653026,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520653026; +Ts:1756520656103,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520656103; +Ts:1756520659183,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520659183; +Ts:1756520662262,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520662262; +Ts:1756520665339,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520665339; +Ts:1756520668418,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520668418; +Ts:1756520671497,Iface:wlan1,Peer:10.6.141.1,RSSI:40; +END,1756520671497; +Ts:1756520674575,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520674575; +Ts:1756520677654,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520677654; +Ts:1756520680732,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520680732; +Ts:1756520683810,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520683810; +Ts:1756520686888,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520686888; diff --git a/interface_rajant/test/test_samples/single_client_two_iface b/interface_rajant/test/test_samples/single_client_two_iface new file mode 100644 index 0000000..3992774 --- /dev/null +++ b/interface_rajant/test/test_samples/single_client_two_iface @@ -0,0 +1,66 @@ +Ts:1756520257596,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520257596,Iface:wlan1,Peer:10.149.180.1,RSSI:56; +END,1756520257596; +Ts:1756520260678,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520260678,Iface:wlan1,Peer:10.149.180.1,RSSI:56; +END,1756520260678; +Ts:1756520263761,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520263761,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520263761; +Ts:1756520266844,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520266844,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520266844; +Ts:1756520269926,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520269926,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520269926; +Ts:1756520273006,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520273006,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520273006; +Ts:1756520276086,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520276086,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520276086; +Ts:1756520279167,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520279167,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520279167; +Ts:1756520282250,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520282250,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520282250; +Ts:1756520285334,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520285334,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520285334; +Ts:1756520288418,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520288418,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520288418; +Ts:1756520291499,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520291499,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520291499; +Ts:1756520294583,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520294583,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520294583; +Ts:1756520297664,Iface:wlan0,Peer:10.149.180.1,RSSI:54; +Ts:1756520297664,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520297664; +Ts:1756520300748,Iface:wlan0,Peer:10.149.180.1,RSSI:54; +Ts:1756520300748,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520300748; +Ts:1756520303829,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520303829,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520303829; +Ts:1756520306909,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520306909,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520306909; +Ts:1756520309989,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520309989,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520309989; +Ts:1756520313068,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520313068,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520313068; +Ts:1756520316150,Iface:wlan0,Peer:10.149.180.1,RSSI:54; +Ts:1756520316150,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520316150; +Ts:1756520319232,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520319232,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520319232; +Ts:1756520322313,Iface:wlan0,Peer:10.149.180.1,RSSI:53; +Ts:1756520322313,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520322313; diff --git a/interface_rajant/test/test_samples/single_client_two_iface_disconnect b/interface_rajant/test/test_samples/single_client_two_iface_disconnect new file mode 100644 index 0000000..06ccd89 --- /dev/null +++ b/interface_rajant/test/test_samples/single_client_two_iface_disconnect @@ -0,0 +1,71 @@ +Ts:1756520789697,Iface:wlan0,Peer:10.149.180.1,RSSI:51; +Ts:1756520789697,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520789697; +Ts:1756520792780,Iface:wlan0,Peer:10.149.180.1,RSSI:51; +Ts:1756520792780,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520792780; +Ts:1756520795861,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520795861,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520795861; +Ts:1756520798940,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520798940,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520798940; +Ts:1756520802017,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520802017,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520802017; +Ts:1756520805097,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520805097,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520805097; +Ts:1756520808179,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520808179,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520808179; +Ts:1756520811260,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520811260,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520811260; +Ts:1756520814340,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520814340,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520814340; +Ts:1756520817420,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520817420,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520817420; +Ts:1756520820500,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520820500,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520820500; +Ts:1756520823582,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520823582,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520823582; +# Disconnect +Ts:1756520826663,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520826663,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520826663; +Ts:1756520829745,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520829745,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520829745; +Ts:1756520832826,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520832826,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520832826; +Ts:1756520835906,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520835906,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520835906; +Ts:1756520838986,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520838986,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520838986; +Ts:1756520842068,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520842068,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520842068; +Ts:1756520845148,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520845148,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520845148; +Ts:1756520848229,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520848229,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520848229; +Ts:1756520851307,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520851307,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520851307; +Ts:1756520854388,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520854388,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520854388; +Ts:1756520857468,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520857468; +END,1756520860547; + diff --git a/interface_rajant/test/test_samples/single_client_two_iface_reconnect b/interface_rajant/test/test_samples/single_client_two_iface_reconnect new file mode 100644 index 0000000..3bc2e6f --- /dev/null +++ b/interface_rajant/test/test_samples/single_client_two_iface_reconnect @@ -0,0 +1,40 @@ +END,1756520900407; +END,1756520903491; +END,1756520906573; +END,1756520909656; +END,1756520912741; +END,1756520915822; +END,1756520918903; +END,1756520921984; +END,1756520925063; +END,1756520928142; +END,1756520931223; +END,1756520934304; +END,1756520937383; +END,1756520940462; +END,1756520943541; +END,1756520946632; +END,1756520949712; +END,1756520952794; +END,1756520955874; +END,1756520958954; +END,1756520962033; +END,1756520965114; +END,1756520968196; +END,1756520971277; +END,1756520974358; +END,1756520977439; +END,1756520980523; +END,1756520983603; +END,1756520986684; +Ts:1756520989764,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520989764,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +END,1756520989764; +Ts:1756520992848,Iface:wlan0,Peer:10.149.180.1,RSSI:47; +Ts:1756520992848,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520992848; +Ts:1756520995930,Iface:wlan0,Peer:10.149.180.1,RSSI:45; +Ts:1756520995930,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +END,1756520995930; +Ts:1756520999011,Iface:wlan0,Peer:10.149.180.1,RSSI:45; +Ts:1756520999011,Iface:wlan1,Peer:10.149.180.1,RSSI:54; diff --git a/interface_rajant/test/test_samples/two_clients_two_iface b/interface_rajant/test/test_samples/two_clients_two_iface new file mode 100644 index 0000000..b4618ed --- /dev/null +++ b/interface_rajant/test/test_samples/two_clients_two_iface @@ -0,0 +1,92 @@ +Ts:1756520436245,Iface:wlan0,Peer:10.149.180.1,RSSI:51; +Ts:1756520436245,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +Ts:1756520436245,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520436245; +Ts:1756520439330,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520439330,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520439330,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520439330; +Ts:1756520442415,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520442415,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520442415,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520442415; +Ts:1756520445498,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520445498,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520445498,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520445498; +Ts:1756520448583,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520448583,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520448583,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520448583; +Ts:1756520451665,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520451665,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520451665,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520451665; +Ts:1756520454746,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520454746,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520454746,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520454746; +Ts:1756520457829,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520457829,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520457829,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520457829; +Ts:1756520460911,Iface:wlan0,Peer:10.149.180.1,RSSI:50; +Ts:1756520460911,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520460911,Iface:wlan1,Peer:10.6.141.1,RSSI:42; +END,1756520460911; +Ts:1756520463993,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520463993,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520463993,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520463993; +Ts:1756520467074,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520467074,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520467074,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520467074; +Ts:1756520470153,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520470153,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520470153,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520470153; +Ts:1756520473234,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520473234,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520473234,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520473234; +Ts:1756520476313,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520476313,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520476313,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520476313; +Ts:1756520479393,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520479393,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520479393,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520479393; +Ts:1756520482475,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520482475,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520482475,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520482475; +Ts:1756520485553,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520485553,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520485553,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520485553; +Ts:1756520488633,Iface:wlan0,Peer:10.149.180.1,RSSI:48; +Ts:1756520488633,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520488633,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520488633; +Ts:1756520491715,Iface:wlan0,Peer:10.149.180.1,RSSI:51; +Ts:1756520491715,Iface:wlan1,Peer:10.149.180.1,RSSI:54; +Ts:1756520491715,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520491715; +Ts:1756520494793,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520494793,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520494793,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520494793; +Ts:1756520497870,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520497870,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520497870,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520497870; +Ts:1756520500948,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520500948,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520500948,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520500948; +Ts:1756520504028,Iface:wlan0,Peer:10.149.180.1,RSSI:49; +Ts:1756520504028,Iface:wlan1,Peer:10.149.180.1,RSSI:55; +Ts:1756520504028,Iface:wlan1,Peer:10.6.141.1,RSSI:41; +END,1756520504028; diff --git a/interface_rajant/scripts/thirdParty/.gitignore b/interface_rajant/thirdParty/.gitignore similarity index 100% rename from interface_rajant/scripts/thirdParty/.gitignore rename to interface_rajant/thirdParty/.gitignore