Files
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

66 lines
2.1 KiB
Python

"""日志模块:UI 文本控件 + 控制台/文件输出,线程安全。
UI 更新不直接在任意线程操作 Tk 控件,而是写入 queue;
主线程通过 drain() 周期性取走并写入文本框(见 app._refresh_ui)。
"""
import os
import queue
import threading
import time
class AppLogger:
def __init__(self, widget=None, file_path=None, max_lines=200,
console=True):
self.widget = widget
self.file_path = file_path
self.max_lines = max_lines
self.console = console
self._lock = threading.Lock()
self._queue = queue.Queue()
if file_path:
os.makedirs(os.path.dirname(os.path.abspath(file_path)), exist_ok=True)
def _format(self, level, msg):
return f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] [{level}] {msg}"
def info(self, msg):
self._write("INFO", msg)
def warning(self, msg):
self._write("WARN", msg)
def error(self, msg):
self._write("ERROR", msg)
def _write(self, level, msg):
line = self._format(level, msg)
self._queue.put(line)
with self._lock:
if self.console:
print(line)
if self.file_path:
try:
with open(self.file_path, "a", encoding="utf-8") as f:
f.write(line + "\n")
except OSError:
pass
def drain(self):
"""主线程调用:把队列中的日志写入 UI 文本框。"""
if self.widget is None or self._queue.empty():
return
try:
self.widget.config(state="normal")
while not self._queue.empty():
self.widget.insert("end", self._queue.get_nowait() + "\n")
count = int(self.widget.index("end-1c").split(".")[0])
if count > self.max_lines:
self.widget.delete("1.0", f"{count - self.max_lines}.0")
self.widget.see("end")
self.widget.config(state="disabled")
except Exception:
# 窗口销毁等异常静默
pass