1. (中繼的存檔) 準備對 mavlinkObject.py 的 mavlink_object 重構

2. 新增 ringBuffer.py 作為 queue 取代
3. 新增 tests 資料夾分離程式與測試檔案
4. asyncioManager.py 是參考用的範例
This commit is contained in:
2025-06-03 02:04:51 +08:00
parent 417d9e8f57
commit 7f7753d0b4
7 changed files with 1015 additions and 5 deletions
@@ -0,0 +1,246 @@
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import time
import threading
import socket
import random
from pymavlink import mavutil
from pymavlink.dialects.v20 import common as mavlink2
from fc_network_adapter.asyncioManager import AsyncIOManager, Connection
class MockMAVLinkSocket:
"""模擬 pymavlink socket 行為的類,用於測試"""
def __init__(self, name="mock", simulate_errors=False):
self.name = name
self.simulate_errors = simulate_errors
self.is_closed = False
self.received_msgs = []
self.seq = 0
def recv_msg(self):
"""模擬接收 MAVLink 消息"""
if self.is_closed:
return None
# 隨機模擬網絡延遲
time.sleep(random.uniform(0.01, 0.05))
# 偶爾模擬錯誤
if self.simulate_errors and random.random() < 0.1:
raise Exception("Simulated network error")
# 70% 的機率返回消息,30% 返回 None (模擬無數據可讀)
if random.random() > 0.3:
msg = self._generate_mavlink_message()
self.received_msgs.append(msg)
return msg
return None
def _generate_mavlink_message(self):
"""生成模擬 MAVLink 消息"""
# 創建 MAVLink 實例
mav = mavlink2.MAVLink(self)
# 設置來源系統和組件
mav.srcSystem = random.randint(1, 5)
mav.srcComponent = random.randint(1, 5)
# 創建 HEARTBEAT 消息
msg = mavlink2.MAVLink_heartbeat_message(
type=mavlink2.MAV_TYPE_QUADROTOR,
autopilot=mavlink2.MAV_AUTOPILOT_GENERIC,
base_mode=0,
custom_mode=0,
system_status=0,
mavlink_version=3
)
# 打包消息
msg.pack(mav)
self.seq += 1
return msg
def write(self, data):
"""模擬發送數據"""
if self.is_closed:
raise Exception("Socket is closed")
# 模擬寫入操作
time.sleep(random.uniform(0.001, 0.01))
# 偶爾模擬錯誤
if self.simulate_errors and random.random() < 0.1:
raise Exception("Simulated write error")
return len(data)
def close(self):
"""關閉連接"""
self.is_closed = True
def create_mock_mavlink_sockets(count=3):
"""創建多個模擬 MAVLink socket"""
sockets = []
for i in range(count):
# 每三個 socket 中有一個模擬錯誤
simulate_errors = (i % 3 == 0)
socket = MockMAVLinkSocket(f"mock_{i}", simulate_errors)
sockets.append(socket)
return sockets
def monitor_thread(io_manager):
"""監視 AsyncIOManager 狀態的執行緒"""
print("Starting monitoring thread...")
try:
while True:
# 獲取總體統計資訊
stats = io_manager.get_stats()
print(f"\n=== AsyncIOManager Stats ===")
print(f"Total connections: {stats.get('connection_count', 0)}")
print(f"Total messages received: {stats.get('rx_count', 0)}")
print(f"Total bytes received: {stats.get('rx_bytes', 0)}")
print(f"Total errors: {stats.get('errors', 0)}")
# 獲取每個連接的緩衝區狀態
for socket_id in io_manager.connections:
buffer = io_manager.get_buffer(socket_id)
conn_stats = io_manager.get_stats(socket_id)
if buffer and not io_manager.connections[socket_id].is_closed:
print(f"Connection {socket_id}: Buffer size={buffer.size()}, " +
f"Messages={conn_stats.get('rx_count', 0)}, " +
f"Last activity={time.time() - io_manager.connections[socket_id].last_activity:.1f}s ago")
# 每秒更新一次
time.sleep(1.0)
except KeyboardInterrupt:
print("Monitoring thread stopped.")
def consumer_thread(io_manager, socket_id):
"""從指定連接的緩衝區消費消息"""
print(f"Starting consumer for socket {socket_id}...")
try:
while True:
# 獲取緩衝區
buffer = io_manager.get_buffer(socket_id)
if not buffer:
print(f"Consumer {socket_id}: Buffer not found or connection closed")
break
# 獲取所有消息
messages = buffer.get_all()
if messages:
print(f"Consumer {socket_id}: Received {len(messages)} messages")
for msg in messages:
# 處理每條消息 (這裡只是示例,實際應用中可能有更複雜的處理)
print(f" - Message from SysID={msg.get_srcSystem()}, CompID={msg.get_srcComponent()}, Type={msg.get_type()}")
# 短暫休眠,避免 CPU 空轉
time.sleep(0.2)
except Exception as e:
print(f"Consumer {socket_id} stopped: {e}")
def send_command_thread(io_manager, socket_ids):
"""定期向所有連接發送命令"""
print("Starting command sender thread...")
# 創建一個 MAVLink 實例用於生成消息
mav = mavlink2.MAVLink(None)
mav.srcSystem = 255 # GCS system ID
mav.srcComponent = 0
try:
count = 0
while True:
# 等待 2 秒
time.sleep(2.0)
count += 1
# 創建一個命令消息
msg = mavlink2.MAVLink_command_long_message(
target_system=1,
target_component=1,
command=mavlink2.MAV_CMD_REQUEST_PROTOCOL_VERSION,
confirmation=0,
param1=0, param2=0, param3=0,
param4=0, param5=0, param6=0, param7=0
)
# 向所有連接發送消息
for socket_id in socket_ids:
if socket_id in io_manager.connections and not io_manager.connections[socket_id].is_closed:
try:
# 打包並發送消息
result = io_manager.send_message(socket_id, msg)
print(f"Sent command to socket {socket_id}: {'Success' if result else 'Failed'}")
except Exception as e:
print(f"Error sending to socket {socket_id}: {e}")
# 每 10 次迭代關閉一個連接 (用於演示關閉功能)
if count % 10 == 0 and socket_ids:
socket_id = socket_ids.pop(0)
print(f"Closing connection {socket_id}...")
io_manager.remove_connection(socket_id)
except Exception as e:
print(f"Command sender thread stopped: {e}")
def main():
print("AsyncIOManager Example")
print("======================")
# 創建 AsyncIOManager 實例
io_manager = AsyncIOManager()
# 啟動 I/O 管理器
io_manager.start()
print("AsyncIOManager started")
# 創建模擬 MAVLink socket
mock_sockets = create_mock_mavlink_sockets(5)
socket_ids = []
# 註冊連接
for i, socket in enumerate(mock_sockets):
socket_id = io_manager.register_connection(socket)
socket_ids.append(socket_id)
print(f"Registered socket {i} with ID {socket_id}")
# 啟動監視執行緒
monitor = threading.Thread(target=monitor_thread, args=(io_manager,))
monitor.daemon = True
monitor.start()
# 為每個連接啟動消費者執行緒
consumers = []
for socket_id in socket_ids:
consumer = threading.Thread(target=consumer_thread, args=(io_manager, socket_id))
consumer.daemon = True
consumer.start()
consumers.append(consumer)
# 啟動命令發送執行緒
sender = threading.Thread(target=send_command_thread, args=(io_manager, socket_ids.copy()))
sender.daemon = True
sender.start()
print("\nPress Ctrl+C to exit...")
try:
# 運行一段時間然後退出
time.sleep(30)
except KeyboardInterrupt:
pass
finally:
# 清理資源
print("\nShutting down...")
io_manager.stop()
print("AsyncIOManager stopped")
if __name__ == "__main__":
main()
@@ -0,0 +1,152 @@
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import time
import threading
from fc_network_adapter.ringBuffer import RingBuffer
def producer(buffer, count, interval=0.01):
"""生產者:向緩衝區添加資料"""
print(f"Producer started (thread {threading.get_ident()})")
for i in range(count):
# 嘗試寫入數據,直到成功
while not buffer.put(f"Item-{i}"):
print(f"Buffer full, producer waiting... (thread {threading.get_ident()})")
time.sleep(0.1)
print(f"Produced: Item-{i}, buffer size: {buffer.size()}")
time.sleep(interval) # 模擬生產過程
print(f"Producer finished (thread {threading.get_ident()})")
def consumer(buffer, max_items, interval=0.05):
"""消費者:從緩衝區讀取資料"""
print(f"Consumer started (thread {threading.get_ident()})")
items_consumed = 0
while items_consumed < max_items:
# 嘗試讀取數據
item = buffer.get()
if item:
print(f"Consumed: {item}, buffer size: {buffer.size()}")
items_consumed += 1
else:
print(f"Buffer empty, consumer waiting... (thread {threading.get_ident()})")
time.sleep(interval) # 模擬消費過程
print(f"Consumer finished (thread {threading.get_ident()})")
def batch_consumer(buffer, interval=0.2):
"""批量消費者:一次性讀取緩衝區中的所有資料"""
print(f"Batch consumer started (thread {threading.get_ident()})")
for _ in range(5): # 執行5次批量讀取
time.sleep(interval) # 等待緩衝區積累數據
items = buffer.get_all()
if items:
print(f"Batch consumed {len(items)} items: {items}")
else:
print("Buffer empty for batch consumer")
print(f"Batch consumer finished (thread {threading.get_ident()})")
def demonstrate_multi_writer():
"""示範多個寫入執行緒同時操作緩衝區"""
print("\n=== Demonstrating Multiple Writers ===")
buffer = RingBuffer(capacity=80)
# 創建多個生產者執行緒
threads = []
for i in range(3):
thread = threading.Thread(target=producer, args=(buffer, 5, 0.1 * (i+1)))
threads.append(thread)
thread.start()
# 等待所有執行緒完成
for thread in threads:
thread.join()
buffer.print_stats() # 印出統計資訊
# 讀出所有剩餘資料
remaining = buffer.get_all()
print(f"Remaining items in buffer after multiple writers: {remaining}")
def demonstrate_basic_usage():
"""示範基本使用方式"""
print("\n=== Demonstrating Basic Usage ===")
# 創建緩衝區
buffer = RingBuffer(capacity=20, buffer_id=7)
# 檢查初始狀態
print(f"Initial buffer state - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
# 添加幾個項目
for i in range(5):
buffer.put(f"Test-{i}")
# 檢查狀態
print(f"After adding 5 items - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
# 讀取一個項目
item = buffer.get()
print(f"Read item: {item}")
print(f"After reading 1 item - Content Size: {buffer.size()}")
# 添加更多項目直到滿
items_added = 0
while not buffer.is_full():
buffer.put(f"Fill-{items_added}")
items_added += 1
print(f"Added {items_added} more items until full")
print(f"Buffer full state - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
# 嘗試添加到已滿的緩衝區
result = buffer.put("Overflow")
print(f"Attempt to add to full buffer: {'Succeeded' if result else 'Failed'}")
# 獲取所有項目
all_items = buffer.get_all()
print(f"All items in buffer: {all_items}")
print(f"Buffer after get_all() - Empty: {buffer.is_empty()}, Content Size: {buffer.size()}")
# 印出統計資訊
buffer.print_stats()
def demonstrate_producer_consumer():
"""示範生產者-消費者模式"""
print("\n=== Demonstrating Producer-Consumer Pattern ===")
buffer = RingBuffer(capacity=16)
# 創建生產者和消費者執行緒
producer_thread = threading.Thread(target=producer, args=(buffer, 20, 0.1))
consumer_thread = threading.Thread(target=consumer, args=(buffer, 3, 0.2))
batch_thread = threading.Thread(target=batch_consumer, args=(buffer, 0.5))
# 啟動執行緒
producer_thread.start()
consumer_thread.start()
batch_thread.start()
# 等待執行緒完成
producer_thread.join()
consumer_thread.join()
batch_thread.join()
# 檢查最終狀態
print(f"Final buffer state - Empty: {buffer.is_empty()}, Size: {buffer.size()}")
buffer.print_stats()
if __name__ == "__main__":
# 展示各種使用場景
# demonstrate_basic_usage()
# demonstrate_producer_consumer()
demonstrate_multi_writer()
print("\nAll demonstrations completed!")