From 5134fa8466f0547757e8e7f5de8097afb036e1d1 Mon Sep 17 00:00:00 2001 From: Chiyu Chen Date: Fri, 19 Dec 2025 10:04:26 +0800 Subject: [PATCH] =?UTF-8?q?commit=20to=20merge=20serial=20port=20=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B7=B2=E9=A9=97=E8=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fc_network_adapter/mainOrchestrator.py | 170 ++++++++++++------ .../fc_network_adapter/serialManager.py | 10 +- 2 files changed, 126 insertions(+), 54 deletions(-) diff --git a/src/fc_network_adapter/fc_network_adapter/mainOrchestrator.py b/src/fc_network_adapter/fc_network_adapter/mainOrchestrator.py index b6c5b1c..e4c0cf0 100644 --- a/src/fc_network_adapter/fc_network_adapter/mainOrchestrator.py +++ b/src/fc_network_adapter/fc_network_adapter/mainOrchestrator.py @@ -37,7 +37,8 @@ class PanelState: self.mavlink_bridge_state = "Stopped" self.object_manager_state = "Stopped" self.serial_manager_state = "Stopped" - self.socket_object_list = [] + self.socket_object_list = [] # 已有的 mavlink object + self.linked_serial_dict = {} # 已連線的 serial 端口 self.panel_info_msg_list = [] # 顯示在面板上的資訊訊息 # 這邊是儲存關於 socket object 的資料 @@ -128,6 +129,8 @@ class ControlPanel: return user_input +# ================ 關於 mavlink object 的部份 =================== + def create_object_list_menu(self, state: PanelState, page=0, items_per_page=8): """動態創建 mavlink_object 列表選單(支持分頁)""" children = [] @@ -155,56 +158,7 @@ class ControlPanel: for child in obj_menu.children: child.socket_id = socket_id children.append(obj_menu) - - # 添加分頁控制 - if total_pages > 1: - children.append(MenuNode("---", "---", None)) - if page > 0: - prev_node = MenuNode("◀ 上一頁", f"第 {page}/{total_pages} 頁", "PREV_PAGE") - prev_node.page = page - 1 - children.append(prev_node) - if page < total_pages - 1: - next_node = MenuNode("下一頁 ▶", f"第 {page + 2}/{total_pages} 頁", "NEXT_PAGE") - next_node.page = page + 1 - children.append(next_node) - - children.append(MenuNode("返回", "回到上層選單", "BACK")) - menu = MenuNode("Object List", f"連結口列表 (第 {page + 1} 頁)", children=children) - menu.current_page = page - return menu - def create_serial_port_menu(self, state: PanelState, page=0, items_per_page=8): - """動態創建 serial port 列表選單(支持分頁)""" - children = [] - - # 獲取可用的 Serial 連接埠列表 - # serial_ports = acquireSerial.get_serial_ports() # debug 全部抓一抓 - serial_ports = acquireSerial.get_serial_ports_with_filter('/dev/ttyUSB*') - - if not serial_ports: - children.append(MenuNode("(空)", "目前沒有串口設備", None)) - else: - total_items = len(serial_ports) - total_pages = (total_items + items_per_page - 1) // items_per_page - start_idx = page * items_per_page - end_idx = min(start_idx + items_per_page, total_items) - - # 顯示當前頁的串口 - for port in serial_ports[start_idx:end_idx]: - port_menu = MenuNode(f"{port}", children=[ - MenuNode("Set Comm Type", "設定通訊形態", "SET_SERIAL_COMM", children=[ - MenuNode("XBee(API-AT)", "XBee 模式", "SET_SERIAL_COMM_XBEE"), - # MenuNode("Telemetry", "數傳模式", "SET_SERIAL_COMM_TELEMETRY"), - ]), - MenuNode("Set Baud", "設定 Baud", "TEXT_BAUD_SERIAL"), - MenuNode("Create", "建立此串口", "CREATE_SERIAL_PORT"), - MenuNode("返回", "回到列表", "BACK"), - ]) - # 將 port 附加到每個子選單項目上 - for child in port_menu.children: - child.port = port - children.append(port_menu) - # 添加分頁控制 if total_pages > 1: children.append(MenuNode("---", f"第 {page+1}/{total_pages} 頁", None)) @@ -216,9 +170,9 @@ class ControlPanel: next_node = MenuNode("Next ▶", "下頁", "NEXT_PAGE") next_node.page = page + 1 children.append(next_node) - + children.append(MenuNode("返回", "回到上層選單", "BACK")) - menu = MenuNode("Serial Port List", f"串口列表 (第 {page + 1} 頁)", children=children) + menu = MenuNode("Object List", f"連結口列表 (第 {page + 1} 頁)", children=children) menu.current_page = page return menu @@ -319,7 +273,107 @@ class ControlPanel: stdscr.clear() stdscr.refresh() return None - + +# ================ 關於 serial link 的部份 =================== + + def create_serial_port_menu(self, state: PanelState, page=0, items_per_page=8): + """動態創建 serial port 列表選單(支持分頁)""" + children = [] + + # 獲取可用的 Serial 連接埠列表 + # serial_ports = acquireSerial.get_serial_ports() # debug 全部抓一抓 + serial_ports = acquireSerial.get_serial_ports_with_filter('/dev/ttyUSB*') + + if not serial_ports: + children.append(MenuNode("(空)", "目前沒有串口設備", None)) + else: + total_items = len(serial_ports) + total_pages = (total_items + items_per_page - 1) // items_per_page + start_idx = page * items_per_page + end_idx = min(start_idx + items_per_page, total_items) + + # 顯示當前頁的串口 + for port in serial_ports[start_idx:end_idx]: + port_menu = MenuNode(f"{port}", children=[ + MenuNode("Set Comm Type", "設定通訊形態", "SET_SERIAL_COMM", children=[ + MenuNode("XBee(API-AT)", "XBee 模式", "SET_SERIAL_COMM_XBEE"), + # MenuNode("Telemetry", "數傳模式", "SET_SERIAL_COMM_TELEMETRY"), + ]), + MenuNode("Set Baud", "設定 Baud", "TEXT_BAUD_SERIAL"), + MenuNode("Create", "建立此串口", "CREATE_SERIAL_PORT"), + MenuNode("返回", "回到列表", "BACK"), + ]) + # 將 port 附加到每個子選單項目上 + for child in port_menu.children: + child.port = port + children.append(port_menu) + + # 添加分頁控制 + if total_pages > 1: + children.append(MenuNode("---", f"第 {page+1}/{total_pages} 頁", None)) + if page > 0: + prev_node = MenuNode("◀ Prev", "上頁", "PREV_PAGE") + prev_node.page = page - 1 + children.append(prev_node) + if page < total_pages - 1: + next_node = MenuNode("Next ▶", "下頁", "NEXT_PAGE") + next_node.page = page + 1 + children.append(next_node) + + children.append(MenuNode("返回", "回到上層選單", "BACK")) + menu = MenuNode("Serial Port List", f"串口列表 (第 {page + 1} 頁)", children=children) + menu.current_page = page + return menu + + def create_linked_serial_menu(self, state: PanelState, page=0, items_per_page=8): + """動態創建 已連線的 serial port 列表選單(支持分頁)並包含後續管理功能""" + children = [] + + if not state.linked_serial_dict: + children.append(MenuNode("(空)", "目前沒有連結口", None)) + else: + total_items = len(state.linked_serial_dict) + total_pages = (total_items + items_per_page - 1) // items_per_page + start_idx = page * items_per_page + end_idx = min(start_idx + items_per_page, total_items) + + # 顯示當前頁的物件 + linked_serial_id_list = list(state.linked_serial_dict.keys()) + for serial_id in linked_serial_id_list[start_idx:end_idx]: + # 為每個 socket 創建子選單 + obj_menu = MenuNode(f"Serial #{serial_id}", f"連結口 {serial_id}", None, children=[ + MenuNode("Info", "查看詳細資訊", "INSPECT_LINKED_SERIAL"), + MenuNode("Remove", "移除此連結口", "REMOVE_LINKED_SERIAL"), + # MenuNode("Change UDP Target", "變更目標 UDP (工程)", "CHANGE_LINKED_SERIAL_TARGET"), + MenuNode("返回", "回到列表", "BACK"), + ]) + # 將 serial_id 附加到每個子選單項目上 + for child in obj_menu.children: + child.serial_id = serial_id + children.append(obj_menu) + + # 添加分頁控制 + if total_pages > 1: + children.append(MenuNode("---", f"第 {page+1}/{total_pages} 頁", None)) + if page > 0: + prev_node = MenuNode("◀ Prev", "上頁", "PREV_PAGE") + prev_node.page = page - 1 + children.append(prev_node) + if page < total_pages - 1: + next_node = MenuNode("Next ▶", "下頁", "NEXT_PAGE") + next_node.page = page + 1 + children.append(next_node) + + children.append(MenuNode("返回", "回到上層選單", "BACK")) + menu = MenuNode("Linked Serial List", f"連結口列表 (第 {page + 1} 頁)", children=children) + menu.current_page = page + return menu + + def show_linked_serial_info(self): + pass + +# ================ 關於 主要選單 的部份 =================== + def menu_tree(self): """建立多層選單結構""" return MenuNode("Main Menu", children=[ @@ -603,6 +657,11 @@ class ControlPanel: created_list_menu = self.create_serial_port_menu(state, page=0) menu_stack.append(created_list_menu) idx_stack.append(0) + + elif selected.action == "LIST_SERIAL_LINKS": + created_list_menu = self.create_linked_serial_menu(state, page=0) + menu_stack.append(created_list_menu) + idx_stack.append(0) elif selected.action == "LIST_MAV_OBJECT": # 動態生成 mavlink_object 列表選單 @@ -621,6 +680,8 @@ class ControlPanel: created_list_menu = self.create_serial_port_menu(state, page=selected.page) elif current_list_menu.name == "Object List": created_list_menu = self.create_object_list_menu(state, page=selected.page) + elif current_list_menu.name == "Linked Serial List": + created_list_menu = self.create_linked_serial_menu(state, page=selected.page) else: # 不支援的選單類型,回到原本的選單 menu_stack.append(current_list_menu) @@ -763,6 +824,9 @@ class Orchestrator: else: self.panelState.serial_manager_state = 'Stopped' + linked_serial_dict = self.plumber.get_serial_link() + self.panelState.linked_serial_dict = linked_serial_dict + # 取出面板丟過來的「動作」 try: cmd = self.cmd_q.get_nowait() diff --git a/src/fc_network_adapter/fc_network_adapter/serialManager.py b/src/fc_network_adapter/fc_network_adapter/serialManager.py index 1cf79e0..e1f7a80 100644 --- a/src/fc_network_adapter/fc_network_adapter/serialManager.py +++ b/src/fc_network_adapter/fc_network_adapter/serialManager.py @@ -473,7 +473,6 @@ class serial_manager: logger.error(f"Failed to create serial link for {serial_port}: {e}") return False - async def _async_create_serial_link(self, serial_port, baudrate, target_port, receiver_type: SerialReceiverType): """在事件循環線程中執行實際的連接創建""" try: @@ -579,6 +578,12 @@ class serial_manager: logger.error(f"Exception in _async_remove_serial_link for {serial_id}: {str(e)}") return False + def get_serial_link(self): + ret = {} # serial id num : serial_port string + for key, obj in self.serial_objects.items(): + ret[key] = obj.serial_port + return ret + @staticmethod def check_serial_port(serial_port, baudrate): """檢查串口是否存在與可用""" @@ -619,5 +624,8 @@ if __name__ == '__main__': UDP_REMOTE_PORT = 14571 sm.create_serial_link(SERIAL_PORT, SERIAL_BAUDRATE, UDP_REMOTE_PORT, SerialReceiverType.XBEEAPI2AT) + linked_serial = sm.get_serial_link() + print(linked_serial) + time.sleep(10) sm.shutdown() \ No newline at end of file