新增复合管控器(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 补充编译产物/临时文件忽略
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user