|
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
"""
|
|
|
|
|
|
測試腳本,用於測試 mavlinkObject.py 中的 mavlink_object 和 async_io_manager 類別
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
|
|
import time
|
|
|
|
|
|
import threading
|
|
|
|
|
|
import socket
|
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
|
|
# 導入要測試的模組
|
|
|
|
|
|
from ..fc_network_adapter.mavlinkObject import (
|
|
|
|
|
|
mavlink_object,
|
|
|
|
|
|
async_io_manager,
|
|
|
|
|
|
MavlinkObjectState,
|
|
|
|
|
|
stream_bridge_ring,
|
|
|
|
|
|
return_packet_ring
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 預先定義好的真實 MAVLink heartbeat 封包 (MAVLink 1.0 格式)
|
|
|
|
|
|
# Format: STX(0xFE) + LEN + SEQ + SYS + COMP + MSG_ID + PAYLOAD(9 bytes for heartbeat) + CRC(2 bytes)
|
|
|
|
|
|
HEARTBEAT_PACKET_1 = bytes([
|
|
|
|
|
|
0xFE, # STX (MAVLink 1.0)
|
|
|
|
|
|
0x09, # payload length (9 bytes)
|
|
|
|
|
|
0x00, # sequence
|
|
|
|
|
|
0x01, # system ID = 1
|
|
|
|
|
|
0x01, # component ID = 1
|
|
|
|
|
|
0x00, # message ID (HEARTBEAT = 0)
|
|
|
|
|
|
# Payload (9 bytes): custom_mode(4), type(1), autopilot(1), base_mode(1), system_status(1), mavlink_version(1)
|
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, # custom_mode = 0
|
|
|
|
|
|
0x02, # type = MAV_TYPE_QUADROTOR (2)
|
|
|
|
|
|
0x03, # autopilot = MAV_AUTOPILOT_ARDUPILOTMEGA (3)
|
|
|
|
|
|
0x40, # base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED (64)
|
|
|
|
|
|
0x03, # system_status = MAV_STATE_STANDBY (3)
|
|
|
|
|
|
0x03, # mavlink_version = 3
|
|
|
|
|
|
0x62, 0x8E # CRC (simplified placeholder)
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
HEARTBEAT_PACKET_2 = bytes([
|
|
|
|
|
|
0xFE, # STX
|
|
|
|
|
|
0x09, # payload length
|
|
|
|
|
|
0x01, # sequence (增加)
|
|
|
|
|
|
0x01, # system ID = 1
|
|
|
|
|
|
0x01, # component ID = 1
|
|
|
|
|
|
0x00, # message ID (HEARTBEAT = 0)
|
|
|
|
|
|
0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
|
0x02, 0x03, 0x41, 0x03, 0x03,
|
|
|
|
|
|
0x33, 0xEC
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
HEARTBEAT_PACKET_3 = bytes([
|
|
|
|
|
|
0xFE, # STX
|
|
|
|
|
|
0x09, # payload length
|
|
|
|
|
|
0x02, # sequence
|
|
|
|
|
|
0x02, # system ID = 2
|
|
|
|
|
|
0x01, # component ID = 1
|
|
|
|
|
|
0x00, # message ID (HEARTBEAT = 0)
|
|
|
|
|
|
0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
|
0x02, 0x03, 0x42, 0x03, 0x03,
|
|
|
|
|
|
0x37, 0x44
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MockMavlinkSocket:
|
|
|
|
|
|
"""模擬 Mavlink Socket 的類別,用於測試
|
|
|
|
|
|
使用真實的 MAVLink 封包,而不是模擬的訊息對象
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, test_packets=None):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Args:
|
|
|
|
|
|
test_packets: list of bytes,每個元素都是完整的 MAVLink 封包
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.closed = False
|
|
|
|
|
|
self.test_packets = test_packets or []
|
|
|
|
|
|
self.packet_index = 0
|
|
|
|
|
|
self.written_data = []
|
|
|
|
|
|
|
|
|
|
|
|
# 使用 pymavlink 來解析封包
|
|
|
|
|
|
from pymavlink import mavutil
|
|
|
|
|
|
self.mav_parser = mavutil.mavlink.MAVLink(self)
|
|
|
|
|
|
|
|
|
|
|
|
def recv_msg(self):
|
|
|
|
|
|
"""返回解析後的 MAVLink 訊息對象"""
|
|
|
|
|
|
if not self.test_packets or self.packet_index >= len(self.test_packets):
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
packet = self.test_packets[self.packet_index]
|
|
|
|
|
|
self.packet_index += 1
|
|
|
|
|
|
|
|
|
|
|
|
# 使用 pymavlink 解析封包
|
|
|
|
|
|
try:
|
|
|
|
|
|
for byte in packet:
|
|
|
|
|
|
msg = self.mav_parser.parse_char(bytes([byte]))
|
|
|
|
|
|
if msg:
|
|
|
|
|
|
return msg
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"Error parsing packet: {e}")
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def write(self, data):
|
|
|
|
|
|
"""寫入數據(用於檢查轉發)"""
|
|
|
|
|
|
self.written_data.append(data)
|
|
|
|
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
|
|
"""關閉 socket"""
|
|
|
|
|
|
self.closed = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestMavlinkObject(unittest.TestCase):
|
|
|
|
|
|
"""測試 mavlink_object 類別的獨立功能"""
|
|
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
|
"""在每個測試方法執行前準備環境"""
|
|
|
|
|
|
# 清空全局變數
|
|
|
|
|
|
mavlink_object.mavlinkObjects = {}
|
|
|
|
|
|
mavlink_object.socket_num = 0
|
|
|
|
|
|
|
|
|
|
|
|
# 清空 ring buffer
|
|
|
|
|
|
stream_bridge_ring.clear()
|
|
|
|
|
|
return_packet_ring.clear()
|
|
|
|
|
|
|
|
|
|
|
|
# 創建模擬的 socket,使用真實封包
|
|
|
|
|
|
self.mock_socket = MockMavlinkSocket([HEARTBEAT_PACKET_1])
|
|
|
|
|
|
|
|
|
|
|
|
# 創建測試對象
|
|
|
|
|
|
self.mavlink_obj = mavlink_object(self.mock_socket)
|
|
|
|
|
|
|
|
|
|
|
|
def test_initialization(self):
|
|
|
|
|
|
"""測試 mavlink_object 初始化是否正確"""
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.socket_id, 0)
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.state, MavlinkObjectState.INIT)
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.target_sockets), 0)
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.bridge_msg_types, [0])
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.return_msg_types, [])
|
|
|
|
|
|
|
|
|
|
|
|
def test_add_remove_target_socket(self):
|
|
|
|
|
|
"""測試添加和移除目標端口功能"""
|
|
|
|
|
|
# 添加目標端口
|
|
|
|
|
|
self.assertTrue(self.mavlink_obj.add_target_socket(1))
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.target_sockets), 1)
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.target_sockets[0], 1)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertTrue(self.mavlink_obj.add_target_socket(2))
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.target_sockets), 2)
|
|
|
|
|
|
self.assertIn(2, self.mavlink_obj.target_sockets)
|
|
|
|
|
|
|
|
|
|
|
|
# 嘗試添加已存在的端口
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.add_target_socket(1))
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.target_sockets), 2)
|
|
|
|
|
|
|
|
|
|
|
|
# 嘗試添加自己的端口
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.add_target_socket(0))
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.target_sockets), 2)
|
|
|
|
|
|
|
|
|
|
|
|
# 移除端口
|
|
|
|
|
|
self.assertTrue(self.mavlink_obj.remove_target_socket(2))
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.target_sockets), 1)
|
|
|
|
|
|
|
|
|
|
|
|
# 嘗試移除不存在的端口
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.remove_target_socket(2))
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_message_types(self):
|
|
|
|
|
|
"""測試設置訊息類型功能"""
|
|
|
|
|
|
# 設置橋接器訊息類型
|
|
|
|
|
|
self.assertTrue(self.mavlink_obj.set_bridge_message_types([0, 30]))
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.bridge_msg_types, [0, 30])
|
|
|
|
|
|
|
|
|
|
|
|
# 設置回傳處理器訊息類型
|
|
|
|
|
|
self.assertTrue(self.mavlink_obj.set_return_message_types([32]))
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj.return_msg_types, [32])
|
|
|
|
|
|
|
|
|
|
|
|
# 測試無效的訊息類型
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.set_bridge_message_types("invalid"))
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.set_return_message_types([0, "invalid"]))
|
|
|
|
|
|
|
|
|
|
|
|
def test_send_message_validation(self):
|
|
|
|
|
|
"""測試 send_message 的數據驗證功能(不需要啟動 manager)"""
|
|
|
|
|
|
# 測試非運行狀態下發送消息
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.send_message(HEARTBEAT_PACKET_1))
|
|
|
|
|
|
|
|
|
|
|
|
# 測試無效的數據類型
|
|
|
|
|
|
self.mavlink_obj.state = MavlinkObjectState.RUNNING # 臨時設置狀態
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.send_message("invalid"))
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.send_message(123))
|
|
|
|
|
|
|
|
|
|
|
|
# 測試太短的封包
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.send_message(bytes([0xFE, 0x00])))
|
|
|
|
|
|
|
|
|
|
|
|
# 測試無效的起始標記
|
|
|
|
|
|
invalid_packet = bytes([0xFF, 0x09, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00])
|
|
|
|
|
|
self.assertFalse(self.mavlink_obj.send_message(invalid_packet))
|
|
|
|
|
|
|
|
|
|
|
|
# 測試有效的封包可以加入佇列
|
|
|
|
|
|
self.assertTrue(self.mavlink_obj.send_message(HEARTBEAT_PACKET_1))
|
|
|
|
|
|
self.assertEqual(len(self.mavlink_obj.outgoing_msgs), 1)
|
|
|
|
|
|
|
|
|
|
|
|
self.mavlink_obj.state = MavlinkObjectState.INIT # 恢復狀態
|
|
|
|
|
|
|
|
|
|
|
|
class TestAsyncIOManager(unittest.TestCase):
|
|
|
|
|
|
"""測試 async_io_manager 類別的獨立功能"""
|
|
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
|
"""在每個測試方法執行前準備環境"""
|
|
|
|
|
|
# 清空全局變數
|
|
|
|
|
|
mavlink_object.mavlinkObjects = {}
|
|
|
|
|
|
mavlink_object.socket_num = 0
|
|
|
|
|
|
|
|
|
|
|
|
# 清空 ring buffer
|
|
|
|
|
|
stream_bridge_ring.clear()
|
|
|
|
|
|
return_packet_ring.clear()
|
|
|
|
|
|
|
|
|
|
|
|
# 創建 async_io_manager 實例
|
|
|
|
|
|
self.manager = async_io_manager()
|
|
|
|
|
|
|
|
|
|
|
|
# 創建模擬 mavlink 對象,使用真實封包
|
|
|
|
|
|
self.mock_socket1 = MockMavlinkSocket([HEARTBEAT_PACKET_1, HEARTBEAT_PACKET_2])
|
|
|
|
|
|
self.mock_socket2 = MockMavlinkSocket([HEARTBEAT_PACKET_3])
|
|
|
|
|
|
|
|
|
|
|
|
self.mavlink_obj1 = mavlink_object(self.mock_socket1)
|
|
|
|
|
|
self.mavlink_obj2 = mavlink_object(self.mock_socket2)
|
|
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
|
"""在每個測試方法執行後清理環境"""
|
|
|
|
|
|
if self.manager.running:
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
def test_singleton_pattern(self):
|
|
|
|
|
|
"""測試 async_io_manager 的單例模式"""
|
|
|
|
|
|
manager1 = async_io_manager()
|
|
|
|
|
|
manager2 = async_io_manager()
|
|
|
|
|
|
self.assertIs(manager1, manager2)
|
|
|
|
|
|
|
|
|
|
|
|
def test_start_stop(self):
|
|
|
|
|
|
"""測試 async_io_manager 的啟動和停止功能"""
|
|
|
|
|
|
# 啟動管理器
|
|
|
|
|
|
self.manager.start()
|
|
|
|
|
|
self.assertTrue(self.manager.running)
|
|
|
|
|
|
self.assertIsNotNone(self.manager.thread)
|
|
|
|
|
|
|
|
|
|
|
|
# 再次啟動應該沒有效果
|
|
|
|
|
|
old_thread = self.manager.thread
|
|
|
|
|
|
self.manager.start()
|
|
|
|
|
|
self.assertIs(self.manager.thread, old_thread)
|
|
|
|
|
|
|
|
|
|
|
|
# 停止管理器
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
self.assertFalse(self.manager.running)
|
|
|
|
|
|
|
|
|
|
|
|
# 最多等待 5 秒讓線程結束
|
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
|
while self.manager.thread.is_alive() and time.time() - start_time < 5:
|
|
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
self.assertFalse(self.manager.thread.is_alive())
|
|
|
|
|
|
|
|
|
|
|
|
def test_add_remove_objects(self):
|
|
|
|
|
|
"""測試添加和移除 mavlink_object"""
|
|
|
|
|
|
# 啟動管理器
|
|
|
|
|
|
self.manager.start()
|
|
|
|
|
|
time.sleep(0.5) # 等待事件循環啟動
|
|
|
|
|
|
|
|
|
|
|
|
# 添加對象
|
|
|
|
|
|
self.assertTrue(self.manager.add_mavlink_object(self.mavlink_obj1))
|
|
|
|
|
|
self.assertEqual(len(self.manager.managed_objects), 1)
|
|
|
|
|
|
self.assertEqual(self.mavlink_obj1.state, MavlinkObjectState.RUNNING)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加另一個對象
|
|
|
|
|
|
self.assertTrue(self.manager.add_mavlink_object(self.mavlink_obj2))
|
|
|
|
|
|
self.assertEqual(len(self.manager.managed_objects), 2)
|
|
|
|
|
|
|
|
|
|
|
|
# 檢查受管理對象列表
|
|
|
|
|
|
managed_objects = self.manager.get_managed_objects()
|
|
|
|
|
|
self.assertEqual(len(managed_objects), 2)
|
|
|
|
|
|
self.assertIn(0, managed_objects)
|
|
|
|
|
|
self.assertIn(1, managed_objects)
|
|
|
|
|
|
|
|
|
|
|
|
# 移除對象
|
|
|
|
|
|
self.assertTrue(self.manager.remove_mavlink_object(0))
|
|
|
|
|
|
self.assertEqual(len(self.manager.managed_objects), 1)
|
|
|
|
|
|
|
|
|
|
|
|
# 嘗試移除不存在的對象
|
|
|
|
|
|
self.assertFalse(self.manager.remove_mavlink_object(999))
|
|
|
|
|
|
|
|
|
|
|
|
# 停止管理器
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
class TestIntegration(unittest.TestCase):
|
|
|
|
|
|
"""整合測試,測試多個 mavlink_object 之間的互動與資料流"""
|
|
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
|
"""在每個測試方法執行前準備環境"""
|
|
|
|
|
|
# 清空全局變數
|
|
|
|
|
|
mavlink_object.mavlinkObjects = {}
|
|
|
|
|
|
mavlink_object.socket_num = 0
|
|
|
|
|
|
|
|
|
|
|
|
# 清空 ring buffer
|
|
|
|
|
|
stream_bridge_ring.clear()
|
|
|
|
|
|
return_packet_ring.clear()
|
|
|
|
|
|
|
|
|
|
|
|
# 創建 async_io_manager 實例
|
|
|
|
|
|
self.manager = async_io_manager()
|
|
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
|
"""在每個測試方法執行後清理環境"""
|
|
|
|
|
|
if self.manager.running:
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
def test_send_message_with_manager(self):
|
|
|
|
|
|
"""測試透過 async_io_manager 發送訊息的完整流程"""
|
|
|
|
|
|
# 創建一個新的 mavlink_object 實例
|
|
|
|
|
|
mock_socket = MockMavlinkSocket()
|
|
|
|
|
|
mavlink_obj = mavlink_object(mock_socket)
|
|
|
|
|
|
|
|
|
|
|
|
# 測試初始狀態
|
|
|
|
|
|
self.assertEqual(len(mock_socket.written_data), 0)
|
|
|
|
|
|
|
|
|
|
|
|
# 測試非運行狀態下發送消息
|
|
|
|
|
|
self.assertFalse(mavlink_obj.send_message(HEARTBEAT_PACKET_1))
|
|
|
|
|
|
self.assertEqual(len(mock_socket.written_data), 0)
|
|
|
|
|
|
|
|
|
|
|
|
# 啟動 manager
|
|
|
|
|
|
self.manager.start()
|
|
|
|
|
|
time.sleep(0.5) # 等待事件循環啟動
|
|
|
|
|
|
|
|
|
|
|
|
# 添加對象到 manager
|
|
|
|
|
|
self.manager.add_mavlink_object(mavlink_obj)
|
|
|
|
|
|
time.sleep(0.1) # 等待對象啟動
|
|
|
|
|
|
|
|
|
|
|
|
# 確認對象狀態
|
|
|
|
|
|
self.assertEqual(mavlink_obj.state, MavlinkObjectState.RUNNING)
|
|
|
|
|
|
|
|
|
|
|
|
# 測試發送消息
|
|
|
|
|
|
self.assertTrue(mavlink_obj.send_message(HEARTBEAT_PACKET_1))
|
|
|
|
|
|
time.sleep(0.2) # 等待消息處理
|
|
|
|
|
|
|
|
|
|
|
|
# 確認消息已發送
|
|
|
|
|
|
self.assertEqual(len(mock_socket.written_data), 1)
|
|
|
|
|
|
self.assertEqual(mock_socket.written_data[0], HEARTBEAT_PACKET_1)
|
|
|
|
|
|
|
|
|
|
|
|
# 測試連續發送多條消息
|
|
|
|
|
|
self.assertTrue(mavlink_obj.send_message(HEARTBEAT_PACKET_2))
|
|
|
|
|
|
time.sleep(0.2) # 等待消息處理
|
|
|
|
|
|
|
|
|
|
|
|
# 確認兩條消息都已發送
|
|
|
|
|
|
self.assertEqual(len(mock_socket.written_data), 2)
|
|
|
|
|
|
self.assertEqual(mock_socket.written_data[1], HEARTBEAT_PACKET_2)
|
|
|
|
|
|
|
|
|
|
|
|
# 停止 manager
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
time.sleep(0.5) # 等待 manager 停止
|
|
|
|
|
|
|
|
|
|
|
|
# 測試對象已關閉後發送消息
|
|
|
|
|
|
self.assertFalse(mavlink_obj.send_message(HEARTBEAT_PACKET_1))
|
|
|
|
|
|
self.assertEqual(len(mock_socket.written_data), 2) # 消息數量未增加
|
|
|
|
|
|
|
|
|
|
|
|
def test_data_processing_and_forwarding(self):
|
|
|
|
|
|
"""測試數據處理與轉發流程"""
|
|
|
|
|
|
# 創建用於轉發的 mavlink_objects
|
|
|
|
|
|
mock_socket1 = MockMavlinkSocket([HEARTBEAT_PACKET_1, HEARTBEAT_PACKET_2,])
|
|
|
|
|
|
mock_socket3 = MockMavlinkSocket()
|
|
|
|
|
|
|
|
|
|
|
|
mavlink_obj1 = mavlink_object(mock_socket1)
|
|
|
|
|
|
mavlink_obj3 = mavlink_object(mock_socket3)
|
|
|
|
|
|
|
|
|
|
|
|
# 設置訊息類型
|
|
|
|
|
|
mavlink_obj1.set_bridge_message_types([0]) # 只處理 HEARTBEAT
|
|
|
|
|
|
|
|
|
|
|
|
# 設置轉發: obj1 -> obj3
|
|
|
|
|
|
mavlink_obj1.add_target_socket(mavlink_obj3.socket_id) # socket1 轉發到 socket3 (socket_id=1)
|
|
|
|
|
|
|
|
|
|
|
|
# 啟動管理器並添加對象
|
|
|
|
|
|
self.manager.start()
|
|
|
|
|
|
time.sleep(0.5) # 等待事件循環啟動
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
這邊出現很奇怪的狀況 應該說 設計時沒有考量 但是實測會發現
|
|
|
|
|
|
mavlink_obj3 是接收端 必需要被優先加入 manager 才能正確接收來自 mavlink_obj1 的轉發封包
|
|
|
|
|
|
若先把 mavlink_ojb1 加入 manger 則可能會導致前面幾個封包丟失
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.manager.add_mavlink_object(mavlink_obj3)
|
|
|
|
|
|
self.manager.add_mavlink_object(mavlink_obj1)
|
|
|
|
|
|
|
|
|
|
|
|
# 等待處理完成
|
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
|
|
|
|
# 檢查 Ring buffer 是否有正確的數據
|
|
|
|
|
|
self.assertGreaterEqual(stream_bridge_ring.size(), 2) # 至少 2 個 HEARTBEAT
|
|
|
|
|
|
|
|
|
|
|
|
# 檢查是否正確轉發
|
|
|
|
|
|
self.assertGreaterEqual(len(mock_socket3.written_data), 2) # 至少 2 個 HEARTBEAT
|
|
|
|
|
|
|
|
|
|
|
|
# 停止管理器
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
def test_bidirectional_forwarding(self):
|
|
|
|
|
|
"""測試雙向轉發"""
|
|
|
|
|
|
# 清空全局變數和 ring buffer
|
|
|
|
|
|
mavlink_object.mavlinkObjects = {}
|
|
|
|
|
|
mavlink_object.socket_num = 0
|
|
|
|
|
|
stream_bridge_ring.clear()
|
|
|
|
|
|
return_packet_ring.clear()
|
|
|
|
|
|
|
|
|
|
|
|
# 創建三個 mavlink 對象,模擬三個通道
|
|
|
|
|
|
socket1 = MockMavlinkSocket()
|
|
|
|
|
|
socket2 = MockMavlinkSocket()
|
|
|
|
|
|
socket3 = MockMavlinkSocket()
|
|
|
|
|
|
|
|
|
|
|
|
obj1 = mavlink_object(socket1)
|
|
|
|
|
|
obj2 = mavlink_object(socket2)
|
|
|
|
|
|
obj3 = mavlink_object(socket3)
|
|
|
|
|
|
|
|
|
|
|
|
# 設置雙向轉發
|
|
|
|
|
|
# obj1 <-> obj2 <-> obj3
|
|
|
|
|
|
obj1.add_target_socket(1) # obj1 -> obj2
|
|
|
|
|
|
obj2.add_target_socket(0) # obj2 -> obj1
|
|
|
|
|
|
obj2.add_target_socket(2) # obj2 -> obj3
|
|
|
|
|
|
obj3.add_target_socket(1) # obj3 -> obj2
|
|
|
|
|
|
|
|
|
|
|
|
# 啟動 async_io_manager
|
|
|
|
|
|
self.manager.start()
|
|
|
|
|
|
time.sleep(0.5) # 等待事件循環啟動
|
|
|
|
|
|
|
|
|
|
|
|
# 添加所有 mavlink_object
|
|
|
|
|
|
self.manager.add_mavlink_object(obj1)
|
|
|
|
|
|
self.manager.add_mavlink_object(obj2)
|
|
|
|
|
|
self.manager.add_mavlink_object(obj3)
|
|
|
|
|
|
|
|
|
|
|
|
# 對三個對象添加數據
|
|
|
|
|
|
socket1.test_packets.append(HEARTBEAT_PACKET_1)
|
|
|
|
|
|
socket2.test_packets.append(HEARTBEAT_PACKET_2)
|
|
|
|
|
|
socket3.test_packets.append(HEARTBEAT_PACKET_3)
|
|
|
|
|
|
|
|
|
|
|
|
# 等待處理所有訊息
|
|
|
|
|
|
time.sleep(1.0)
|
|
|
|
|
|
|
|
|
|
|
|
# 檢查轉發結果
|
|
|
|
|
|
# socket1 應該收到 socket2 的訊息
|
|
|
|
|
|
self.assertGreaterEqual(len(socket1.written_data), 1)
|
|
|
|
|
|
|
|
|
|
|
|
# socket2 應該收到 socket1 和 socket3 的訊息
|
|
|
|
|
|
self.assertGreaterEqual(len(socket2.written_data), 2)
|
|
|
|
|
|
|
|
|
|
|
|
# socket3 應該收到 socket2 的訊息
|
|
|
|
|
|
self.assertGreaterEqual(len(socket3.written_data), 1)
|
|
|
|
|
|
|
|
|
|
|
|
# 檢查 ring buffer 的數據
|
|
|
|
|
|
# 所有對象都啟用了橋接器,且預設的 bridge_msg_types = [0]
|
|
|
|
|
|
self.assertGreaterEqual(stream_bridge_ring.size(), 3) # 至少 3 個 HEARTBEAT
|
|
|
|
|
|
|
|
|
|
|
|
# 停止管理器
|
|
|
|
|
|
self.manager.shutdown()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
# 可以指定要運行的測試
|
|
|
|
|
|
# unittest.main(defaultTest="TestMavlinkObject.test_send_message_validation")
|
|
|
|
|
|
# unittest.main(defaultTest="TestAsyncIOManager.test_add_remove_objects")
|
|
|
|
|
|
unittest.main(defaultTest="TestIntegration.test_bidirectional_forwarding")
|
|
|
|
|
|
unittest.main()
|
|
|
|
|
|
|