(Tested)
新增刪除載具功能 代碼優化 詳見改版記錄
This commit is contained in:
@@ -84,16 +84,20 @@
|
||||
唯一實例
|
||||
實際去解析 mavlink 封包的地方
|
||||
接收 stream_bridge_ring 與 return_packet_ring 的資料
|
||||
這邊是比較偏自動化 不會被操作的
|
||||
- *self.thread* 自己的執行緒
|
||||
---
|
||||
- *_run_thread()* 核心方法
|
||||
- *_handle_XXXXX()* 每一種單項 mavlink 封包的解析
|
||||
- *send_message()* 是 _send_to_socket() 的高階包裝 跟 ros2 介面做互動的方法
|
||||
- *_send_to_socket()* 把要傳送的封包 丟給 mavlink 去處理
|
||||
|
||||
### **[Class]** async_io_manager
|
||||
唯一實例
|
||||
異步 event loop
|
||||
管理 mavlink_object 的地方
|
||||
沒有核心方法
|
||||
這邊主要是管理 mavlink_object 的地方 (但不會對於某個 mavlink_object 內部需求做操作)
|
||||
|
||||
- *self.thread* 自己的執行緒
|
||||
- *self.managed_objects* 資料結構 socket_id: mavlink_object
|
||||
---
|
||||
@@ -110,7 +114,7 @@
|
||||
---
|
||||
- *process_data()* [async method] 核心方法
|
||||
- *remove_target_socket()* *add_target_socket()*
|
||||
- *send_message()*
|
||||
- *message_put_queue()* 把要傳送的封包放到自己這個物件的暫存區 會由 process_data() 依照異步流程被實際丟出
|
||||
|
||||
## serialManager.py
|
||||
看這個檔案的重點再於要搞清楚 端口物件 還是 傳輸物件
|
||||
@@ -120,7 +124,6 @@
|
||||
管理 mavlink_object 的地方
|
||||
- *self.thread* 自己的執行緒
|
||||
- *self.loop* 自己的事件迴圈
|
||||
- *self.serial_objects* 存放管理的物件 serial id num : serial_object
|
||||
---
|
||||
- *create_serial_link()* [call method] 把 serial 端口跟 UDP 端口打通
|
||||
- *_async_create_serial_link()* [async method] 把兩種端口接起來的重點程序
|
||||
@@ -157,10 +160,15 @@
|
||||
## 已實現功能
|
||||
1. mavlink 分流解析
|
||||
2. mavlink socket 建立
|
||||
3. mavlink socket 轉拋
|
||||
4. 連結 Serial 轉 UDP
|
||||
5. 各單元模組化
|
||||
6. 終端機介面控制
|
||||
7. 基礎載具流量觀測
|
||||
|
||||
3. mavlink socket 轉拋 proxy
|
||||
4. 建立 Serial 轉 UDP 連結 並管理
|
||||
5. 建立 serial 連線
|
||||
6. 各單元模組化
|
||||
7. 終端機介面控制
|
||||
8. 基礎載具流量觀測
|
||||
9. 載具狀態收集與彙整
|
||||
|
||||
### 待開發功能
|
||||
5-1. 建立 serial 連線 並可以對接收器下達AT指令
|
||||
5-2. 模組化 serial 連線機制 以利後期擴容其他模組
|
||||
10-1. ros2 應用開發介面
|
||||
@@ -28,7 +28,7 @@ from .utils import acquireSerial, acquirePort
|
||||
from .utils.acquirePort import find_available_port
|
||||
|
||||
logger = setup_logger(os.path.basename(__file__))
|
||||
VERSION_NO = "v0.56"
|
||||
VERSION_NO = "v0.58"
|
||||
|
||||
class PanelState:
|
||||
def __init__(self):
|
||||
@@ -1088,12 +1088,17 @@ class ControlPanel:
|
||||
# 確保跳過顯示區域
|
||||
line = line + 6
|
||||
|
||||
dialog_win.addstr(dialog_height - 2, 2, "Press any key to return... | Auto-refresh: 1.0s")
|
||||
dialog_win.addstr(dialog_height - 2, 2, "Press R: Reset Stats, C: Unregister, other key to return...")
|
||||
dialog_win.refresh()
|
||||
|
||||
# 檢查是否有按鍵(非阻塞)
|
||||
ch = dialog_win.getch()
|
||||
if ch != -1: # 有按鍵則退出
|
||||
if ch in (ord('R'), ord('r')):
|
||||
cmd_q.put(("RESET_VEHICLE_STATISTICS", sysid, compid))
|
||||
elif ch in (ord('C'), ord('c')):
|
||||
cmd_q.put(("UNREGISTER_VEHICLE", sysid))
|
||||
break
|
||||
elif ch != -1: # 有按鍵則退出
|
||||
break
|
||||
|
||||
# 短暫延遲
|
||||
@@ -1197,7 +1202,7 @@ class Orchestrator:
|
||||
self.remove_target_from_object(source_id, target_id)
|
||||
elif action == "INSPECT_MAV_OBJECT":
|
||||
socket_id = cmd[1]
|
||||
mav_obj = self.manager.managed_objects.get(socket_id, None)
|
||||
mav_obj = mo.mavlink_object.mavlinkObjects.get(socket_id, None)
|
||||
if mav_obj:
|
||||
self.panelState.socket_info_single["socket_type"] = mav_obj.socket_type
|
||||
self.panelState.socket_info_single["socket_state"] = mav_obj.state.name
|
||||
@@ -1223,8 +1228,16 @@ class Orchestrator:
|
||||
elif action == "INSPECT_VEHICLE":
|
||||
sysid, compid = cmd[1], cmd[2]
|
||||
self._prepare_vehicle_info(sysid, compid)
|
||||
elif action == "UPDATE_VEHICLES_LIST":
|
||||
self._update_vehicles_list()
|
||||
# elif action == "UPDATE_VEHICLES_LIST": # TODO 這個擺這邊 不知道為何可以有作用 先不動 也許後面有bug?
|
||||
# logger.debug("Orchestrator: Updating vehicles list upon request")
|
||||
# self._update_vehicles_list()
|
||||
elif action == "RESET_VEHICLE_STATISTICS":
|
||||
sysid, compid = cmd[1], cmd[2]
|
||||
vehicle_sys = self.vehicle_registry.get(sysid)
|
||||
vehicle_sys.reset_component_stats(compid)
|
||||
elif action == "UNREGISTER_VEHICLE":
|
||||
sysid = cmd[1]
|
||||
self.vehicle_registry.unregister(sysid)
|
||||
|
||||
elif cmd == "CREATE_UDP_INBOUND":
|
||||
self.panelState.udp_info_temp["direction"] = "inbound"
|
||||
@@ -1240,12 +1253,12 @@ class Orchestrator:
|
||||
self.manager.shutdown()
|
||||
elif cmd == "SHUTDOWN_SERIAL_MANAGER":
|
||||
self.plumber.shutdown()
|
||||
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing command: {e}")
|
||||
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
@@ -1321,6 +1334,14 @@ class Orchestrator:
|
||||
|
||||
def delete_udp_object(self, socket_id):
|
||||
"""移除指定的 mavlink_object"""
|
||||
|
||||
mavlink_obj = mo.mavlink_object.mavlinkObjects[socket_id]
|
||||
connection_string = mavlink_obj.mavlink_socket.address
|
||||
strings = connection_string.split(':')
|
||||
ip = strings[0]
|
||||
port = int(strings[1])
|
||||
self.occupied_ip_ports[ip].remove(port)
|
||||
|
||||
self.manager.remove_mavlink_object(socket_id)
|
||||
|
||||
def add_target_to_object(self, source_id, target_id):
|
||||
@@ -1414,7 +1435,7 @@ class Orchestrator:
|
||||
|
||||
# 更新 vehicle_info_single
|
||||
socket_type = "N/A"
|
||||
socket_obj = self.manager.managed_objects.get(socket_id, None)
|
||||
socket_obj =mo.mavlink_object.mavlinkObjects.get(socket_id, None)
|
||||
if socket_obj:
|
||||
socket_type = socket_obj.socket_type
|
||||
|
||||
@@ -1534,5 +1555,11 @@ if __name__ == "__main__":
|
||||
4. 修改 避免 serial 與 ip port 重複建立相同的通道
|
||||
5. 修改 show_object_info 與 show_linked_serial_info 改變檢核 Ready 方式 避免卡死
|
||||
|
||||
2025.01.16
|
||||
1. 新增 "移除載具" 與 "重置載具統計" 功能
|
||||
2. 修正 udp port 在移除後仍被記錄為佔用的問題
|
||||
3. 因應 mvalinkObject.py 中 mavlinkObjects 修正變數存取方式
|
||||
4. 註解掉無效代碼 action == "UPDATE_VEHICLES_LIST" 區塊
|
||||
|
||||
|
||||
'''
|
||||
|
||||
@@ -323,7 +323,7 @@ class mavlink_bridge:
|
||||
return False
|
||||
|
||||
mav_obj = mavlink_object.mavlinkObjects[socket_id]
|
||||
return mav_obj.send_message(message_bytes)
|
||||
return mav_obj.message_put_queue(message_bytes)
|
||||
|
||||
# 定義 mavlink_object 的狀態
|
||||
class MavlinkObjectState(Enum):
|
||||
@@ -501,7 +501,7 @@ class mavlink_object:
|
||||
logger.error(f"Invalid return message types: {msg_types}")
|
||||
return False
|
||||
|
||||
def send_message(self, message_bytes):
|
||||
def message_put_queue(self, message_bytes):
|
||||
"""
|
||||
從主線程向此 mavlink_object 的 socket 發送數據
|
||||
將數據添加到簡單的列表中,由 asyncio 任務處理
|
||||
@@ -568,9 +568,7 @@ class async_io_manager:
|
||||
self.loop = None
|
||||
self.running = False
|
||||
# self.main_task = None
|
||||
self.managed_objects = {} # socket_id: mavlink_object
|
||||
self.thread = None
|
||||
self._stop_event = threading.Event()
|
||||
|
||||
def __del__(self):
|
||||
self.loop = None
|
||||
@@ -586,7 +584,6 @@ class async_io_manager:
|
||||
return
|
||||
|
||||
self.running = True
|
||||
self._stop_event.clear()
|
||||
|
||||
# 啟動獨立線程 命名為 AsyncIOManager
|
||||
self.thread = threading.Thread(
|
||||
@@ -618,12 +615,11 @@ class async_io_manager:
|
||||
return
|
||||
|
||||
# 停止所有被管理的 mavlink_object 所屬的 task
|
||||
for socket_id in list(self.managed_objects.keys()):
|
||||
for socket_id in list(mavlink_object.mavlinkObjects.keys()):
|
||||
self.remove_mavlink_object(socket_id)
|
||||
|
||||
# 停止自己的 task
|
||||
self.running = False
|
||||
self._stop_event.set()
|
||||
|
||||
# 解開事件循環的阻塞
|
||||
self.loop.call_soon_threadsafe(self.loop.stop)
|
||||
@@ -663,15 +659,6 @@ class async_io_manager:
|
||||
self.running = False
|
||||
logger.info("async_io_manager event loop END!")
|
||||
|
||||
# async def _main_task(self): # 當初想說可能要一個額外的 task 來管理 但是目前好像用不掉 先放著不管
|
||||
# """主任務協程 讓 async_io_manager 在執行緒中持續運作"""
|
||||
# logger.info("async_io_manager main task started")
|
||||
|
||||
# while self.running and not self._stop_event.is_set():
|
||||
# await asyncio.sleep(0.1)
|
||||
|
||||
# logger.info("async_io_manager main task ended")
|
||||
|
||||
def add_mavlink_object(self, mavlink_obj: mavlink_object):
|
||||
"""添加 mavlink_object"""
|
||||
# 一個防呆 確保有 event loop 與 _main_task 正在運作
|
||||
@@ -681,9 +668,12 @@ class async_io_manager:
|
||||
|
||||
socket_id = mavlink_obj.socket_id
|
||||
|
||||
if socket_id in self.managed_objects:
|
||||
logger.warning(f"mavlink_object {socket_id} already managed")
|
||||
return False
|
||||
# 檢查該對象是否已經在運行中
|
||||
if socket_id in mavlink_object.mavlinkObjects:
|
||||
existing_obj = mavlink_object.mavlinkObjects[socket_id]
|
||||
if existing_obj.state == MavlinkObjectState.RUNNING:
|
||||
logger.warning(f"mavlink_object {socket_id} already managed")
|
||||
return False
|
||||
|
||||
# 使用 run_coroutine_threadsafe 執行協程並獲取結果
|
||||
future = asyncio.run_coroutine_threadsafe(
|
||||
@@ -708,7 +698,6 @@ class async_io_manager:
|
||||
|
||||
try:
|
||||
task = asyncio.create_task(mavlink_obj.process_data())
|
||||
self.managed_objects[socket_id] = mavlink_obj
|
||||
mavlink_obj.task = task
|
||||
mavlink_obj.state = MavlinkObjectState.RUNNING
|
||||
mavlink_obj.outgoing_msgs.clear()
|
||||
@@ -743,11 +732,11 @@ class async_io_manager:
|
||||
|
||||
async def _async_remove_mavlink_object(self, socket_id):
|
||||
"""在事件循環線程中同步執行"""
|
||||
if socket_id not in self.managed_objects:
|
||||
logger.warning(f"mavlink_object {socket_id} not managed")
|
||||
return
|
||||
if socket_id not in mavlink_object.mavlinkObjects:
|
||||
logger.warning(f"mavlink_object {socket_id} not found")
|
||||
return False
|
||||
|
||||
mavlink_obj = self.managed_objects[socket_id]
|
||||
mavlink_obj = mavlink_object.mavlinkObjects[socket_id]
|
||||
mavlink_obj.state = MavlinkObjectState.SHUTTINGDOWN
|
||||
|
||||
if not mavlink_obj.task.done():
|
||||
@@ -761,9 +750,8 @@ class async_io_manager:
|
||||
break
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# 如果正常結束 則移除
|
||||
# 如果正常結束 則設置為關閉狀態(物件的清理由 __del__ 處理)
|
||||
if mavlink_obj.task.done():
|
||||
del self.managed_objects[socket_id]
|
||||
mavlink_obj.state = MavlinkObjectState.CLOSED
|
||||
logger.info(f"Removed mavlink_object {socket_id} from manager.")
|
||||
return True
|
||||
@@ -773,8 +761,9 @@ class async_io_manager:
|
||||
return False
|
||||
|
||||
def get_managed_objects(self):
|
||||
"""獲取所有被管理的對象列表"""
|
||||
return list(self.managed_objects.keys())
|
||||
"""獲取所有被管理的對象列表(狀態為 RUNNING 的對象)"""
|
||||
return [socket_id for socket_id, obj in mavlink_object.mavlinkObjects.items()
|
||||
if obj.state == MavlinkObjectState.RUNNING]
|
||||
|
||||
# ====================== 分割線 =====================
|
||||
|
||||
@@ -807,9 +796,13 @@ if __name__ == '__main__':
|
||||
3. mavlink_bridge 的主要迴圈 增加 send_message 功能 可指定目標 sysid 或 socket_id 發送 mavlink 封包
|
||||
4. async_io_manager 循環邏輯大改動 優化 mavlink_object 加入與移除的邏輯 並使得 task 與 evenlt loop 分層更清楚
|
||||
5. mavlink_object 移除不必要的 start 與 stop 方法 由 async_io_manager 統一管理其生命週期
|
||||
6. mavlink_object 優化 send_message 方法 避免無效判斷 與 增加一些防呆檢驗 並與 mavlink_bridge 連動工作
|
||||
6. mavlink_object 優化 message_put_queue 方法 避免無效判斷 與 增加一些防呆檢驗 並與 mavlink_bridge 連動工作
|
||||
7. 移除迴圈內的 try except 堆疊 增加效能
|
||||
8. 移除對於 mavlinkDevice 的依賴 改用 vehicle_registry 來管理所有的載具
|
||||
|
||||
2026年 01月 15日
|
||||
1. async_io_manager.managed_objects 與 mavlink_object.mavlinkObjects 功能重複整合 保留 mavlink_object.mavlinkObjects
|
||||
2. async_io_manager 的 _stop_event 無效變數移除
|
||||
|
||||
'''
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ class VehicleView:
|
||||
"""
|
||||
if component_id not in self.components:
|
||||
self.components[component_id] = VehicleComponent(component_id, comp_type)
|
||||
logger.info(f"Added component {component_id} to system {self.sysid}")
|
||||
# logger.info(f"Added component {component_id} to system {self.sysid}")
|
||||
return self.components[component_id]
|
||||
|
||||
def get_component(self, component_id: int) -> Optional[VehicleComponent]:
|
||||
@@ -334,10 +334,17 @@ class VehicleView:
|
||||
"""移除組件"""
|
||||
if component_id in self.components:
|
||||
del self.components[component_id]
|
||||
logger.info(f"Removed component {component_id} from system {self.sysid}")
|
||||
# logger.info(f"Removed component {component_id} from system {self.sysid}")
|
||||
return True
|
||||
return False
|
||||
|
||||
def reset_component_stats(self, component_id: int) -> None:
|
||||
"""重置指定組件的封包統計"""
|
||||
component = self.get_component(component_id)
|
||||
if component:
|
||||
component.reset_packet_stats()
|
||||
# logger.info(f"Reset packet stats for component {component_id} in system {self.sysid}")
|
||||
|
||||
def set_rf_module(self, rf_type: RFModuleType) -> RFModule:
|
||||
"""設定RF模組"""
|
||||
self.rf_module = RFModule(rf_type)
|
||||
@@ -435,3 +442,12 @@ class VehicleViewRegistry:
|
||||
|
||||
# 全域註冊表實例
|
||||
vehicle_registry = VehicleViewRegistry()
|
||||
|
||||
'''
|
||||
================= 改版記錄 ============================
|
||||
|
||||
2026.01.16
|
||||
1. 新增 重置指定組件的封包統計 功能
|
||||
|
||||
'''
|
||||
|
||||
|
||||
Reference in New Issue
Block a user