first commit
This commit is contained in:
Binary file not shown.
203
midware/core/history/bus_udp_decoupler_20260306.py
Normal file
203
midware/core/history/bus_udp_decoupler_20260306.py
Normal file
@@ -0,0 +1,203 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tan Mingyan 2026/3/6
|
||||
|
||||
四、关键设计亮点(解耦 + 队列的核心价值)
|
||||
彻底解耦:
|
||||
UDP 收发 ≠ 总线处理:UDP 线程只做「数据搬运」,总线线程只做「业务处理」,修改总线逻辑无需改动 UDP 代码;
|
||||
异常隔离:某一线程(如 CAN 处理)崩溃,不影响 UDP 收发和其他总线线程运行。
|
||||
队列缓冲区的核心作用:
|
||||
削峰填谷:UDP 突发大量数据时,队列暂存,总线线程按能力消费,避免数据丢失;
|
||||
异步通信:线程间通过队列通信,无直接调用,消除阻塞依赖;
|
||||
可监控:通过 qsize() 监控队列长度,及时发现「生产 > 消费」的瓶颈(如队列持续满则需优化总线处理速度)。
|
||||
工业级容错:
|
||||
队列满时丢弃数据 + 告警,避免内存溢出;
|
||||
线程异常时休眠重试,避免 CPU 100%;
|
||||
守护线程(daemon=True):主线程退出时自动终止子线程,避免僵尸线程;
|
||||
优雅退出:join() 等待队列处理完成,避免强制退出导致数据丢失。
|
||||
五、扩展建议(根据业务需求调整)
|
||||
队列持久化:若需断电不丢数据,可将队列数据写入本地文件 / Redis;
|
||||
优先级队列:对关键设备(如 1553B)使用 queue.PriorityQueue,优先处理高优先级数据;
|
||||
流量控制:监控队列长度,超过阈值时暂停 UDP 接收(或降低接收频率);
|
||||
线程池优化:若总线协议过多,改用 concurrent.futures.ThreadPoolExecutor 管理线程,避免线程数量过多。
|
||||
"""
|
||||
"""
|
||||
总线-UDP解耦核心模块:队列+多线程
|
||||
"""
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
from loguru import logger
|
||||
from typing import Dict, Any, List
|
||||
|
||||
# ========== 1. 全局队列定义(按协议分类) ==========
|
||||
# 队列最大长度:避免内存溢出,根据业务调整(如1000)
|
||||
MAX_QUEUE_SIZE = 1000
|
||||
|
||||
# 协议分发队列(UDP接收→协议分流)
|
||||
protocol_dispatch_queue = queue.Queue(maxsize=MAX_QUEUE_SIZE)
|
||||
|
||||
# 各总线处理队列
|
||||
bus_queues = {
|
||||
"UART": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"CAN": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"1553B": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"AD": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"OC": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"网络": queue.Queue(maxsize=MAX_QUEUE_SIZE)
|
||||
}
|
||||
|
||||
# UDP发送队列(总线回传→UDP发送)
|
||||
udp_send_queue = queue.Queue(maxsize=MAX_QUEUE_SIZE)
|
||||
|
||||
# ========== 2. UDP接收线程(仅负责收数据,不处理) ==========
|
||||
def udp_receive_thread(udp_server):
|
||||
"""UDP接收线程:独立运行,仅收数据放入分发队列"""
|
||||
logger.info("UDP接收线程启动")
|
||||
while True:
|
||||
try:
|
||||
recv_data = udp_server.recv_multi_udp_hex()
|
||||
if recv_data:
|
||||
for data in recv_data:
|
||||
# 非阻塞放入队列,避免线程阻塞
|
||||
try:
|
||||
protocol_dispatch_queue.put_nowait(data)
|
||||
logger.debug(f"UDP接收:{data['dev_name']} 数据放入分发队列,队列当前长度:{protocol_dispatch_queue.qsize()}")
|
||||
except queue.Full:
|
||||
logger.warning(f"协议分发队列已满,丢弃 {data['dev_name']} 数据")
|
||||
time.sleep(1e-6)
|
||||
except Exception as e:
|
||||
logger.error(f"UDP接收线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1) # 异常时休眠,避免CPU飙高
|
||||
|
||||
# ========== 3. 协议分发线程(仅负责分流,不处理业务) ==========
|
||||
def protocol_dispatch_thread():
|
||||
"""协议分发线程:从分发队列取数据,按protocol分流到对应总线队列"""
|
||||
logger.info("协议分发线程启动")
|
||||
while True:
|
||||
try:
|
||||
# 阻塞取数据(队列为空时等待,无CPU消耗)
|
||||
data = protocol_dispatch_queue.get(timeout=1)
|
||||
protocol = data.get("protocol", "").strip().upper()
|
||||
|
||||
# 分流到对应总线队列
|
||||
if protocol in bus_queues:
|
||||
try:
|
||||
bus_queues[protocol].put_nowait(data)
|
||||
logger.debug(f"分发:{data['dev_name']} 数据到 {protocol} 队列,队列长度:{bus_queues[protocol].qsize()}")
|
||||
except queue.Full:
|
||||
logger.warning(f"{protocol} 队列已满,丢弃 {data['dev_name']} 数据")
|
||||
else:
|
||||
logger.warning(f"未知协议 {protocol},丢弃 {data['dev_name']} 数据")
|
||||
|
||||
# 标记任务完成(队列join时需要)
|
||||
protocol_dispatch_queue.task_done()
|
||||
except queue.Empty:
|
||||
continue # 队列为空,继续循环
|
||||
except Exception as e:
|
||||
logger.error(f"协议分发线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 4. 总线处理线程(以UART为例,其他协议同理) ==========
|
||||
def bus_process_thread(protocol: str, udp_server):
|
||||
"""通用总线处理线程:从对应队列取数据,处理后放入UDP发送队列"""
|
||||
logger.info(f"{protocol} 总线处理线程启动")
|
||||
while True:
|
||||
try:
|
||||
# 阻塞取数据
|
||||
data = bus_queues[protocol].get(timeout=1)
|
||||
dev_name = data["dev_name"]
|
||||
raw_data = data["raw_data"]
|
||||
|
||||
# ========== 核心:总线业务处理(解耦后仅在此处修改) ==========
|
||||
logger.info(f"{protocol} 处理:{dev_name} 原始数据:{raw_data.hex()}")
|
||||
# 1. 总线数据解析(如UART波特率解析、CAN帧解析等)
|
||||
processed_data = raw_data # 示例:实际需替换为业务逻辑
|
||||
# 2. 总线数据发送(如写入UART串口、发送CAN帧等)
|
||||
# bus_driver.send(protocol, processed_data) # 总线驱动调用
|
||||
|
||||
# ========== 处理完成后,回传数据到UDP发送队列 ==========
|
||||
try:
|
||||
udp_send_queue.put_nowait({
|
||||
"dev_name": dev_name,
|
||||
"data": processed_data,
|
||||
"protocol": protocol
|
||||
})
|
||||
logger.debug(f"{protocol} 处理完成,{dev_name} 回传数据放入UDP发送队列")
|
||||
except queue.Full:
|
||||
logger.warning(f"UDP发送队列已满,丢弃 {dev_name} 回传数据")
|
||||
|
||||
# 标记任务完成
|
||||
bus_queues[protocol].task_done()
|
||||
except queue.Empty:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"{protocol} 总线线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 5. UDP发送线程(仅负责发数据,不处理) ==========
|
||||
def udp_send_thread(udp_server):
|
||||
"""UDP发送线程:从发送队列取数据,发送到故障注入平台"""
|
||||
logger.info("UDP发送线程启动")
|
||||
while True:
|
||||
try:
|
||||
# 阻塞取数据
|
||||
send_data = udp_send_queue.get(timeout=1)
|
||||
dev_name = send_data["dev_name"]
|
||||
data = send_data["data"]
|
||||
|
||||
# 调用UDP服务发送
|
||||
udp_server.send_to_fault(data, dev_name)
|
||||
logger.debug(f"UDP发送:{dev_name} 数据到故障注入平台,队列剩余:{udp_send_queue.qsize()}")
|
||||
|
||||
# 标记任务完成
|
||||
udp_send_queue.task_done()
|
||||
except queue.Empty:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"UDP发送线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 6. 线程启动/停止管理(集成到main.py) ==========
|
||||
'''
|
||||
def start_all_threads(udp_server):
|
||||
"""启动所有解耦线程"""
|
||||
threads = []
|
||||
|
||||
# 1. UDP接收线程
|
||||
udp_recv_t = threading.Thread(target=udp_receive_thread, args=(udp_server,), daemon=True)
|
||||
threads.append(udp_recv_t)
|
||||
|
||||
# 2. 协议分发线程
|
||||
dispatch_t = threading.Thread(target=protocol_dispatch_thread, daemon=True)
|
||||
threads.append(dispatch_t)
|
||||
|
||||
# 3. 各总线处理线程
|
||||
for protocol in bus_queues.keys():
|
||||
bus_t = threading.Thread(target=bus_process_thread, args=(protocol, udp_server), daemon=True)
|
||||
threads.append(bus_t)
|
||||
|
||||
# 4. UDP发送线程
|
||||
udp_send_t = threading.Thread(target=udp_send_thread, args=(udp_server,), daemon=True)
|
||||
threads.append(udp_send_t)
|
||||
|
||||
# 启动所有线程
|
||||
for t in threads:
|
||||
t.start()
|
||||
logger.info(f"线程 {t.name} 启动成功")
|
||||
|
||||
return threads
|
||||
|
||||
def stop_all_threads():
|
||||
"""优雅停止所有线程(等待队列处理完成)"""
|
||||
logger.info("开始停止所有线程,等待队列处理完成...")
|
||||
|
||||
# 等待队列所有任务完成
|
||||
protocol_dispatch_queue.join()
|
||||
for q in bus_queues.values():
|
||||
q.join()
|
||||
udp_send_queue.join()
|
||||
|
||||
logger.info("所有队列处理完成,线程已停止")
|
||||
'''
|
||||
234
midware/core/history/bus_udp_decoupler_20260307.py
Normal file
234
midware/core/history/bus_udp_decoupler_20260307.py
Normal file
@@ -0,0 +1,234 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tan Mingyan 2026/3/7
|
||||
"""
|
||||
"""
|
||||
总线-UDP双向通信解耦模块(加入收发缓冲区分离)
|
||||
"""
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
from loguru import logger
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# ========== 1. 收发队列分离:为每个协议定义独立的收/发队列 ==========
|
||||
MAX_QUEUE_SIZE = 1000 # 可按协议单独调整(如1553B设2000,UART设500)
|
||||
|
||||
# 总线接收队列(总线→中间件:上行数据)
|
||||
bus_recv_queues = {
|
||||
"UART": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"CAN": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"1553B": queue.Queue(maxsize=2000), # 1553B数据量大,单独调整
|
||||
"AD": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"OC": queue.Queue(maxsize=MAX_QUEUE_SIZE),
|
||||
"网络": queue.Queue(maxsize=MAX_QUEUE_SIZE)
|
||||
}
|
||||
|
||||
# 总线发送队列(中间件→总线:下行数据)
|
||||
bus_send_queues = {
|
||||
"UART": queue.PriorityQueue(maxsize=MAX_QUEUE_SIZE), # 发送队列支持优先级
|
||||
"CAN": queue.PriorityQueue(maxsize=MAX_QUEUE_SIZE),
|
||||
"1553B": queue.PriorityQueue(maxsize=2000),
|
||||
"AD": queue.PriorityQueue(maxsize=MAX_QUEUE_SIZE),
|
||||
"OC": queue.PriorityQueue(maxsize=MAX_QUEUE_SIZE),
|
||||
"网络": queue.PriorityQueue(maxsize=MAX_QUEUE_SIZE)
|
||||
}
|
||||
|
||||
# UDP发送队列(中间件→UDP:上行数据,总线接收后转发)
|
||||
udp_send_queue = queue.Queue(maxsize=MAX_QUEUE_SIZE)
|
||||
# UDP接收队列(UDP→中间件:下行数据,转发到总线发送队列)
|
||||
udp_recv_dispatch_queue = queue.Queue(maxsize=MAX_QUEUE_SIZE)
|
||||
|
||||
# ========== 2. UDP接收线程:仅处理下行指令(转发到总线发送队列) ==========
|
||||
def udp_recv_thread(udp_server):
|
||||
"""UDP接收线程:接收故障注入平台的下行指令,分发到总线发送队列"""
|
||||
logger.info("UDP接收线程(下行指令)启动")
|
||||
while True:
|
||||
try:
|
||||
recv_data = udp_server.recv_multi_udp_hex()
|
||||
if recv_data:
|
||||
for data in recv_data:
|
||||
protocol = data.get("protocol", "").strip().upper()
|
||||
dev_name = data.get("dev_name", "")
|
||||
raw_data = data.get("raw_data", b"")
|
||||
priority = data.get("priority", 5) # 默认优先级5(1最高,10最低)
|
||||
|
||||
if protocol not in bus_send_queues:
|
||||
logger.warning(f"未知协议 {protocol},丢弃下行指令:{dev_name}")
|
||||
continue
|
||||
|
||||
# 放入总线发送队列(带优先级)
|
||||
try:
|
||||
# PriorityQueue格式:(优先级, 数据)
|
||||
bus_send_queues[protocol].put_nowait((priority, {
|
||||
"dev_name": dev_name,
|
||||
"raw_data": raw_data,
|
||||
"protocol": protocol
|
||||
}))
|
||||
logger.debug(f"下行指令:{dev_name} ({protocol}) 放入发送队列,优先级:{priority}")
|
||||
except queue.Full:
|
||||
logger.warning(f"{protocol} 发送队列已满,丢弃 {dev_name} 下行指令")
|
||||
time.sleep(1e-6)
|
||||
except Exception as e:
|
||||
logger.error(f"UDP接收线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 3. 总线发送线程:处理下行指令(中间件→总线) ==========
|
||||
def bus_send_thread(protocol: str, bus_driver: Optional[Any] = None):
|
||||
"""总线发送线程:从发送队列取下行指令,发送到总线"""
|
||||
logger.info(f"{protocol} 总线发送线程启动")
|
||||
while True:
|
||||
try:
|
||||
# PriorityQueue阻塞取数据(优先级高的先取)
|
||||
priority, send_data = bus_send_queues[protocol].get(timeout=1)
|
||||
dev_name = send_data["dev_name"]
|
||||
raw_data = send_data["raw_data"]
|
||||
|
||||
# 总线发送逻辑(适配双向通信:调用总线驱动发送指令)
|
||||
logger.info(f"{protocol} 发送:{dev_name} | 优先级:{priority} | 数据:{raw_data.hex()}")
|
||||
# 实际业务:调用总线驱动发送(如CAN发送帧、UART写串口)
|
||||
# if bus_driver:
|
||||
# bus_driver.send(raw_data)
|
||||
|
||||
# 标记任务完成
|
||||
bus_send_queues[protocol].task_done()
|
||||
|
||||
# 在bus_send_thread中添加超时检查
|
||||
if time.time() - send_data.get("send_time", 0) > 5: # 5秒超时
|
||||
logger.error(f"{protocol} 发送超时:{dev_name}")
|
||||
bus_send_queues[protocol].task_done()
|
||||
continue
|
||||
except queue.Empty:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"{protocol} 发送线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 4. 总线接收线程:处理上行数据(总线→中间件) ==========
|
||||
def bus_recv_thread(protocol: str, bus_driver: Optional[Any] = None):
|
||||
"""总线接收线程:从总线接收上行数据,放入接收队列"""
|
||||
logger.info(f"{protocol} 总线接收线程启动")
|
||||
while True:
|
||||
try:
|
||||
# 模拟总线接收(实际需替换为总线驱动的接收接口)
|
||||
# raw_data = bus_driver.recv() # 从总线读取上行数据
|
||||
# 此处为示例,实际需对接真实总线驱动
|
||||
raw_data = b"" # 占位:替换为总线接收逻辑
|
||||
|
||||
if raw_data:
|
||||
dev_name = f"{protocol}_DEV" # 实际从总线数据解析设备名
|
||||
# 放入总线接收队列
|
||||
try:
|
||||
bus_recv_queues[protocol].put_nowait({
|
||||
"dev_name": dev_name,
|
||||
"raw_data": raw_data,
|
||||
"protocol": protocol,
|
||||
"recv_time": time.time() # 记录接收时间,便于时序分析
|
||||
})
|
||||
logger.debug(f"{protocol} 接收:{dev_name} 上行数据,队列长度:{bus_recv_queues[protocol].qsize()}")
|
||||
except queue.Full:
|
||||
logger.warning(f"{protocol} 接收队列已满,丢弃上行数据")
|
||||
time.sleep(1e-6) # 匹配总线接收速率
|
||||
except Exception as e:
|
||||
logger.error(f"{protocol} 接收线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 5. 总线接收分发线程:汇总上行数据→UDP发送 ==========
|
||||
def bus_recv_dispatch_thread():
|
||||
"""总线接收分发线程:汇总各协议接收队列数据,转发到UDP发送队列"""
|
||||
logger.info("总线接收分发线程启动")
|
||||
while True:
|
||||
try:
|
||||
# 遍历所有总线接收队列,非阻塞取数据(避免单队列阻塞)
|
||||
for protocol, q in bus_recv_queues.items():
|
||||
try:
|
||||
recv_data = q.get_nowait()
|
||||
# 转发到UDP发送队列(发送到故障注入平台)
|
||||
try:
|
||||
udp_send_queue.put_nowait(recv_data)
|
||||
logger.debug(f"{protocol} 上行数据:{recv_data['dev_name']} 转发到UDP发送队列")
|
||||
except queue.Full:
|
||||
logger.warning(f"UDP发送队列已满,丢弃 {protocol} 上行数据")
|
||||
q.task_done()
|
||||
except queue.Empty:
|
||||
continue
|
||||
time.sleep(1e-6)
|
||||
except Exception as e:
|
||||
logger.error(f"总线接收分发线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# ========== 6. UDP发送线程:处理上行数据(中间件→UDP) ==========
|
||||
def udp_send_thread(udp_server):
|
||||
"""UDP发送线程:从UDP发送队列取上行数据,发送到故障注入平台"""
|
||||
logger.info("UDP发送线程(上行数据)启动")
|
||||
while True:
|
||||
try:
|
||||
send_data = udp_send_queue.get(timeout=1)
|
||||
dev_name = send_data["dev_name"]
|
||||
raw_data = send_data["raw_data"]
|
||||
|
||||
# 调用UDP服务发送到故障注入平台
|
||||
udp_server.send_to_fault(raw_data, dev_name)
|
||||
logger.debug(f"UDP发送:{dev_name} 上行数据,队列剩余:{udp_send_queue.qsize()}")
|
||||
udp_send_queue.task_done()
|
||||
except queue.Empty:
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"UDP发送线程异常:{str(e)}", exc_info=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
'''
|
||||
# ========== 7. 线程管理:启动/停止所有收发线程 ==========
|
||||
def start_all_threads(udp_server, bus_drivers: Dict[str, Any] = None):
|
||||
"""
|
||||
启动所有双向通信线程
|
||||
:param udp_server: UDP服务实例
|
||||
:param bus_drivers: 总线驱动字典,格式:{"UART": uart_driver, "CAN": can_driver, ...}
|
||||
"""
|
||||
threads = []
|
||||
bus_drivers = bus_drivers or {} # 无驱动时传空字典
|
||||
|
||||
# 1. UDP接收线程(下行指令)
|
||||
udp_recv_t = threading.Thread(target=udp_recv_thread, args=(udp_server,), daemon=True)
|
||||
threads.append(udp_recv_t)
|
||||
|
||||
# 2. 总线发送线程(每个协议一个)
|
||||
for protocol in bus_send_queues.keys():
|
||||
driver = bus_drivers.get(protocol)
|
||||
send_t = threading.Thread(target=bus_send_thread, args=(protocol, driver), daemon=True)
|
||||
threads.append(send_t)
|
||||
|
||||
# 3. 总线接收线程(每个协议一个)
|
||||
for protocol in bus_recv_queues.keys():
|
||||
driver = bus_drivers.get(protocol)
|
||||
recv_t = threading.Thread(target=bus_recv_thread, args=(protocol, driver), daemon=True)
|
||||
threads.append(recv_t)
|
||||
|
||||
# 4. 总线接收分发线程
|
||||
recv_dispatch_t = threading.Thread(target=bus_recv_dispatch_thread, daemon=True)
|
||||
threads.append(recv_dispatch_t)
|
||||
|
||||
# 5. UDP发送线程(上行数据)
|
||||
udp_send_t = threading.Thread(target=udp_send_thread, args=(udp_server,), daemon=True)
|
||||
threads.append(udp_send_t)
|
||||
|
||||
# 启动所有线程
|
||||
for t in threads:
|
||||
t.start()
|
||||
logger.info(f"线程 {t.name} 启动成功")
|
||||
return threads
|
||||
|
||||
def stop_all_threads():
|
||||
"""优雅停止所有线程,等待队列处理完成"""
|
||||
logger.info("开始停止所有线程,等待队列处理完成...")
|
||||
# 等待发送队列处理完成
|
||||
for q in bus_send_queues.values():
|
||||
q.join()
|
||||
# 等待接收队列处理完成
|
||||
for q in bus_recv_queues.values():
|
||||
q.join()
|
||||
# 等待UDP发送队列处理完成
|
||||
udp_send_queue.join()
|
||||
logger.info("所有队列处理完成,线程已停止")
|
||||
|
||||
'''
|
||||
Reference in New Issue
Block a user