forked from chiyu1468/AirTrapMine
1. 調整檔案結構 變動 import 的路徑與方法
2. 新增 mainOrchestrator.py 作為接下來開發介面化統合工具的主要檔案
This commit is contained in:
@@ -33,6 +33,15 @@ Package 簡述
|
|||||||
2. fc_network_adapter
|
2. fc_network_adapter
|
||||||
建立、維護與飛控韌體的連接
|
建立、維護與飛控韌體的連接
|
||||||
構築 mavlink 封包
|
構築 mavlink 封包
|
||||||
同時處理與 Gazebo 的 ardupilot_plugin 溝通的 FDM/JSON 訊息
|
|
||||||
處理無線模組的通訊格式 (XBee)
|
處理無線模組的通訊格式 (XBee)
|
||||||
|
--同時處理與 Gazebo 的 ardupilot_plugin 溝通的 FDM/JSON 訊息 (移除)--
|
||||||
|
|
||||||
|
N. logs 是執行時期的記錄檔
|
||||||
|
|
||||||
|
===
|
||||||
|
請一律在 ~/AirTrapMine/src/ 資料夾下 以模組化啟動程式
|
||||||
|
|
||||||
|
例如 在 ~/AirTrapMine/src/ 資料夾下
|
||||||
|
> python -m fc_network_adapter.fc_network_adapter.mainOrchestrator
|
||||||
|
> python -m fc_network_adapter.tests.test_ringBuffer
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
主要調配流程的程式
|
||||||
|
|
||||||
|
這個檔案包含 Terminal Utility Layer (TUL) 作為人機互動介面,並調用 mavlinkDevice 和 mavlinkObject 來處理 MAVLink 通訊和物件管理。
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import curses
|
||||||
|
import threading
|
||||||
|
import queue
|
||||||
|
import signal
|
||||||
|
|
||||||
|
# 自定義的 import
|
||||||
|
from . import mavlinkObject as mo
|
||||||
|
from . import mavlinkDevice as md
|
||||||
|
from .utils import RingBuffer, setup_logger
|
||||||
|
|
||||||
|
logger = setup_logger(os.path.basename(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class MenuNode:
|
||||||
|
def __init__(self, name, desc="", action=None, children=None):
|
||||||
|
self.name = name
|
||||||
|
self.desc = desc
|
||||||
|
self.action = action # 可以是函式或特殊字串
|
||||||
|
self.children = children or [] # 子選單列表
|
||||||
|
|
||||||
|
class ControlPanel:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def menu_tree(self):
|
||||||
|
"""建立多層選單結構"""
|
||||||
|
return MenuNode("Main Menu", children=[
|
||||||
|
MenuNode("MavLink Object Control", children=[
|
||||||
|
MenuNode("New+", "新增一個連結口", "ADD_MAV_OBJECT"),
|
||||||
|
MenuNode("Remove", "移除某個連結口", "REMOVE_MAV_OBJECT"),
|
||||||
|
MenuNode("ListAll", "顯示連結口列表", "LIST_MAV_OBJECT"),
|
||||||
|
MenuNode("Inspection", "顯示連結口資訊", "INSPECT_MAV_OBJECT"),
|
||||||
|
]),
|
||||||
|
MenuNode("參數設定", children=[
|
||||||
|
MenuNode("Speed", children=[
|
||||||
|
MenuNode("Increase Speed", "增加速度", "ADJUST_SPEED"),
|
||||||
|
MenuNode("Decrease Speed", "減少速度", "ADJUST_SPEED"),
|
||||||
|
]),
|
||||||
|
MenuNode("輸入文字", "鍵盤輸入模式", "INPUT_TEXT"),
|
||||||
|
]),
|
||||||
|
MenuNode("資訊查看", children=[
|
||||||
|
MenuNode("顯示狀態", "查看當前狀態", "SHOW_STATUS"),
|
||||||
|
MenuNode("顯示輸入", "查看用戶輸入", "SHOW_INPUT"),
|
||||||
|
]),
|
||||||
|
MenuNode("返回上層", "回到上一層選單", "BACK"),
|
||||||
|
MenuNode("關閉面板", "關閉控制面板", "QUIT"),
|
||||||
|
])
|
||||||
|
|
||||||
|
def panel_thread(self, cmd_q: queue.Queue, stop_evt: threading.Event):
|
||||||
|
stdscr = None
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
"""清理 curses 狀態"""
|
||||||
|
if stdscr:
|
||||||
|
stdscr.keypad(False)
|
||||||
|
curses.nocbreak()
|
||||||
|
curses.echo()
|
||||||
|
curses.endwin()
|
||||||
|
|
||||||
|
def draw_menu(screen):
|
||||||
|
nonlocal stdscr
|
||||||
|
stdscr = screen
|
||||||
|
|
||||||
|
curses.curs_set(0)
|
||||||
|
stdscr.nodelay(False) # 阻塞讀鍵
|
||||||
|
stdscr.keypad(True)
|
||||||
|
|
||||||
|
# 選單導航狀態
|
||||||
|
menu_stack = [self.menu_tree()] # 選單堆疊
|
||||||
|
idx_stack = [0] # 索引堆疊
|
||||||
|
|
||||||
|
while not stop_evt.is_set():
|
||||||
|
# 檢查是否需要退出
|
||||||
|
if stop_evt.is_set():
|
||||||
|
break
|
||||||
|
|
||||||
|
current_menu = menu_stack[-1]
|
||||||
|
current_idx = idx_stack[-1]
|
||||||
|
|
||||||
|
stdscr.clear()
|
||||||
|
|
||||||
|
stdscr.border()
|
||||||
|
stdscr.addstr(0, 10, " MavLink MiddleWare ", curses.A_BOLD)
|
||||||
|
stdscr.addstr(1, 2, f"mavlink bridge state : ")
|
||||||
|
stdscr.addstr(2, 2, f"object manager state : ")
|
||||||
|
stdscr.addstr(3, 2, f"Node state : ")
|
||||||
|
|
||||||
|
# # Header - 顯示選單路徑
|
||||||
|
# path = " → ".join([menu.name for menu in menu_stack])
|
||||||
|
# stdscr.addstr(0, 2, f"控制面板: {path}", curses.A_BOLD)
|
||||||
|
# stdscr.addstr(1, 2, f"狀態: {state.status} | 速度: {state.speed} | 計數: {state.counter}")
|
||||||
|
# if state.user_input:
|
||||||
|
# stdscr.addstr(2, 2, f"輸入: {state.user_input[:50]}...")
|
||||||
|
|
||||||
|
# 顯示當前選單項目
|
||||||
|
start_line = 5
|
||||||
|
for i, child in enumerate(current_menu.children):
|
||||||
|
marker = "➤ " if i == current_idx else " "
|
||||||
|
line = f"{marker}{child.name:15s} – {child.desc}"
|
||||||
|
attr = curses.A_REVERSE if i == current_idx else curses.A_NORMAL
|
||||||
|
stdscr.addstr(start_line + i, 4, line, attr)
|
||||||
|
|
||||||
|
# 操作說明
|
||||||
|
help_line = start_line + len(current_menu.children) + 2
|
||||||
|
stdscr.addstr(help_line, 2, "操作: ↑↓選擇 Enter確認 ←返回上層 ←→調參數 q退出", curses.A_DIM)
|
||||||
|
|
||||||
|
stdscr.refresh()
|
||||||
|
|
||||||
|
# 設定短暫的 timeout,讓執行緒能夠響應 stop_evt
|
||||||
|
stdscr.timeout(100) # 100ms timeout
|
||||||
|
ch = stdscr.getch()
|
||||||
|
|
||||||
|
if ch == -1: # timeout,繼續檢查 stop_evt
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 處理按鍵
|
||||||
|
if ch in (curses.KEY_UP, ord('k')):
|
||||||
|
idx_stack[-1] = (current_idx - 1) % len(current_menu.children)
|
||||||
|
|
||||||
|
elif ch in (curses.KEY_DOWN, ord('j')):
|
||||||
|
idx_stack[-1] = (current_idx + 1) % len(current_menu.children)
|
||||||
|
|
||||||
|
elif ch == curses.KEY_LEFT:
|
||||||
|
# 返回上層或調整參數
|
||||||
|
if len(menu_stack) > 1:
|
||||||
|
menu_stack.pop()
|
||||||
|
idx_stack.pop()
|
||||||
|
else:
|
||||||
|
# 在根選單,檢查是否為調整參數
|
||||||
|
selected = current_menu.children[current_idx]
|
||||||
|
if selected.action == "ADJUST_SPEED":
|
||||||
|
state.speed = max(1, state.speed - 1)
|
||||||
|
|
||||||
|
elif ch == curses.KEY_RIGHT:
|
||||||
|
# 調整參數
|
||||||
|
selected = current_menu.children[current_idx]
|
||||||
|
if selected.action == "ADJUST_SPEED":
|
||||||
|
state.speed = min(10, state.speed + 1)
|
||||||
|
|
||||||
|
elif ch in (curses.KEY_ENTER, 10, 13):
|
||||||
|
selected = current_menu.children[current_idx]
|
||||||
|
|
||||||
|
# 處理不同類型的動作
|
||||||
|
if selected.children: # 有子選單
|
||||||
|
menu_stack.append(selected)
|
||||||
|
idx_stack.append(0)
|
||||||
|
|
||||||
|
elif selected.action == "BACK":
|
||||||
|
if len(menu_stack) > 1:
|
||||||
|
menu_stack.pop()
|
||||||
|
idx_stack.pop()
|
||||||
|
|
||||||
|
elif selected.action == "QUIT":
|
||||||
|
break
|
||||||
|
|
||||||
|
elif selected.action == "INPUT_TEXT":
|
||||||
|
# 進入輸入模式
|
||||||
|
result = input_dialog(stdscr, "請輸入文字: ")
|
||||||
|
if result is not None:
|
||||||
|
cmd_q.put(lambda: state.set_user_input(result))
|
||||||
|
|
||||||
|
elif selected.action == "SHOW_STATUS":
|
||||||
|
# 顯示狀態訊息
|
||||||
|
stdscr.clear()
|
||||||
|
stdscr.addstr(5, 2, f"當前狀態: {state.status}")
|
||||||
|
stdscr.addstr(6, 2, f"速度設定: {state.speed}")
|
||||||
|
stdscr.addstr(7, 2, f"計數器: {state.counter}")
|
||||||
|
stdscr.addstr(9, 2, "按任意鍵返回...")
|
||||||
|
stdscr.refresh()
|
||||||
|
stdscr.getch()
|
||||||
|
|
||||||
|
elif selected.action == "SHOW_INPUT":
|
||||||
|
# 顯示用戶輸入
|
||||||
|
stdscr.clear()
|
||||||
|
stdscr.addstr(5, 2, f"用戶輸入內容:")
|
||||||
|
stdscr.addstr(6, 2, f"{state.user_input}")
|
||||||
|
stdscr.addstr(8, 2, "按任意鍵返回...")
|
||||||
|
stdscr.refresh()
|
||||||
|
stdscr.getch()
|
||||||
|
|
||||||
|
elif callable(selected.action):
|
||||||
|
# 執行函式
|
||||||
|
cmd_q.put(selected.action)
|
||||||
|
|
||||||
|
elif ch in (ord('q'), 27):
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
curses.wrapper(draw_menu)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logger.warning(f"Hello this is mainOrchestrator.py")
|
||||||
|
pp = ControlPanel()
|
||||||
|
|
||||||
|
cmd_q = queue.Queue()
|
||||||
|
stop_evt = threading.Event()
|
||||||
|
panel_thread_obj = None
|
||||||
|
|
||||||
|
def signal_handler(signum, frame):
|
||||||
|
"""處理 Ctrl+C 信號"""
|
||||||
|
print("\n收到中斷信號,正在安全退出...")
|
||||||
|
stop_evt.set()
|
||||||
|
if panel_thread_obj and panel_thread_obj.is_alive():
|
||||||
|
panel_thread_obj.join(timeout=2)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# 註冊信號處理器
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
|
||||||
|
# 啟動控制面板(改為非 daemon)
|
||||||
|
panel_thread_obj = threading.Thread(target=pp.panel_thread, args=(cmd_q, stop_evt))
|
||||||
|
panel_thread_obj.start()
|
||||||
|
|
||||||
|
print("多層選單控制面板啟動。Ctrl+C 結束程式。")
|
||||||
|
|
||||||
|
try:
|
||||||
|
while not stop_evt.is_set():
|
||||||
|
# 取出面板丟過來的「動作」
|
||||||
|
try:
|
||||||
|
fn = cmd_q.get_nowait()
|
||||||
|
fn() # 執行對應動作
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# # 模擬你的長跑邏輯
|
||||||
|
# if state.status == "running":
|
||||||
|
# # 依 speed 前進
|
||||||
|
# state.counter += state.speed
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n收到 Ctrl+C,準備結束...")
|
||||||
|
finally:
|
||||||
|
stop_evt.set()
|
||||||
|
if panel_thread_obj.is_alive():
|
||||||
|
panel_thread_obj.join(timeout=2)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
# 自定義的 import
|
# 自定義的 import
|
||||||
from .theLogger import setup_logger
|
from .utils import setup_logger
|
||||||
|
|
||||||
# ====================== 分割線 =====================
|
# ====================== 分割線 =====================
|
||||||
|
|
||||||
logger = setup_logger("mavlinkDevice.py")
|
logger = setup_logger(os.path.basename(__file__))
|
||||||
|
|
||||||
# 用來記錄每個 system 的資訊
|
# 用來記錄每個 system 的資訊
|
||||||
# 資料格式 { sysid : mavlink_device object }
|
# 資料格式 { sysid : mavlink_device object }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
# 基礎功能的 import
|
# 基礎功能的 import
|
||||||
import threading
|
import threading
|
||||||
|
import os
|
||||||
# import queue
|
# import queue
|
||||||
import time
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -27,12 +28,12 @@ from rclpy.node import Node
|
|||||||
# 自定義的 import
|
# 自定義的 import
|
||||||
from .mavlinkDevice import mavlink_device, mavlink_systems
|
from .mavlinkDevice import mavlink_device, mavlink_systems
|
||||||
from .mavlinkPublish import mavlink_publisher
|
from .mavlinkPublish import mavlink_publisher
|
||||||
from .theLogger import setup_logger
|
from .utils import RingBuffer, setup_logger
|
||||||
from .ringBuffer import RingBuffer
|
|
||||||
|
|
||||||
# ====================== 分割線 =====================
|
# ====================== 分割線 =====================
|
||||||
|
|
||||||
logger = setup_logger("mavlinkObject.py")
|
logger = setup_logger(os.path.basename(__file__))
|
||||||
|
|
||||||
stream_bridge_ring = RingBuffer(capacity=1024, buffer_id=255)
|
stream_bridge_ring = RingBuffer(capacity=1024, buffer_id=255)
|
||||||
return_packet_ring = RingBuffer(capacity=256, buffer_id=254)
|
return_packet_ring = RingBuffer(capacity=256, buffer_id=254)
|
||||||
@@ -328,7 +329,7 @@ class mavlink_object:
|
|||||||
|
|
||||||
while self.state == MavlinkObjectState.RUNNING:
|
while self.state == MavlinkObjectState.RUNNING:
|
||||||
timestamp = time.time()
|
timestamp = time.time()
|
||||||
try:
|
try: #TODO 這邊的錯誤處理要再想想看怎麼做比較好
|
||||||
# 處理接收到的封包
|
# 處理接收到的封包
|
||||||
mavlinkMsg = self.mavlink_socket.recv_msg()
|
mavlinkMsg = self.mavlink_socket.recv_msg()
|
||||||
# except Exception as e:
|
# except Exception as e:
|
||||||
@@ -368,7 +369,7 @@ class mavlink_object:
|
|||||||
if target_port != self.socket_id and target_port in self.mavlinkObjects:
|
if target_port != self.socket_id and target_port in self.mavlinkObjects:
|
||||||
target_obj = self.mavlinkObjects[target_port]
|
target_obj = self.mavlinkObjects[target_port]
|
||||||
if target_obj.state == MavlinkObjectState.RUNNING:
|
if target_obj.state == MavlinkObjectState.RUNNING:
|
||||||
try:
|
try: # TODO 藉由 if 的檢測 確定目標端口是開啟狀態後 再進行寫出 之後刪掉 try except
|
||||||
target_obj.mavlink_socket.write(mavlinkMsg.get_msgbuf())
|
target_obj.mavlink_socket.write(mavlinkMsg.get_msgbuf())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error forwarding message to port {target_port}: {e}")
|
logger.error(f"Error forwarding message to port {target_port}: {e}")
|
||||||
@@ -449,7 +450,7 @@ class mavlink_object:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# 使用鎖保護共享資源訪問
|
# 使用鎖保護共享資源訪問
|
||||||
with self.outgoing_msgs_lock:
|
with self.outgoing_msg_lock:
|
||||||
self.outgoing_msgs.append(message_bytes)
|
self.outgoing_msgs.append(message_bytes)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -559,7 +560,7 @@ class async_io_manager:
|
|||||||
task = self.managed_objects[socket_id]
|
task = self.managed_objects[socket_id]
|
||||||
|
|
||||||
if task.done():
|
if task.done():
|
||||||
try:
|
try: # TODO 這邊的錯誤處理要再想想看怎麼做比較好
|
||||||
exc = task.exception()
|
exc = task.exception()
|
||||||
if exc:
|
if exc:
|
||||||
logger.error(f"Task for mavlink_object {socket_id} raised exception: {exc}")
|
logger.error(f"Task for mavlink_object {socket_id} raised exception: {exc}")
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
publisher topic name 命名規則為 <前綴詞>/s<sysid>/<具體 topic>
|
publisher topic name 命名規則為 <前綴詞>/s<sysid>/<具體 topic>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
# ROS2 的 import
|
# ROS2 的 import
|
||||||
import std_msgs.msg
|
import std_msgs.msg
|
||||||
import sensor_msgs.msg
|
import sensor_msgs.msg
|
||||||
@@ -18,9 +20,9 @@ import mavros_msgs.msg
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
# 自定義的 import
|
# 自定義的 import
|
||||||
from .theLogger import setup_logger
|
from .utils import setup_logger
|
||||||
|
|
||||||
logger = setup_logger("mavlinkPublish.py")
|
logger = setup_logger(os.path.basename(__file__))
|
||||||
|
|
||||||
class mavlink_publisher():
|
class mavlink_publisher():
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
共用工具模組
|
||||||
|
"""
|
||||||
|
from .ringBuffer import RingBuffer
|
||||||
|
from .theLogger import setup_logger
|
||||||
|
|
||||||
|
__all__ = ['RingBuffer', 'setup_logger']
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from logging.handlers import TimedRotatingFileHandler
|
|||||||
# 全域 Logger 實例
|
# 全域 Logger 實例
|
||||||
_global_logger = None
|
_global_logger = None
|
||||||
|
|
||||||
def setup_logger(name: str, log_dir: str = "log", level=logging.DEBUG) -> logging.Logger:
|
def setup_logger(name: str, log_dir: str = "logs", level=logging.DEBUG) -> logging.Logger:
|
||||||
global _global_logger
|
global _global_logger
|
||||||
|
|
||||||
if _global_logger is None:
|
if _global_logger is None:
|
||||||
@@ -20,6 +20,7 @@ setup(
|
|||||||
tests_require=['pytest'],
|
tests_require=['pytest'],
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
|
'mavlink_orchestrator = fc_network_adapter.mainOrchestrator:main',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-3
@@ -13,12 +13,12 @@ import rclpy
|
|||||||
from pymavlink import mavutil
|
from pymavlink import mavutil
|
||||||
|
|
||||||
# 自定義的 import
|
# 自定義的 import
|
||||||
from fc_network_adapter import mavlinkObject as mo
|
from ..fc_network_adapter import mavlinkObject as mo
|
||||||
from fc_network_adapter import mavlinkDevice as md
|
from ..fc_network_adapter import mavlinkDevice as md
|
||||||
|
|
||||||
# ====================== 分割線 =====================
|
# ====================== 分割線 =====================
|
||||||
|
|
||||||
test_item = 12
|
test_item = 10
|
||||||
running_time = 10000
|
running_time = 10000
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from ..fc_network_adapter.utils import RingBuffer
|
||||||
|
|
||||||
|
|
||||||
|
def producer(buffer, count, interval=0.01):
|
||||||
|
"""生產者:向緩衝區添加資料"""
|
||||||
|
print(f"Producer started (thread {threading.get_ident()})")
|
||||||
|
for i in range(count):
|
||||||
|
# 嘗試寫入數據,直到成功
|
||||||
|
while not buffer.put(f"Item-{i}"):
|
||||||
|
print(f"Buffer full, producer waiting... (thread {threading.get_ident()})")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print(f"Produced: Item-{i}, buffer size: {buffer.size()}")
|
||||||
|
time.sleep(interval) # 模擬生產過程
|
||||||
|
|
||||||
|
print(f"Producer finished (thread {threading.get_ident()})")
|
||||||
|
|
||||||
|
def consumer(buffer, max_items, interval=0.05):
|
||||||
|
"""消費者:從緩衝區讀取資料"""
|
||||||
|
print(f"Consumer started (thread {threading.get_ident()})")
|
||||||
|
items_consumed = 0
|
||||||
|
|
||||||
|
while items_consumed < max_items:
|
||||||
|
# 嘗試讀取數據
|
||||||
|
item = buffer.get()
|
||||||
|
if item:
|
||||||
|
print(f"Consumed: {item}, buffer size: {buffer.size()}")
|
||||||
|
items_consumed += 1
|
||||||
|
else:
|
||||||
|
print(f"Buffer empty, consumer waiting... (thread {threading.get_ident()})")
|
||||||
|
|
||||||
|
time.sleep(interval) # 模擬消費過程
|
||||||
|
|
||||||
|
print(f"Consumer finished (thread {threading.get_ident()})")
|
||||||
|
|
||||||
|
def batch_consumer(buffer, interval=0.2):
|
||||||
|
"""批量消費者:一次性讀取緩衝區中的所有資料"""
|
||||||
|
print(f"Batch consumer started (thread {threading.get_ident()})")
|
||||||
|
|
||||||
|
for _ in range(5): # 執行5次批量讀取
|
||||||
|
time.sleep(interval) # 等待緩衝區積累數據
|
||||||
|
items = buffer.get_all()
|
||||||
|
if items:
|
||||||
|
print(f"Batch consumed {len(items)} items: {items}")
|
||||||
|
else:
|
||||||
|
print("Buffer empty for batch consumer")
|
||||||
|
|
||||||
|
print(f"Batch consumer finished (thread {threading.get_ident()})")
|
||||||
|
|
||||||
|
def demonstrate_multi_writer():
|
||||||
|
"""示範多個寫入執行緒同時操作緩衝區"""
|
||||||
|
print("\n=== Demonstrating Multiple Writers ===")
|
||||||
|
buffer = RingBuffer(capacity=80)
|
||||||
|
|
||||||
|
# 創建多個生產者執行緒
|
||||||
|
threads = []
|
||||||
|
for i in range(3):
|
||||||
|
thread = threading.Thread(target=producer, args=(buffer, 5, 0.1 * (i+1)))
|
||||||
|
threads.append(thread)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
# 等待所有執行緒完成
|
||||||
|
for thread in threads:
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
buffer.print_stats() # 印出統計資訊
|
||||||
|
|
||||||
|
# 讀出所有剩餘資料
|
||||||
|
remaining = buffer.get_all()
|
||||||
|
print(f"Remaining items in buffer after multiple writers: {remaining}")
|
||||||
|
|
||||||
|
def demonstrate_basic_usage():
|
||||||
|
"""示範基本使用方式"""
|
||||||
|
print("\n=== Demonstrating Basic Usage ===")
|
||||||
|
# 創建緩衝區
|
||||||
|
buffer = RingBuffer(capacity=20, buffer_id=7)
|
||||||
|
|
||||||
|
# 檢查初始狀態
|
||||||
|
print(f"Initial buffer state - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
|
||||||
|
|
||||||
|
# 添加幾個項目
|
||||||
|
for i in range(5):
|
||||||
|
buffer.put(f"Test-{i}")
|
||||||
|
|
||||||
|
# 檢查狀態
|
||||||
|
print(f"After adding 5 items - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
|
||||||
|
|
||||||
|
# 讀取一個項目
|
||||||
|
item = buffer.get()
|
||||||
|
print(f"Read item: {item}")
|
||||||
|
print(f"After reading 1 item - Content Size: {buffer.size()}")
|
||||||
|
|
||||||
|
# 添加更多項目直到滿
|
||||||
|
items_added = 0
|
||||||
|
while not buffer.is_full():
|
||||||
|
buffer.put(f"Fill-{items_added}")
|
||||||
|
items_added += 1
|
||||||
|
|
||||||
|
print(f"Added {items_added} more items until full")
|
||||||
|
print(f"Buffer full state - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
|
||||||
|
|
||||||
|
# 嘗試添加到已滿的緩衝區
|
||||||
|
result = buffer.put("Overflow")
|
||||||
|
print(f"Attempt to add to full buffer: {'Succeeded' if result else 'Failed'}")
|
||||||
|
|
||||||
|
# 獲取所有項目
|
||||||
|
all_items = buffer.get_all()
|
||||||
|
print(f"All items in buffer: {all_items}")
|
||||||
|
print(f"Buffer after get_all() - Empty: {buffer.is_empty()}, Content Size: {buffer.size()}")
|
||||||
|
|
||||||
|
# 印出統計資訊
|
||||||
|
buffer.print_stats()
|
||||||
|
|
||||||
|
def demonstrate_producer_consumer():
|
||||||
|
"""示範生產者-消費者模式"""
|
||||||
|
print("\n=== Demonstrating Producer-Consumer Pattern ===")
|
||||||
|
buffer = RingBuffer(capacity=16)
|
||||||
|
|
||||||
|
# 創建生產者和消費者執行緒
|
||||||
|
producer_thread = threading.Thread(target=producer, args=(buffer, 20, 0.1))
|
||||||
|
consumer_thread = threading.Thread(target=consumer, args=(buffer, 3, 0.2))
|
||||||
|
batch_thread = threading.Thread(target=batch_consumer, args=(buffer, 0.5))
|
||||||
|
|
||||||
|
# 啟動執行緒
|
||||||
|
producer_thread.start()
|
||||||
|
consumer_thread.start()
|
||||||
|
batch_thread.start()
|
||||||
|
|
||||||
|
# 等待執行緒完成
|
||||||
|
producer_thread.join()
|
||||||
|
consumer_thread.join()
|
||||||
|
batch_thread.join()
|
||||||
|
|
||||||
|
# 檢查最終狀態
|
||||||
|
print(f"Final buffer state - Empty: {buffer.is_empty()}, Size: {buffer.size()}")
|
||||||
|
|
||||||
|
buffer.print_stats()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 展示各種使用場景
|
||||||
|
# demonstrate_basic_usage()
|
||||||
|
# demonstrate_producer_consumer()
|
||||||
|
demonstrate_multi_writer()
|
||||||
|
|
||||||
|
print("\nAll demonstrations completed!")
|
||||||
@@ -16,7 +16,7 @@ import asyncio
|
|||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
# 導入要測試的模組
|
# 導入要測試的模組
|
||||||
from fc_network_adapter.mavlinkObject import (
|
from ..fc_network_adapter.mavlinkObject import (
|
||||||
mavlink_object,
|
mavlink_object,
|
||||||
async_io_manager,
|
async_io_manager,
|
||||||
MavlinkObjectState,
|
MavlinkObjectState,
|
||||||
@@ -466,3 +466,4 @@ class TestIntegration(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main(defaultTest="TestMavlinkObject.test_send_message")
|
unittest.main(defaultTest="TestMavlinkObject.test_send_message")
|
||||||
|
# unittest.main(defaultTest="TestAsyncIOManager")
|
||||||
|
|||||||
@@ -1,152 +1,296 @@
|
|||||||
import os
|
#!/usr/bin/env python
|
||||||
import sys
|
"""
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
測試 RingBuffer 類別的功能
|
||||||
|
"""
|
||||||
|
|
||||||
import time
|
import unittest
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
from fc_network_adapter.ringBuffer import RingBuffer
|
from ..fc_network_adapter.utils import RingBuffer
|
||||||
|
|
||||||
|
class TestRingBuffer(unittest.TestCase):
|
||||||
|
"""測試 RingBuffer 基本功能"""
|
||||||
|
|
||||||
def producer(buffer, count, interval=0.01):
|
def setUp(self):
|
||||||
"""生產者:向緩衝區添加資料"""
|
"""每個測試前的準備"""
|
||||||
print(f"Producer started (thread {threading.get_ident()})")
|
self.buffer = RingBuffer(capacity=8)
|
||||||
for i in range(count):
|
|
||||||
# 嘗試寫入數據,直到成功
|
|
||||||
while not buffer.put(f"Item-{i}"):
|
|
||||||
print(f"Buffer full, producer waiting... (thread {threading.get_ident()})")
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
print(f"Produced: Item-{i}, buffer size: {buffer.size()}")
|
def test_initialization(self):
|
||||||
time.sleep(interval) # 模擬生產過程
|
"""測試初始化"""
|
||||||
|
self.assertEqual(self.buffer.capacity, 8)
|
||||||
|
self.assertTrue(self.buffer.is_empty())
|
||||||
|
self.assertFalse(self.buffer.is_full())
|
||||||
|
self.assertEqual(self.buffer.size(), 0)
|
||||||
|
|
||||||
print(f"Producer finished (thread {threading.get_ident()})")
|
def test_put_get_basic(self):
|
||||||
|
"""測試基本的放入和取出"""
|
||||||
|
# 測試放入
|
||||||
|
self.assertTrue(self.buffer.put("item1"))
|
||||||
|
self.assertTrue(self.buffer.put("item2"))
|
||||||
|
self.assertEqual(self.buffer.size(), 2)
|
||||||
|
self.assertFalse(self.buffer.is_empty())
|
||||||
|
|
||||||
def consumer(buffer, max_items, interval=0.05):
|
# 測試取出
|
||||||
"""消費者:從緩衝區讀取資料"""
|
item1 = self.buffer.get()
|
||||||
print(f"Consumer started (thread {threading.get_ident()})")
|
self.assertEqual(item1, "item1")
|
||||||
items_consumed = 0
|
self.assertEqual(self.buffer.size(), 1)
|
||||||
|
|
||||||
while items_consumed < max_items:
|
item2 = self.buffer.get()
|
||||||
# 嘗試讀取數據
|
self.assertEqual(item2, "item2")
|
||||||
item = buffer.get()
|
self.assertTrue(self.buffer.is_empty())
|
||||||
if item:
|
|
||||||
print(f"Consumed: {item}, buffer size: {buffer.size()}")
|
|
||||||
items_consumed += 1
|
|
||||||
else:
|
|
||||||
print(f"Buffer empty, consumer waiting... (thread {threading.get_ident()})")
|
|
||||||
|
|
||||||
time.sleep(interval) # 模擬消費過程
|
# 空緩衝區取出應返回 None
|
||||||
|
self.assertIsNone(self.buffer.get())
|
||||||
|
|
||||||
print(f"Consumer finished (thread {threading.get_ident()})")
|
def test_capacity_overflow(self):
|
||||||
|
"""測試緩衝區容量限制"""
|
||||||
|
# 填滿緩衝區 (容量-1,因為需要預留一個位置)
|
||||||
|
for i in range(7): # 8-1=7
|
||||||
|
self.assertTrue(self.buffer.put(f"item{i}"))
|
||||||
|
|
||||||
def batch_consumer(buffer, interval=0.2):
|
self.assertTrue(self.buffer.is_full())
|
||||||
"""批量消費者:一次性讀取緩衝區中的所有資料"""
|
|
||||||
print(f"Batch consumer started (thread {threading.get_ident()})")
|
|
||||||
|
|
||||||
for _ in range(5): # 執行5次批量讀取
|
# 嘗試再放入一個應該失敗
|
||||||
time.sleep(interval) # 等待緩衝區積累數據
|
self.assertFalse(self.buffer.put("overflow"))
|
||||||
items = buffer.get_all()
|
self.assertEqual(self.buffer.overflow_count.value, 1)
|
||||||
if items:
|
|
||||||
print(f"Batch consumed {len(items)} items: {items}")
|
|
||||||
else:
|
|
||||||
print("Buffer empty for batch consumer")
|
|
||||||
|
|
||||||
print(f"Batch consumer finished (thread {threading.get_ident()})")
|
def test_get_all(self):
|
||||||
|
"""測試取出所有項目"""
|
||||||
|
items = ["a", "b", "c", "d"]
|
||||||
|
for item in items:
|
||||||
|
self.buffer.put(item)
|
||||||
|
|
||||||
def demonstrate_multi_writer():
|
all_items = self.buffer.get_all()
|
||||||
"""示範多個寫入執行緒同時操作緩衝區"""
|
self.assertEqual(all_items, items)
|
||||||
print("\n=== Demonstrating Multiple Writers ===")
|
self.assertTrue(self.buffer.is_empty())
|
||||||
buffer = RingBuffer(capacity=80)
|
|
||||||
|
|
||||||
# 創建多個生產者執行緒
|
def test_clear(self):
|
||||||
threads = []
|
"""測試清空緩衝區"""
|
||||||
for i in range(3):
|
|
||||||
thread = threading.Thread(target=producer, args=(buffer, 5, 0.1 * (i+1)))
|
|
||||||
threads.append(thread)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
# 等待所有執行緒完成
|
|
||||||
for thread in threads:
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
buffer.print_stats() # 印出統計資訊
|
|
||||||
|
|
||||||
# 讀出所有剩餘資料
|
|
||||||
remaining = buffer.get_all()
|
|
||||||
print(f"Remaining items in buffer after multiple writers: {remaining}")
|
|
||||||
|
|
||||||
def demonstrate_basic_usage():
|
|
||||||
"""示範基本使用方式"""
|
|
||||||
print("\n=== Demonstrating Basic Usage ===")
|
|
||||||
# 創建緩衝區
|
|
||||||
buffer = RingBuffer(capacity=20, buffer_id=7)
|
|
||||||
|
|
||||||
# 檢查初始狀態
|
|
||||||
print(f"Initial buffer state - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
|
|
||||||
|
|
||||||
# 添加幾個項目
|
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
buffer.put(f"Test-{i}")
|
self.buffer.put(f"item{i}")
|
||||||
|
|
||||||
# 檢查狀態
|
self.buffer.clear()
|
||||||
print(f"After adding 5 items - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
|
self.assertTrue(self.buffer.is_empty())
|
||||||
|
self.assertEqual(self.buffer.size(), 0)
|
||||||
|
|
||||||
# 讀取一個項目
|
|
||||||
item = buffer.get()
|
|
||||||
print(f"Read item: {item}")
|
|
||||||
print(f"After reading 1 item - Content Size: {buffer.size()}")
|
|
||||||
|
|
||||||
# 添加更多項目直到滿
|
class TestRingBufferThreadSafety(unittest.TestCase):
|
||||||
items_added = 0
|
"""測試 RingBuffer 的線程安全性"""
|
||||||
while not buffer.is_full():
|
|
||||||
buffer.put(f"Fill-{items_added}")
|
|
||||||
items_added += 1
|
|
||||||
|
|
||||||
print(f"Added {items_added} more items until full")
|
def setUp(self):
|
||||||
print(f"Buffer full state - Empty: {buffer.is_empty()}, Full: {buffer.is_full()}, Content Size: {buffer.size()}")
|
"""每個測試前的準備"""
|
||||||
|
self.buffer = RingBuffer(capacity=256)
|
||||||
|
self.results = []
|
||||||
|
self.write_count = 1000
|
||||||
|
self.read_count = 1000
|
||||||
|
|
||||||
# 嘗試添加到已滿的緩衝區
|
def test_concurrent_producers_consumers(self):
|
||||||
result = buffer.put("Overflow")
|
"""測試多生產者多消費者場景"""
|
||||||
print(f"Attempt to add to full buffer: {'Succeeded' if result else 'Failed'}")
|
self.results = []
|
||||||
|
stats = self.buffer.get_stats()
|
||||||
|
self.assertEqual(stats['total_writes'], 0)
|
||||||
|
|
||||||
# 獲取所有項目
|
def producer(producer_id, count):
|
||||||
all_items = buffer.get_all()
|
"""生產者函數"""
|
||||||
print(f"All items in buffer: {all_items}")
|
for i in range(count):
|
||||||
print(f"Buffer after get_all() - Empty: {buffer.is_empty()}, Content Size: {buffer.size()}")
|
item = f"producer_{producer_id}_item_{i}"
|
||||||
|
while not self.buffer.put(item):
|
||||||
|
time.sleep(0.001) # 緩衝區滿時稍微等待
|
||||||
|
|
||||||
# 印出統計資訊
|
def consumer(consumer_id, count):
|
||||||
buffer.print_stats()
|
"""消費者函數"""
|
||||||
|
items = []
|
||||||
|
for _ in range(count):
|
||||||
|
item = None
|
||||||
|
while item is None:
|
||||||
|
item = self.buffer.get()
|
||||||
|
if item is None:
|
||||||
|
time.sleep(0.001) # 緩衝區空時稍微等待
|
||||||
|
items.append(item)
|
||||||
|
self.results.extend(items)
|
||||||
|
|
||||||
def demonstrate_producer_consumer():
|
# 創建多個生產者和消費者
|
||||||
"""示範生產者-消費者模式"""
|
with ThreadPoolExecutor(max_workers=8) as executor:
|
||||||
print("\n=== Demonstrating Producer-Consumer Pattern ===")
|
# 2 個生產者,每個寫入 500 個項目
|
||||||
|
producer_futures = [
|
||||||
|
executor.submit(producer, 0, 500),
|
||||||
|
executor.submit(producer, 1, 500)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 2 個消費者,每個讀取 500 個項目
|
||||||
|
consumer_futures = [
|
||||||
|
executor.submit(consumer, 0, 500),
|
||||||
|
executor.submit(consumer, 1, 500)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 等待所有任務完成
|
||||||
|
for future in producer_futures + consumer_futures:
|
||||||
|
future.result()
|
||||||
|
|
||||||
|
# 驗證結果
|
||||||
|
self.assertEqual(len(self.results), 1000)
|
||||||
|
self.assertTrue(self.buffer.is_empty())
|
||||||
|
|
||||||
|
# 檢查統計數據
|
||||||
|
stats = self.buffer.get_stats()
|
||||||
|
self.assertEqual(stats['total_writes'], 1000)
|
||||||
|
self.assertGreater(stats['total_reads'], 1000) # 包含失敗的讀取嘗試
|
||||||
|
self.assertGreater(stats['write_threads'], 1)
|
||||||
|
self.assertGreater(stats['read_threads'], 1)
|
||||||
|
|
||||||
|
def test_high_throughput(self):
|
||||||
|
"""測試高吞吐量場景"""
|
||||||
|
items_per_thread = 10000
|
||||||
|
num_threads = 4
|
||||||
|
|
||||||
|
def writer():
|
||||||
|
for i in range(items_per_thread):
|
||||||
|
while not self.buffer.put(i):
|
||||||
|
pass # 忙等待
|
||||||
|
|
||||||
|
def reader():
|
||||||
|
items = []
|
||||||
|
for _ in range(items_per_thread):
|
||||||
|
item = None
|
||||||
|
while item is None:
|
||||||
|
item = self.buffer.get()
|
||||||
|
items.append(item)
|
||||||
|
self.results.extend(items)
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=num_threads * 2) as executor:
|
||||||
|
# 啟動寫入線程
|
||||||
|
writer_futures = [executor.submit(writer) for _ in range(num_threads)]
|
||||||
|
|
||||||
|
# 啟動讀取線程
|
||||||
|
reader_futures = [executor.submit(reader) for _ in range(num_threads)]
|
||||||
|
|
||||||
|
# 等待完成
|
||||||
|
for future in writer_futures + reader_futures:
|
||||||
|
future.result()
|
||||||
|
|
||||||
|
end_time = time.time()
|
||||||
|
|
||||||
|
# 驗證結果
|
||||||
|
total_items = items_per_thread * num_threads
|
||||||
|
self.assertEqual(len(self.results), total_items)
|
||||||
|
|
||||||
|
# 性能統計
|
||||||
|
duration = end_time - start_time
|
||||||
|
throughput = total_items / duration
|
||||||
|
|
||||||
|
print(f"\nHigh Throughput Test Results:")
|
||||||
|
print(f"Total items: {total_items}")
|
||||||
|
print(f"Duration: {duration:.2f}s")
|
||||||
|
print(f"Throughput: {throughput:.0f} items/sec")
|
||||||
|
|
||||||
|
# 顯示詳細統計
|
||||||
|
self.buffer.print_stats()
|
||||||
|
|
||||||
|
|
||||||
|
class TestRingBufferStatistics(unittest.TestCase):
|
||||||
|
"""測試 RingBuffer 的統計功能"""
|
||||||
|
|
||||||
|
def test_statistics_tracking(self):
|
||||||
|
"""測試統計數據追蹤"""
|
||||||
buffer = RingBuffer(capacity=16)
|
buffer = RingBuffer(capacity=16)
|
||||||
|
|
||||||
# 創建生產者和消費者執行緒
|
# 寫入一些數據
|
||||||
producer_thread = threading.Thread(target=producer, args=(buffer, 20, 0.1))
|
for i in range(10):
|
||||||
consumer_thread = threading.Thread(target=consumer, args=(buffer, 3, 0.2))
|
buffer.put(f"item{i}")
|
||||||
batch_thread = threading.Thread(target=batch_consumer, args=(buffer, 0.5))
|
|
||||||
|
|
||||||
# 啟動執行緒
|
# 讀取一些數據
|
||||||
producer_thread.start()
|
for _ in range(5):
|
||||||
consumer_thread.start()
|
buffer.get()
|
||||||
batch_thread.start()
|
|
||||||
|
|
||||||
# 等待執行緒完成
|
stats = buffer.get_stats()
|
||||||
producer_thread.join()
|
|
||||||
consumer_thread.join()
|
|
||||||
batch_thread.join()
|
|
||||||
|
|
||||||
# 檢查最終狀態
|
# 驗證基本統計
|
||||||
print(f"Final buffer state - Empty: {buffer.is_empty()}, Size: {buffer.size()}")
|
self.assertEqual(stats['total_writes'], 10)
|
||||||
|
self.assertEqual(stats['total_reads'], 5)
|
||||||
|
self.assertEqual(stats['current_size'], 5)
|
||||||
|
self.assertEqual(stats['write_threads'], 1)
|
||||||
|
self.assertEqual(stats['read_threads'], 1)
|
||||||
|
|
||||||
|
def test_reset_statistics(self):
|
||||||
|
"""測試重置統計數據"""
|
||||||
|
buffer = RingBuffer(capacity=16)
|
||||||
|
|
||||||
|
# 產生一些活動
|
||||||
|
for i in range(5):
|
||||||
|
buffer.put(f"item{i}")
|
||||||
|
for _ in range(3):
|
||||||
|
buffer.get()
|
||||||
|
|
||||||
|
# 重置統計
|
||||||
|
buffer.reset_stats()
|
||||||
|
|
||||||
|
stats = buffer.get_stats()
|
||||||
|
self.assertEqual(stats['total_writes'], 0)
|
||||||
|
self.assertEqual(stats['total_reads'], 0)
|
||||||
|
self.assertEqual(stats['concurrent_writes'], 0)
|
||||||
|
self.assertEqual(stats['concurrent_reads'], 0)
|
||||||
|
self.assertEqual(stats['overflow_count'], 0)
|
||||||
|
|
||||||
|
|
||||||
|
def benchmark_ringbuffer():
|
||||||
|
"""RingBuffer 性能基準測試"""
|
||||||
|
print("\n=== RingBuffer Performance Benchmark ===")
|
||||||
|
|
||||||
|
buffer = RingBuffer(capacity=1024)
|
||||||
|
num_operations = 100000
|
||||||
|
|
||||||
|
# 單線程性能測試
|
||||||
|
start_time = time.time()
|
||||||
|
for i in range(num_operations):
|
||||||
|
buffer.put(i)
|
||||||
|
for _ in range(num_operations):
|
||||||
|
buffer.get()
|
||||||
|
end_time = time.time()
|
||||||
|
|
||||||
|
single_thread_time = end_time - start_time
|
||||||
|
throughput = (num_operations * 2) / single_thread_time
|
||||||
|
|
||||||
|
print(f"Single Thread: {throughput:.0f} ops/sec")
|
||||||
|
|
||||||
|
# 多線程性能測試
|
||||||
|
buffer = RingBuffer(capacity=1024)
|
||||||
|
|
||||||
|
def producer():
|
||||||
|
for i in range(num_operations // 2):
|
||||||
|
while not buffer.put(i):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def consumer():
|
||||||
|
for _ in range(num_operations // 2):
|
||||||
|
while buffer.get() is None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||||
|
future1 = executor.submit(producer)
|
||||||
|
future2 = executor.submit(consumer)
|
||||||
|
future1.result()
|
||||||
|
future2.result()
|
||||||
|
|
||||||
|
end_time = time.time()
|
||||||
|
|
||||||
|
multi_thread_time = end_time - start_time
|
||||||
|
throughput = num_operations / multi_thread_time
|
||||||
|
|
||||||
|
print(f"Multi Thread: {throughput:.0f} ops/sec")
|
||||||
|
print(f"Speedup: {single_thread_time/multi_thread_time:.2f}x")
|
||||||
|
|
||||||
buffer.print_stats()
|
buffer.print_stats()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# 展示各種使用場景
|
|
||||||
# demonstrate_basic_usage()
|
|
||||||
# demonstrate_producer_consumer()
|
|
||||||
demonstrate_multi_writer()
|
|
||||||
|
|
||||||
print("\nAll demonstrations completed!")
|
if __name__ == "__main__":
|
||||||
|
# 運行單元測試
|
||||||
|
unittest.main(argv=[''], exit=False, verbosity=2)
|
||||||
|
|
||||||
|
# 運行性能基準測試
|
||||||
|
benchmark_ringbuffer()
|
||||||
Reference in New Issue
Block a user