import rclpy from pymavlink import mavutil from time import sleep import time import socket import struct # used at fdm_switch_example_two import threading import queue import json def mavlink_analyzer_simple(count = 500): # rclpy.init() # node = rclpy.create_node('mavlink_analyzer_simple') print('Inintializing connection') connection_string="udp:127.0.0.1:14550" connection = mavutil.mavlink_connection(connection_string) print('Catch messages') msg_count = {} start_time = time.time() for _ in range(count): msg = connection.recv_msg() # msg = connection.recv_match(blocking=True) if msg: msg_type = msg.get_type() if msg_type in msg_count: msg_count[msg_type] += 1 else: msg_count[msg_type] = 1 if msg.get_type() == 'SERVO_OUTPUT_RAW': print(msg) pass else: print('No message yet, 1 second for data to fill') sleep(1) print('Messages Type received:') print(msg_count) end_time = time.time() print('Time elapsed: ', end_time - start_time) print('Closing connection') connection.close() def fdm_parser_example(data=None): if data is None: data = bytes.fromhex('1a48e903b1340500e803e803e803e803000000000000000000000000000000000000000000000000') # 1a48e903b1340500e803e803e803e803000000000000000000000000000000000000000000000000 parse_format = 'HHI16H' if len(data) != struct.calcsize(parse_format): print("got packet of len %u, expected %u" % (len(data), struct.calcsize(parse_format))) exit(1) decoded = struct.unpack(parse_format,data) magic = decoded[0] frame_rate_hz = decoded[1] frame_count = decoded[2] pwm = decoded[3:] print("magic: %u, frame_rate_hz: %u, frame_count: %u, pwm: %s" % (magic, frame_rate_hz, frame_count, pwm)) def fdm_switch_example_one(): ''' 這個範例在 SITL 與 Gazebo 之間 做一個簡單的UDP轉發器 沒有轉換數據格式 ''' # make a link get fdm from SITL, as a server sock_s2g = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_s2g.bind(('', 9012)) sock_s2g.settimeout(0.1) # set a target link pass JSON to Gazebo, as a client sock_g2s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_address = ('127.0.0.1', 19012) packet_count = 0 start_time = time.time() while True: try: # 接到SITL的數據 並轉發給Gazebo data_s2g, addr_s2g = sock_s2g.recvfrom(1024) sock_g2s.sendto(data_s2g, server_address) # 得到Gazebo的回應 並轉發給SITL data_g2s, addr_g2s = sock_g2s.recvfrom(1024) sock_s2g.sendto(data_g2s, addr_s2g) # 解析並保存接收到的數據到 JSON 檔案 (SITL -> Gazebo) try: # 解析從 SITL 到 Gazebo 的數據 decoded = struct.unpack('HHI16H', data_s2g) s2g_json = { "magic": decoded[0], "frame_rate_hz": decoded[1], "frame_count": decoded[2], "pwm": list(decoded[3:]) } # 解析從 Gazebo 到 SITL 的數據 # 注意:假設從 Gazebo 返回的數據是字串形式的 JSON try: g2s_data_str = data_g2s.decode('utf-8') # 保存原始數據,適合後續分析 with open('gazebo_response_raw.txt', 'a') as f: f.write(g2s_data_str + '\n') except: # 如果不是 UTF-8 格式,可能是二進制數據 g2s_data_str = str(data_g2s) # 合併數據到一個 JSON 對象 combined_data = { "sitl_to_gazebo": s2g_json, "gazebo_to_sitl": g2s_data_str, "timestamp": time.time() } # 寫入 JSON 檔案 with open('fdm_one_data.json', 'w') as f: json.dump(combined_data, f, indent=2) except Exception as e: print(f"JSON 處理錯誤: {e}") packet_count += 1 except socket.timeout: print(f'no value') #print(f'address:{addr_s2g}') #pass current_time = time.time() elapsed_time = current_time - start_time if elapsed_time >= 1.0: #print(f"Packets received in the last second: {packet_count}") print(f'JSON Pack:{data_g2s}') packet_count = 0 start_time = current_time fdm_parser_example(data_s2g) def fdm_switch_example_two(): ''' 這個範例在 SITL 與 Gazebo 之間 做一個簡單的UDP轉發器 有轉換數據格式 time_usec servo1_raw servo16_raw ''' # read info from SITL via MAVLink connection_string="udp:127.0.0.1:14550" # uart mavlink # MATLAB connection = mavutil.mavlink_connection(connection_string) # set a target link pass JSON to Gazebo, as a client sock_g2s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_address = ('127.0.0.1', 19012) data_queue = queue.Queue() servo_out = [0] * 16 data_queue.put(servo_out) def send_data_from_queue(sock, server_address, q, frame_rate_fdm=600, frame_count_fdm=1): interval = 1.0 / frame_rate_fdm while True: start_time = time.time() try: data = q.get(timeout=0.1) if data is None: break last_data = data except queue.Empty: data = last_data data = struct.pack('HHI16H', 0x481a, frame_rate_fdm, frame_count_fdm, *servo_out) sock.sendto(data, server_address) # 新增:寫入 JSON 檔案 data_json = { "magic": 0x481a, "frame_rate_hz": frame_rate_fdm, "frame_count": frame_count_fdm, "pwm": list(servo_out) } with open('fdm_data.json', 'w') as f: json.dump(data_json, f) frame_count_fdm += 1 fdm_parser_example(data) # debug elapsed_time = time.time() - start_time sleep_time = interval - elapsed_time if sleep_time > 0: sleep(sleep_time) Running = True thread = threading.Thread(target=send_data_from_queue, args=(sock_g2s, server_address, data_queue)) thread.start() while Running: msgb1 = None msg = connection.recv_msg() while msg : if msg.get_type() == 'SERVO_OUTPUT_RAW': msgb1 = msg # break # 這裡不需要break,因為我要最後一個 servo_output_raw 的值 msg = connection.recv_msg() if msgb1: for i in range(16): servo_out[i] = getattr(msgb1, f'servo{i+1}_raw') data_queue.put(servo_out) msgb1 = None # Running = False # debug else: # print('No message yet, 10 ms for data to fill') sleep(0.01) # Example of putting data into the queue # data_queue.put(data) # To stop the thread, you can put None into the queue # data_queue.put(None) def fdm_switch_example_one_with_remote_forwarding(): ''' 這個範例在 SITL 與 Gazebo 之間 做一個簡單的UDP轉發器 並且將JSON數據轉發到指定的遠端IP ''' # make a link get fdm from SITL, as a server sock_s2g = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_s2g.bind(('', 9012)) sock_s2g.settimeout(0.1) # set a target link pass JSON to Gazebo, as a client sock_g2s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_address = ('127.0.0.1', 19012) # 建立與遠端IP的socket連接 remote_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) remote_address = ('140.120.31.130', 9003) # 遠端IP與埠號,您可以根據需要更改埠號 print(f'Setting up forwarding to remote address: {remote_address}') packet_count = 0 start_time = time.time() while True: try: # 接到SITL的數據 並轉發給Gazebo data_s2g, addr_s2g = sock_s2g.recvfrom(1024) sock_g2s.sendto(data_s2g, server_address) # 得到Gazebo的回應 並轉發給SITL data_g2s, addr_g2s = sock_g2s.recvfrom(1024) sock_s2g.sendto(data_g2s, addr_s2g) # 解析並保存接收到的數據到 JSON 檔案 (SITL -> Gazebo) try: # 解析從 SITL 到 Gazebo 的數據 decoded = struct.unpack('HHI16H', data_s2g) s2g_json = { "magic": decoded[0], "frame_rate_hz": decoded[1], "frame_count": decoded[2], "pwm": list(decoded[3:]) } # 解析從 Gazebo 到 SITL 的數據 try: g2s_data_str = data_g2s.decode('utf-8') # 保存原始數據,適合後續分析 with open('gazebo_response_raw.txt', 'a') as f: f.write(g2s_data_str + '\n') except: # 如果不是 UTF-8 格式,可能是二進制數據 g2s_data_str = str(data_g2s) # 合併數據到一個 JSON 對象 combined_data = { "sitl_to_gazebo": s2g_json, "gazebo_to_sitl": g2s_data_str, "timestamp": time.time() } # 寫入 JSON 檔案 with open('fdm_one_data.json', 'w') as f: json.dump(combined_data, f, indent=2) # 將JSON數據轉發到遠端IP remote_socket.sendto(json.dumps(combined_data).encode(), remote_address) except Exception as e: print(f"JSON 處理錯誤: {e}") packet_count += 1 except socket.timeout: print(f'no value') #pass current_time = time.time() elapsed_time = current_time - start_time if elapsed_time >= 1.0: print(f'JSON Pack:{data_g2s}') print(f'Forwarded data to remote IP: 140.120.31.130') packet_count = 0 start_time = current_time fdm_parser_example(data_s2g) def fdm_switch_multiple(num_systems=3): ''' Create multiple UDP forwarders between SITL and Gazebo using threading Each forwarder uses its own set of ports following the pattern: - SITL ports: 9002, 9012, 9022, ... - Gazebo ports: 19002, 19012, 19022, ... Parameters: num_systems (int): Number of forwarding pairs to create ''' # Flag to control thread execution running = True def forwarder_thread(system_id): sitl_port = 9002 + (system_id * 10) gazebo_port = 19002 + (system_id * 10) # Create SITL socket (server) sitl_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sitl_socket.bind(('', sitl_port)) sitl_socket.settimeout(0.1) # Create Gazebo socket (client) gazebo_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) gazebo_socket.settimeout(0.1) gazebo_address = ('127.0.0.1', gazebo_port) print(f"System {system_id}: Forwarding {sitl_port} → {gazebo_port}") packet_count = 0 start_time = time.time() last_data_s2g = None try: while running: try: # Receive from SITL and forward to Gazebo data_s2g, addr_s2g = sitl_socket.recvfrom(1024) last_data_s2g = data_s2g gazebo_socket.sendto(data_s2g, gazebo_address) try: # Get response from Gazebo and send back to SITL data_g2s, addr_g2s = gazebo_socket.recvfrom(1024) sitl_socket.sendto(data_g2s, addr_s2g) # Process and store data try: # Parse SITL to Gazebo data decoded = struct.unpack('HHI16H', data_s2g) s2g_json = { "magic": decoded[0], "frame_rate_hz": decoded[1], "frame_count": decoded[2], "pwm": list(decoded[3:]) } # Try to parse Gazebo to SITL data try: g2s_data_str = data_g2s.decode('utf-8') if packet_count % 50 == 0: with open(f'gazebo_response_raw_{system_id}.txt', 'a') as f: f.write(g2s_data_str + '\n') except: g2s_data_str = str(data_g2s) # Combine data combined_data = { "sitl_to_gazebo": s2g_json, "gazebo_to_sitl": g2s_data_str, "timestamp": time.time(), "system_id": system_id, "sitl_port": sitl_port, "gazebo_port": gazebo_port } # Save to JSON file (once per second) current_time = time.time() elapsed_time = current_time - start_time if elapsed_time >= 1.0: with open(f'fdm_data_system_{system_id}.json', 'w') as f: json.dump(combined_data, f, indent=2) except Exception as e: print(f"System {system_id} - JSON processing error: {e}") packet_count += 1 except socket.timeout: # No response from Gazebo, continue pass except socket.timeout: # No data from SITL, continue pass # Print stats periodically current_time = time.time() elapsed_time = current_time - start_time if elapsed_time >= 1.0: # print(f"System {system_id} - Packets: {packet_count} in the last second") if packet_count > 0 and last_data_s2g: print(f'System {system_id} - JSON Pack: {data_g2s}') fdm_parser_example(last_data_s2g) packet_count = 0 start_time = current_time except Exception as e: print(f"System {system_id} - Error in forwarder thread: {e}") finally: # Clean up resources sitl_socket.close() gazebo_socket.close() print(f"System {system_id} - Forwarder thread terminated") # Create and start threads for each system threads = [] for i in range(num_systems): thread = threading.Thread(target=forwarder_thread, args=(i,)) thread.daemon = True threads.append(thread) thread.start() # Keep the main thread running try: while True: time.sleep(1) except KeyboardInterrupt: print("\nShutting down all forwarders...") running = False time.sleep(1) print("Done.") return def fdm_switch_multiple_with_remote(num_systems=3, remote_ip='140.120.31.130'): ''' Create multiple UDP forwarders between SITL and Gazebo using threading Also forwards the combined data to a remote IP address Each forwarder uses its own set of ports following the pattern: - SITL ports: 9002, 9012, 9022, ... - Gazebo ports: 19002, 19012, 19022, ... - Remote ports: 9003, 9013, 9023, ... (for the remote forwarding) Parameters: num_systems (int): Number of forwarding pairs to create remote_ip (str): IP address for remote forwarding ''' # Flag to control thread execution running = True # Add shared counters for total packets total_packets_sent = 0 total_packets_lock = threading.Lock() # For thread safety def forwarder_thread(system_id): nonlocal total_packets_sent sitl_port = 9002 + (system_id * 10) gazebo_port = 19002 + (system_id * 10) remote_port = 9003 + (system_id * 10) # Create SITL socket (server) sitl_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sitl_socket.bind(('', sitl_port)) sitl_socket.settimeout(0.1) # Create Gazebo socket (client) gazebo_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) gazebo_socket.settimeout(0.1) gazebo_address = ('127.0.0.1', gazebo_port) # Create remote forwarding socket remote_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) remote_address = (remote_ip, remote_port) print(f"System {system_id}: SITL:{sitl_port} → Gazebo:{gazebo_port}, Remote:{remote_ip}:{remote_port}") packet_count = 0 start_time = time.time() last_data_s2g = None forward_counter = 0 # 添加轉發計數器 system_packets_sent = 0 # Local counter for this system try: while running: try: # Receive from SITL and forward to Gazebo data_s2g, addr_s2g = sitl_socket.recvfrom(1024) last_data_s2g = data_s2g gazebo_socket.sendto(data_s2g, gazebo_address) try: # Get response from Gazebo and send back to SITL data_g2s, addr_g2s = gazebo_socket.recvfrom(1024) sitl_socket.sendto(data_g2s, addr_s2g) # Process and store data try: # Parse SITL to Gazebo data decoded = struct.unpack('HHI16H', data_s2g) s2g_json = { "magic": decoded[0], "frame_rate_hz": decoded[1], "frame_count": decoded[2], "pwm": list(decoded[3:]) } # Try to parse Gazebo to SITL data try: g2s_data_str = data_g2s.decode('utf-8') if packet_count % 50 == 0: with open(f'gazebo_response_raw_{system_id}.txt', 'a') as f: f.write(g2s_data_str + '\n') except: g2s_data_str = str(data_g2s) # Combine data combined_data = { "sitl_to_gazebo": s2g_json, "gazebo_to_sitl": g2s_data_str, "timestamp": time.time(), "system_id": system_id, "sitl_port": sitl_port, "gazebo_port": gazebo_port } ''' # 每n筆才轉發一次 forward_counter += 1 if forward_counter >= 18: # Forward data to remote server remote_socket.sendto(json.dumps(combined_data).encode(), remote_address) #print(f"System {system_id} - Send data to remote (第{packet_count}個包)") forward_counter = 0 # 重置計數器 ''' # 使用模運算代替計數器重置 forward_counter += 1 if packet_count % 1 == 0: # Forward data to remote server remote_socket.sendto(json.dumps(combined_data).encode(), remote_address) system_packets_sent += 1 with total_packets_lock: total_packets_sent += 1 # Debug用,看每秒傳了多少筆出去 #print(f"System {system_id} - Send data to remote (第{packet_count}個包)") # Save to JSON file (once per second) current_time = time.time() elapsed_time = current_time - start_time if elapsed_time >= 1.0: with open(f'fdm_data_system_{system_id}.json', 'w') as f: json.dump(combined_data, f, indent=2) except Exception as e: print(f"System {system_id} - JSON processing error: {e}") packet_count += 1 except socket.timeout: # No response from Gazebo, continue pass except socket.timeout: # No data from SITL, continue pass # Print stats periodically current_time = time.time() elapsed_time = current_time - start_time if elapsed_time >= 1.0: print(f"System {system_id} - Packets: {packet_count} in the last second") if packet_count > 0 and last_data_s2g: fdm_parser_example(last_data_s2g) packet_count = 0 start_time = current_time except Exception as e: print(f"System {system_id} - Error in forwarder thread: {e}") finally: # Clean up resources sitl_socket.close() gazebo_socket.close() remote_socket.close() print(f"System {system_id} - Forwarder thread terminated") # Create statistics display thread def stats_display_thread(): start_time = time.time() while running: time.sleep(5) # Display stats every 5 seconds elapsed = time.time() - start_time with total_packets_lock: rate = total_packets_sent / elapsed if elapsed > 0 else 0 print(f"\n===== STATISTICS =====") print(f"Total packets sent: {total_packets_sent}") print(f"Average rate: {rate:.2f} packets/sec") print(f"Running time: {elapsed:.1f} seconds") print(f"======================\n") # Create and start threads for each system threads = [] for i in range(num_systems): thread = threading.Thread(target=forwarder_thread, args=(i,)) thread.daemon = True threads.append(thread) thread.start() # Start stats display thread stats_thread = threading.Thread(target=stats_display_thread) stats_thread.daemon = True stats_thread.start() # Keep the main thread running try: while True: time.sleep(1) except KeyboardInterrupt: print("\nShutting down all forwarders...") running = False time.sleep(1) print(f"Final total packets sent: {total_packets_sent}") print("Done.") return print('Start') #fdm_switch_example_two() #mavlink_analyzer_simple() #fdm_switch_example_one() #fdm_switch_example_one_with_remote_forwarding() #fdm_switch_multiple(1) # Create 3 forwarding pairs fdm_switch_multiple_with_remote(3) # Create 1 forwarding pair with remote forwarding #fdm_parser_example()