commit to merge
serial port 功能已驗證
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user