first commit

This commit is contained in:
2026-06-16 15:40:19 +08:00
commit 8ba6324244
97 changed files with 6216 additions and 0 deletions

View 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("所有队列处理完成,线程已停止")
'''

View 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设2000UART设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) # 默认优先级51最高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("所有队列处理完成,线程已停止")
'''