Files
H100PowerManger/test/disSysTest/app/udp_comm.py
T
zjk 54c32a180a 新增复合管控器(pCCU) + 惯导数据解析 + 设备控制页增强 + 0824协议文档归档
- 新增 pCCU 复合管控器程序(src/pCCU):FC/PM 双链路(UDP)、协议编解码器
  (FcProtocol/PmProtocol/Frame/Message/MessageRegistry)、快照与落库(DbStore)、
  内置 WebServer;挂入 src/CMakeLists.txt 与板卡 mission(h100.moos)
- 惯导/深度数据:新增 uDevice_insData 消息解析,接入 UpperCommManager,
  pPowerMangerHost 注册监视,UpmsgTest 覆盖
- pPowerMangerHost 设备控制页增强(web/index.html)
- 部署/运维脚本改为 4 个 systemd 服务(moosdb/pPowerManger/pPowerMangerHost/pCCU):
  deploy/clean-data/fetch-data 同步适配
- 协议文档:归档 0824 控制主机与复合管控器通信协议 xlsx 及关联 docx,移除旧 md
- 测试:新增 pccu 单元/集成测试与 disSysTest Python 配电仿真
- 清理:移除未使用的 CCU 状态调试打印 coutMsg;.gitignore 补充编译产物/临时文件忽略
2026-08-26 22:43:10 +08:00

118 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""UDP 通信层:socket 管理、收发线程、按消息 ID 分发。
线程安全:start/stop/reconfigure 可随时调用;
收包在独立线程解析,解析成功后回调 on_feedback / on_command,
原始帧经 on_raw 回调(用于转发/透传)。
"""
import socket
import threading
import time
from . import protocol
class UdpComm:
def __init__(self, logger=None):
self.logger = logger
self.sock = None
self.running = False
self._thread = None
self._lock = threading.Lock()
# 回调
self.on_feedback = None # callable(msg, addr)
self.on_command = None # callable(msg, addr)
self.on_raw = None # callable(data, addr)
self.on_unknown = None # callable(data, addr) 解析失败/未知ID
# 统计
self.rx_count = 0
self.tx_count = 0
self.last_rx_time = None
def bind(self, ip, port):
"""创建并绑定监听 socket(带超时 + 地址复用)。失败抛 OSError。"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.settimeout(0.1)
s.bind((ip, int(port)))
with self._lock:
old = self.sock
self.sock = s
if old:
old.close()
return self
@property
def bound(self):
return self.sock is not None
def start(self):
if self.running:
return
if self.sock is None:
raise RuntimeError("socket 未绑定")
self.running = True
self._thread = threading.Thread(target=self._recv_loop, daemon=True,
name="udp-recv")
self._thread.start()
def stop(self):
self.running = False
t = self._thread
self._thread = None
if t:
t.join(timeout=1.5)
def _recv_loop(self):
while self.running:
try:
data, addr = self.sock.recvfrom(2048)
except socket.timeout:
continue
except OSError:
break
self.rx_count += 1
self.last_rx_time = time.time()
try:
self._dispatch(data, addr)
except Exception as e:
if self.logger:
self.logger.error(f"分发消息异常: {e}")
def _dispatch(self, data, addr):
if self.on_raw:
self.on_raw(data, addr)
msg_id = protocol.msg_id_of(data)
cls = protocol.ALL_MESSAGES.get(msg_id)
msg = None
if cls:
msg = cls()
if not msg.unpack(data):
msg = None
if msg is None:
if self.on_unknown:
self.on_unknown(data, addr)
return
if msg_id in protocol.CMD_MESSAGES:
if self.on_command:
self.on_command(msg, addr)
else:
if self.on_feedback:
self.on_feedback(msg, addr)
def sendto(self, data, ip, port):
"""发送原始字节。返回 True/False。"""
if self.sock is None:
return False
try:
self.sock.sendto(data, (ip, int(port)))
self.tx_count += 1
return True
except OSError as e:
if self.logger:
self.logger.error(f"发送到 {ip}:{port} 失败: {e}")
return False
def send_message(self, msg, ip, port):
return self.sendto(msg.pack(), ip, port)