|
|
|
|
@ -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 = []
|
|
|
|
|
@ -156,55 +159,6 @@ class ControlPanel:
|
|
|
|
|
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))
|
|
|
|
|
@ -218,7 +172,7 @@ class ControlPanel:
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
@ -320,6 +274,106 @@ class ControlPanel:
|
|
|
|
|
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=[
|
|
|
|
|
@ -604,6 +658,11 @@ class ControlPanel:
|
|
|
|
|
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 列表選單
|
|
|
|
|
created_list_menu = self.create_object_list_menu(state, page=0)
|
|
|
|
|
@ -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()
|
|
|
|
|
|