新增复合管控器(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:
zjk
2026-08-26 22:43:10 +08:00
parent e0488d513b
commit 54c32a180a
79 changed files with 8731 additions and 1398 deletions
+117
View File
@@ -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)