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