commit to merge

serial port 功能已驗證
chiyu
Chiyu Chen 5 months ago
parent f31cc8742a
commit 5134fa8466

@ -37,7 +37,8 @@ class PanelState:
self.mavlink_bridge_state = "Stopped" self.mavlink_bridge_state = "Stopped"
self.object_manager_state = "Stopped" self.object_manager_state = "Stopped"
self.serial_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 = [] # 顯示在面板上的資訊訊息 self.panel_info_msg_list = [] # 顯示在面板上的資訊訊息
# 這邊是儲存關於 socket object 的資料 # 這邊是儲存關於 socket object 的資料
@ -128,6 +129,8 @@ class ControlPanel:
return user_input return user_input
# ================ 關於 mavlink object 的部份 ===================
def create_object_list_menu(self, state: PanelState, page=0, items_per_page=8): def create_object_list_menu(self, state: PanelState, page=0, items_per_page=8):
"""動態創建 mavlink_object 列表選單(支持分頁)""" """動態創建 mavlink_object 列表選單(支持分頁)"""
children = [] children = []
@ -155,56 +158,7 @@ class ControlPanel:
for child in obj_menu.children: for child in obj_menu.children:
child.socket_id = socket_id child.socket_id = socket_id
children.append(obj_menu) 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: if total_pages > 1:
children.append(MenuNode("---", f"{page+1}/{total_pages}", None)) children.append(MenuNode("---", f"{page+1}/{total_pages}", None))
@ -216,9 +170,9 @@ class ControlPanel:
next_node = MenuNode("Next ▶", "下頁", "NEXT_PAGE") next_node = MenuNode("Next ▶", "下頁", "NEXT_PAGE")
next_node.page = page + 1 next_node.page = page + 1
children.append(next_node) children.append(next_node)
children.append(MenuNode("返回", "回到上層選單", "BACK")) 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 menu.current_page = page
return menu return menu
@ -319,7 +273,107 @@ class ControlPanel:
stdscr.clear() stdscr.clear()
stdscr.refresh() stdscr.refresh()
return None 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): def menu_tree(self):
"""建立多層選單結構""" """建立多層選單結構"""
return MenuNode("Main Menu", children=[ return MenuNode("Main Menu", children=[
@ -603,6 +657,11 @@ class ControlPanel:
created_list_menu = self.create_serial_port_menu(state, page=0) created_list_menu = self.create_serial_port_menu(state, page=0)
menu_stack.append(created_list_menu) menu_stack.append(created_list_menu)
idx_stack.append(0) 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": elif selected.action == "LIST_MAV_OBJECT":
# 動態生成 mavlink_object 列表選單 # 動態生成 mavlink_object 列表選單
@ -621,6 +680,8 @@ class ControlPanel:
created_list_menu = self.create_serial_port_menu(state, page=selected.page) created_list_menu = self.create_serial_port_menu(state, page=selected.page)
elif current_list_menu.name == "Object List": elif current_list_menu.name == "Object List":
created_list_menu = self.create_object_list_menu(state, page=selected.page) 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: else:
# 不支援的選單類型,回到原本的選單 # 不支援的選單類型,回到原本的選單
menu_stack.append(current_list_menu) menu_stack.append(current_list_menu)
@ -763,6 +824,9 @@ class Orchestrator:
else: else:
self.panelState.serial_manager_state = 'Stopped' self.panelState.serial_manager_state = 'Stopped'
linked_serial_dict = self.plumber.get_serial_link()
self.panelState.linked_serial_dict = linked_serial_dict
# 取出面板丟過來的「動作」 # 取出面板丟過來的「動作」
try: try:
cmd = self.cmd_q.get_nowait() cmd = self.cmd_q.get_nowait()

@ -473,7 +473,6 @@ class serial_manager:
logger.error(f"Failed to create serial link for {serial_port}: {e}") logger.error(f"Failed to create serial link for {serial_port}: {e}")
return False return False
async def _async_create_serial_link(self, serial_port, baudrate, target_port, receiver_type: SerialReceiverType): async def _async_create_serial_link(self, serial_port, baudrate, target_port, receiver_type: SerialReceiverType):
"""在事件循環線程中執行實際的連接創建""" """在事件循環線程中執行實際的連接創建"""
try: try:
@ -579,6 +578,12 @@ class serial_manager:
logger.error(f"Exception in _async_remove_serial_link for {serial_id}: {str(e)}") logger.error(f"Exception in _async_remove_serial_link for {serial_id}: {str(e)}")
return False 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 @staticmethod
def check_serial_port(serial_port, baudrate): def check_serial_port(serial_port, baudrate):
"""檢查串口是否存在與可用""" """檢查串口是否存在與可用"""
@ -619,5 +624,8 @@ if __name__ == '__main__':
UDP_REMOTE_PORT = 14571 UDP_REMOTE_PORT = 14571
sm.create_serial_link(SERIAL_PORT, SERIAL_BAUDRATE, UDP_REMOTE_PORT, SerialReceiverType.XBEEAPI2AT) 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) time.sleep(10)
sm.shutdown() sm.shutdown()
Loading…
Cancel
Save