using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.IO.Ports; using System.Threading; using UnityEngine; /// /// 发送帧数据结构 /// public struct SendFrameData { public byte Command; public byte[] Data; public string Description; public SendFrameData(byte command, byte[] data, string description = "") { Command = command; Data = data; Description = description; } } /// /// 串口通信服务实现 - 基于DCS项目串口通信协议 /// public class SerialCommunicationService : ISerialCommunicationService { // 协议常量 private const byte FRAME_HEADER_1 = 0xAA; private const byte FRAME_HEADER_2 = 0x55; private const byte FRAME_TAIL_1 = 0x0D; private const byte FRAME_TAIL_2 = 0x0A; // 命令字定义 private const byte CMD_HANDSHAKE_RESPONSE = 0x01; private const byte CMD_HANDSHAKE_REQUEST = 0x02; private const byte CMD_HEARTBEAT_REPORT = 0x03; private const byte CMD_HEARTBEAT_RESPONSE = 0x04; private const byte CMD_ALARM_CONTROL = 0x05; private const byte CMD_DATA_REPORT = 0x06; private const byte CMD_PARAM_QUERY_SET = 0x07; private const byte CMD_ALARM_STATUS_FEEDBACK = 0x08; private const byte CMD_TIME_SYNC = 0x09; // 事件定义 public event Action OnHandshakeCompleted; public event Action OnDeviceStatusReceived; // public event Action OnDeviceDataReceived; // public event Action OnAlarmStatusReceived; public event Action OnCommunicationError; // 属性 public bool IsConnected => _serialPort?.IsOpen ?? false; public bool IsHandshakeCompleted { get; private set; } // 私有字段 private SerialPort _serialPort; private Thread _readThread; private volatile bool _keepReading; private readonly ConcurrentQueue _receiveQueue = new ConcurrentQueue(); private readonly object _sendLock = new object(); // 接收队列大小限制,防止内存积压 private const int MAX_RECEIVE_QUEUE_SIZE = 50; // 发送队列相关 private readonly ConcurrentQueue _sendQueue = new ConcurrentQueue(); private DateTime _lastSendTime = DateTime.MinValue; private const int SEND_INTERVAL_MS = 50; // 帧间间隔50ms(已优化) // 队列大小限制,防止内存积压 private const int MAX_SEND_QUEUE_SIZE = 10; // 心跳应答限制,防止频繁响应 private DateTime _lastHeartbeatResponseTime = DateTime.MinValue; private const int HEARTBEAT_RESPONSE_INTERVAL_MS = 200; // 心跳应答最小间隔 // 协议状态 private byte _firmwareVersion = 0x01; private DateTime _lastHeartbeat = DateTime.Now; // private readonly Timer _heartbeatTimer; // 内存管理 private DateTime _lastMemoryCleanup = DateTime.Now; private const int MEMORY_CLEANUP_INTERVAL_MINUTES = 30; // 30分钟清理一次 // 安卓平台日志优化 private bool _enableVerboseLogging = false; // 安卓板上禁用详细日志 private DateTime _lastLogTime = DateTime.MinValue; private const int LOG_THROTTLE_MS = 1000; // 日志限制:1秒最多1条 private const float MIN_VALID_BFI = 0f; private const float MAX_VALID_BFI = 1000f; private float _lastValidBFI = 100f; public SerialCommunicationService() { // 创建心跳检测定时器 (每5秒检查一次) // _heartbeatTimer = new Timer(CheckHeartbeat, null, 5000, 5000); // 检测安卓平台并优化日志 #if UNITY_ANDROID && !UNITY_EDITOR _enableVerboseLogging = false; // 安卓板上禁用详细日志 #else _enableVerboseLogging = true; // PC上保持详细日志 #endif OptimizedLog("[串口服务] 初始化完成,版本: v2.1 (安卓内存优化版)"); OptimizedLog($"[串口服务] 队列限制 - 发送: {MAX_SEND_QUEUE_SIZE}, 接收: {MAX_RECEIVE_QUEUE_SIZE}"); OptimizedLog($"[串口服务] 详细日志: {(_enableVerboseLogging ? "开启" : "关闭")}"); } /// /// 优化的日志输出,减少安卓板内存压力 /// private void OptimizedLog(string message, bool forceLog = false) { if (!_enableVerboseLogging && !forceLog) return; var now = DateTime.Now; if (!forceLog && (now - _lastLogTime).TotalMilliseconds < LOG_THROTTLE_MS) return; Debug.Log(message); _lastLogTime = now; } public bool Connect(string portName = "COM1", int baudRate = 115200) { try { if (IsConnected) { Debug.LogWarning("串口已连接"); return true; } _serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One) { ReadTimeout = 1000, WriteTimeout = 1000 }; _serialPort.Open(); if (_serialPort.IsOpen) { _keepReading = true; _readThread = new Thread(ReadThread) { IsBackground = true }; _readThread.Start(); Debug.Log($"串口连接成功: {portName} @ {baudRate}"); // 连接成功后发送握手请求 // SendHandshakeRequest(); return true; } } catch (Exception ex) { Debug.LogError($"串口连接失败: {ex.Message}"); OnCommunicationError?.Invoke(); } return false; } public void Disconnect() { _keepReading = false; IsHandshakeCompleted = false; // 等待读取线程安全退出 if (_readThread != null && _readThread.IsAlive) { try { if (!_readThread.Join(2000)) // 增加等待时间到2秒 { Debug.LogWarning("[断开连接] 读取线程未能及时退出,强制中止"); _readThread.Abort(); } } catch (Exception ex) { Debug.LogError($"[断开连接] 停止读取线程异常: {ex.Message}"); } finally { _readThread = null; } } // 清空队列,释放内存 int sendQueueCount = 0, receiveQueueCount = 0; while (_sendQueue.TryDequeue(out _)) { sendQueueCount++; } while (_receiveQueue.TryDequeue(out _)) { receiveQueueCount++; } Debug.Log($"队列已清空: 发送队列{sendQueueCount}项, 接收队列{receiveQueueCount}项"); // 安全关闭串口 if (_serialPort?.IsOpen == true) { try { _serialPort.Close(); Debug.Log("串口已关闭"); } catch (Exception ex) { Debug.LogError($"[断开连接] 关闭串口异常: {ex.Message}"); } } // 释放串口资源 if (_serialPort != null) { try { _serialPort.Dispose(); _serialPort = null; Debug.Log("串口资源已释放"); } catch (Exception ex) { Debug.LogError($"[断开连接] 释放串口资源异常: {ex.Message}"); } } } public void SendHandshakeRequest() { var data = new byte[] { _firmwareVersion }; SendFrame(CMD_HANDSHAKE_REQUEST, data); } /// /// 立即发送帧(跳过队列,用于紧急情况) /// private void SendFrameImmediately(byte command, byte[] data) { if (!IsConnected) return; SendFrameDirectly(command, data, GetCommandName(command)); _lastSendTime = DateTime.Now; } public void SendHeartbeatResponse() { // 添加心跳应答频率限制,防止过于频繁的应答 var now = DateTime.Now; var timeSinceLastResponse = (now - _lastHeartbeatResponseTime).TotalMilliseconds; if (timeSinceLastResponse < HEARTBEAT_RESPONSE_INTERVAL_MS) { // 减少限制日志的频率 if (timeSinceLastResponse < 50) // 只有间隔很短时才记录 { OptimizedLog($"[心跳限制] 距离上次应答仅{timeSinceLastResponse:F0}ms,跳过此次应答"); } return; } var data = new byte[] { 0x00 }; // 应答数据 SendFrame(CMD_HEARTBEAT_RESPONSE, data); _lastHeartbeatResponseTime = now; // 减少心跳日志频率 OptimizedLog("[心跳应答] 发送心跳应答"); } public void SendAlarmCommand(byte alarmCode, byte priority, byte state) { var data = new byte[] { alarmCode, priority, state }; SendFrame(CMD_ALARM_CONTROL, data); Debug.Log($"发送报警命令: Code={alarmCode:X2}, Priority={priority}, State={state}"); } public void SendTimeSync(DateTime time) { var data = new byte[] { (byte)(time.Year % 100), // YY (byte)time.Month, // MM (byte)time.Day, // DD (byte)time.Hour, // HH (byte)time.Minute, // mm (byte)time.Second // ss }; SendFrame(CMD_TIME_SYNC, data); Debug.Log($"发送时间同步: {time:yyyy-MM-dd HH:mm:ss}"); } public void SendParameterQuery(byte paramType) { var data = new byte[] { paramType, 0x00 }; // 查询命令 SendFrame(CMD_PARAM_QUERY_SET, data); } public void SendParameterSet(byte paramType, byte[] paramData) { var data = new byte[paramData.Length + 2]; data[0] = paramType; data[1] = 0x01; // 设置命令 Array.Copy(paramData, 0, data, 2, paramData.Length); SendFrame(CMD_PARAM_QUERY_SET, data); } private void SendFrame(byte command, byte[] data) { if (!IsConnected) return; // 检查队列大小,防止积压过多 if (_sendQueue.Count >= MAX_SEND_QUEUE_SIZE) { OptimizedLog($"[队列满] 发送队列已满({MAX_SEND_QUEUE_SIZE}),丢弃命令: {GetCommandName(command)}", true); // 清理一些旧的命令为新命令让路 if (_sendQueue.TryDequeue(out SendFrameData oldFrame)) { OptimizedLog($"[队列清理] 丢弃旧命令: {oldFrame.Description}"); } } var frameData = new SendFrameData(command, data, GetCommandName(command)); _sendQueue.Enqueue(frameData); // 减少队列日志频率,只有重要命令或队列状态异常时才记录 if (command != CMD_HEARTBEAT_RESPONSE || _sendQueue.Count > 3) { OptimizedLog($"[队列] 已加入发送队列: {GetCommandName(command)}, 队列长度: {_sendQueue.Count}"); } } /// /// 处理发送队列,确保帧间间隔 /// private void ProcessSendQueue() { if (_sendQueue.IsEmpty || !IsConnected) return; var now = DateTime.Now; var timeSinceLastSend = (now - _lastSendTime).TotalMilliseconds; if (timeSinceLastSend >= SEND_INTERVAL_MS) { if (_sendQueue.TryDequeue(out SendFrameData frameData)) { SendFrameDirectly(frameData.Command, frameData.Data, frameData.Description); _lastSendTime = now; } } } /// /// 直接发送帧(内部方法) /// private void SendFrameDirectly(byte command, byte[] data, string description = "") { lock (_sendLock) { try { var frame = BuildFrame(command, data); if (frame == null) { Debug.LogError("构建帧失败"); return; } if (_serialPort == null || !_serialPort.IsOpen) { Debug.LogError("串口未连接"); return; } _serialPort.Write(frame, 0, frame.Length); var hexString = BitConverter.ToString(frame).Replace("-", " "); Debug.Log($"[串口发送] {description}: {hexString}"); } catch (Exception ex) { Debug.LogError($"发送帧失败: {ex.Message}"); Debug.LogError($"异常堆栈: {ex.StackTrace}"); OnCommunicationError?.Invoke(); } } } private byte[] BuildFrame(byte command, byte[] data) { try { var dataLength = data?.Length ?? 0; var totalFrameLength = 7 + dataLength; // 帧头(2) + 命令字(1) + 长度(1) + 数据(N) + 校验(1) + 帧尾(2) = 7+N var frame = new byte[totalFrameLength]; var index = 0; frame[index++] = FRAME_HEADER_1; // 0xAA frame[index++] = FRAME_HEADER_2; // 0x55 frame[index++] = command; frame[index++] = (byte)dataLength; // 复制数据 if (data != null && dataLength > 0) { if (data.Length < dataLength) { Debug.LogError($"数据长度不匹配: 期望{dataLength}, 实际{data.Length}"); return null; } Array.Copy(data, 0, frame, index, dataLength); index += dataLength; } // 计算校验:从命令字到数据区最后字节的异或校验 byte checksum = command; checksum ^= (byte)dataLength; // 安全的校验计算 if (data != null && data.Length > 0) { var actualDataLength = Math.Min(dataLength, data.Length); for (int i = 0; i < actualDataLength; i++) { checksum ^= data[i]; } } frame[index++] = checksum; // 检查帧尾空间是否足够 (需要2个字节) if (index + 1 >= frame.Length) { Debug.LogError($"帧尾空间不足"); return null; } frame[index++] = FRAME_TAIL_1; // 0x0D frame[index++] = FRAME_TAIL_2; // 0x0A return frame; } catch (Exception ex) { Debug.LogError($"BuildFrame异常: {ex.Message}"); return null; } } /// /// 获取命令名称 /// private string GetCommandName(byte command) { switch (command) { case CMD_HANDSHAKE_RESPONSE: return "握手应答"; case CMD_HANDSHAKE_REQUEST: return "握手请求"; case CMD_HEARTBEAT_REPORT: return "心跳上报"; case CMD_HEARTBEAT_RESPONSE: return "心跳应答"; case CMD_ALARM_CONTROL: return "报警控制"; case CMD_DATA_REPORT: return "数据上报"; case CMD_PARAM_QUERY_SET: return "参数查询设置"; case CMD_ALARM_STATUS_FEEDBACK: return "报警状态反馈"; case CMD_TIME_SYNC: return "时间同步"; default: return $"未知命令(0x{command:X2})"; } } private void ReadThread() { var buffer = new byte[256]; var frameBuffer = new byte[256]; var frameIndex = 0; var state = FrameState.WaitHeader1; OptimizedLog("[ReadThread] 串口读取线程已启动", true); while (_keepReading) { try { if (_serialPort.BytesToRead > 0) { int bytesRead = _serialPort.Read(buffer, 0, buffer.Length); // 减少原始数据日志,只在详细模式下输出 if (_enableVerboseLogging) { var rawHex = BitConverter.ToString(buffer, 0, bytesRead).Replace("-", " "); OptimizedLog($"[原始数据] 接收 {bytesRead} 字节: {rawHex}"); } for (int i = 0; i < bytesRead; i++) { if (ProcessFrameByte(buffer[i], ref frameBuffer, ref frameIndex, ref state)) { // 完整帧接收完成 var frame = new byte[frameIndex]; Array.Copy(frameBuffer, frame, frameIndex); // 减少接收帧日志频率 var commandByte = frame.Length > 2 ? frame[2] : (byte)0x00; var hexString = BitConverter.ToString(frame).Replace("-", " "); // 只记录重要命令的接收信息 if (commandByte != CMD_HEARTBEAT_REPORT || _enableVerboseLogging) { OptimizedLog($"[串口接收] {GetCommandName(commandByte)}: {hexString}"); } // 检查接收队列大小,防止积压过多 if (_receiveQueue.Count >= MAX_RECEIVE_QUEUE_SIZE) { OptimizedLog($"[接收队列满] 队列长度达到{MAX_RECEIVE_QUEUE_SIZE},丢弃最旧的帧", true); if (_receiveQueue.TryDequeue(out byte[] oldFrame)) { var oldCommand = oldFrame.Length > 2 ? oldFrame[2] : (byte)0x00; OptimizedLog($"[接收队列清理] 丢弃旧帧: {GetCommandName(oldCommand)}"); } } _receiveQueue.Enqueue(frame); frameIndex = 0; state = FrameState.WaitHeader1; } } } else { Thread.Sleep(10); } // 注意:不在子线程中处理队列,改为在Unity主线程Update中处理 } catch (ThreadAbortException) { break; } catch (Exception ex) { #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogError("SerialComm", $"读取线程异常: {ex.GetType().Name} - {ex.Message}"); #endif // 特殊处理可能导致Unity底层错误的异常 if (ex is System.IO.IOException || ex.Message.Contains("Network") || ex.Message.Contains("Socket") || ex.Message.Contains("unreachable")) { OptimizedLog($"[严重异常] 检测到系统级通信错误,停止读取线程: {ex.Message}", true); // 立即停止接收,避免系统级错误导致安卓板死机 break; } else { OptimizedLog($"读取线程异常: {ex.Message}", true); Thread.Sleep(1000); // 延长等待时间,减少错误频率 } } } } private bool ProcessFrameByte(byte b, ref byte[] frameBuffer, ref int frameIndex, ref FrameState state) { switch (state) { case FrameState.WaitHeader1: if (b == FRAME_HEADER_1) { frameBuffer[frameIndex++] = b; state = FrameState.WaitHeader2; // 减少帧头日志 if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到帧头1: 0x{b:X2}, 索引={frameIndex}"); } break; case FrameState.WaitHeader2: if (b == FRAME_HEADER_2) { frameBuffer[frameIndex++] = b; state = FrameState.ReadCommand; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到帧头2: 0x{b:X2}, 索引={frameIndex}"); } else { if (_enableVerboseLogging) OptimizedLog($"[帧解析] 帧头2错误: 期望0x55, 收到0x{b:X2}, 重置状态"); frameIndex = 0; state = FrameState.WaitHeader1; // 重新检查当前字节是否是新的帧头1 if (b == FRAME_HEADER_1) { frameBuffer[frameIndex++] = b; state = FrameState.WaitHeader2; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 重置后立即收到帧头1: 0x{b:X2}"); } } break; case FrameState.ReadCommand: frameBuffer[frameIndex++] = b; state = FrameState.ReadLength; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到命令字: 0x{b:X2}, 索引={frameIndex}"); break; case FrameState.ReadLength: frameBuffer[frameIndex++] = b; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到数据长度: {b}, 索引={frameIndex}"); if (b == 0) { state = FrameState.ReadChecksum; // 无数据,直接读校验 if (_enableVerboseLogging) OptimizedLog("[帧解析] 数据长度为0,直接读校验"); } else { state = FrameState.ReadData; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 开始读取{b}字节数据"); } break; case FrameState.ReadData: frameBuffer[frameIndex++] = b; var dataLength = frameBuffer[3]; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 读取数据字节: 0x{b:X2}, 当前索引={frameIndex}, 期望总长度={4 + dataLength}"); if (frameIndex >= 4 + dataLength) { state = FrameState.ReadChecksum; if (_enableVerboseLogging) OptimizedLog("[帧解析] 数据读取完成,转到校验状态"); } break; case FrameState.ReadChecksum: frameBuffer[frameIndex++] = b; state = FrameState.ReadTail1; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到校验字节: 0x{b:X2}, 索引={frameIndex}"); break; case FrameState.ReadTail1: if (b == FRAME_TAIL_1) { frameBuffer[frameIndex++] = b; state = FrameState.ReadTail2; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到帧尾1: 0x{b:X2}, 索引={frameIndex}"); } else { OptimizedLog($"[帧解析] 帧尾1错误: 期望0x0D, 收到0x{b:X2}, 重置状态", true); frameIndex = 0; state = FrameState.WaitHeader1; // 重新检查当前字节是否是新的帧头1 if (b == FRAME_HEADER_1) { frameBuffer[frameIndex++] = b; state = FrameState.WaitHeader2; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 重置后立即收到帧头1: 0x{b:X2}"); } } break; case FrameState.ReadTail2: if (b == FRAME_TAIL_2) { frameBuffer[frameIndex++] = b; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 收到帧尾2: 0x{b:X2}, 完整帧接收完成! 总长度={frameIndex}"); return true; // 完整帧接收完成 } else { OptimizedLog($"[帧解析] 帧尾2错误: 期望0x0A, 收到0x{b:X2}, 重置状态", true); frameIndex = 0; state = FrameState.WaitHeader1; // 重新检查当前字节是否是新的帧头1 if (b == FRAME_HEADER_1) { frameBuffer[frameIndex++] = b; state = FrameState.WaitHeader2; if (_enableVerboseLogging) OptimizedLog($"[帧解析] 重置后立即收到帧头1: 0x{b:X2}"); } } break; } return false; } private void ProcessReceiveQueue() { int processedCount = 0; const int MAX_FRAMES_PER_UPDATE = 10; // 每次Update最多处理10个帧,防止阻塞 while (_receiveQueue.TryDequeue(out byte[] frame) && processedCount < MAX_FRAMES_PER_UPDATE) { try { ProcessReceivedFrame(frame); processedCount++; } catch (Exception ex) { Debug.LogError($"[接收队列] 处理帧异常: {ex.Message}"); Debug.LogError($"[接收队列] 异常堆栈: {ex.StackTrace}"); // 继续处理下一个帧,不让单个异常影响整体流程 } } if (processedCount >= MAX_FRAMES_PER_UPDATE && !_receiveQueue.IsEmpty) { Debug.Log($"[接收队列] 本次处理{processedCount}个帧,队列还有{_receiveQueue.Count}个待处理"); } } /// /// 在Unity主线程中处理接收队列和发送队列 /// public void Update() { ProcessReceiveQueue(); ProcessSendQueue(); // 定期内存清理 var now = DateTime.Now; if ((now - _lastMemoryCleanup).TotalMinutes >= MEMORY_CLEANUP_INTERVAL_MINUTES) { PerformMemoryCleanup(); MonitorMemoryUsage(); // 监控内存使用情况 _lastMemoryCleanup = now; } } /// /// 定期内存清理,防止长期运行内存积累 /// private void PerformMemoryCleanup() { OptimizedLog("[内存清理] 开始定期清理...", true); int initialSendCount = _sendQueue.Count; int initialReceiveCount = _receiveQueue.Count; // 清理发送队列中过多的心跳应答(保留最新的几个) var tempSendList = new List(); while (_sendQueue.TryDequeue(out SendFrameData frame)) { tempSendList.Add(frame); } // 只保留最新的心跳应答和其他重要命令 int heartbeatCount = 0; foreach (var frame in tempSendList.AsEnumerable().Reverse()) { if (frame.Command == CMD_HEARTBEAT_RESPONSE) { if (heartbeatCount < 2) // 最多保留2个心跳应答 { _sendQueue.Enqueue(frame); heartbeatCount++; } } else { _sendQueue.Enqueue(frame); // 保留所有非心跳命令 } } // 强制垃圾回收 System.GC.Collect(); System.GC.WaitForPendingFinalizers(); OptimizedLog($"[内存清理] 完成。发送队列: {initialSendCount}->{_sendQueue.Count}, " + $"接收队列: {initialReceiveCount}->{_receiveQueue.Count}", true); } private void ProcessReceivedFrame(byte[] frame) { try { if (!ValidateFrame(frame)) return; var command = frame[2]; var dataLength = frame[3]; var data = new byte[dataLength]; if (dataLength > 0) { Array.Copy(frame, 4, data, 0, dataLength); } var hexString = BitConverter.ToString(frame).Replace("-", " "); Debug.Log($"接收帧: {hexString}"); switch (command) { case CMD_HANDSHAKE_REQUEST: ProcessHandshakeRequest(data); break; case CMD_HANDSHAKE_RESPONSE: ProcessHandshakeResponse(data); break; case CMD_HEARTBEAT_REPORT: ProcessHeartbeatReport(data); break; case CMD_DATA_REPORT: ProcessDataReport(data); break; case CMD_ALARM_STATUS_FEEDBACK: ProcessAlarmStatusFeedback(data); break; default: Debug.LogWarning($"未知命令字: 0x{command:X2}"); break; } } catch (Exception ex) { Debug.LogError($"处理接收帧异常: {ex.Message}"); } } private bool ValidateFrame(byte[] frame) { if (frame.Length < 6) { OptimizedLog($"[校验失败] 帧长度不足: {frame.Length}, 最小需要6字节", true); return false; } var command = frame[2]; var dataLength = frame[3]; var expectedLength = 7 + dataLength; // 帧头(2) + 命令字(1) + 长度(1) + 数据(N) + 校验(1) + 帧尾(2) if (frame.Length != expectedLength) { OptimizedLog($"[校验失败] 帧长度不匹配: 实际{frame.Length}, 期望{expectedLength}", true); return false; } // 验证校验和 byte checksum = command; checksum ^= (byte)dataLength; for (int i = 0; i < dataLength; i++) { checksum ^= frame[4 + i]; } var receivedChecksum = frame[4 + dataLength]; bool checksumValid = checksum == receivedChecksum; if (!checksumValid) { OptimizedLog($"[校验失败] 校验和不匹配: 计算{checksum:X2}, 接收{receivedChecksum:X2}", true); if (_enableVerboseLogging) { OptimizedLog($"帧内容: {BitConverter.ToString(frame)}", true); } // 校验失败帧直接丢弃,避免坏帧进入后续BFI计算 return true; } else { if (_enableVerboseLogging) OptimizedLog($"[校验成功] 命令{GetCommandName(command)}, 长度{dataLength}, 校验{checksum:X2}"); } return checksumValid; } private void ProcessHandshakeRequest(byte[] data) { // 如果握手已完成,避免重复处理握手请求 // if (IsHandshakeCompleted) // { // Debug.Log("[握手保护] 握手已完成,忽略重复握手请求"); // return; // } // 根据协议,收到握手请求后,上位机应该发送握手应答+时间同步 Debug.Log($"收到握手请求,固件版本: {(data.Length > 0 ? data[0] : 0)}"); // 发送握手应答+时间同步 (命令字0x01) var now = DateTime.Now; var timeData = new byte[] { (byte)(now.Year % 100), // YY (byte)now.Month, // MM (byte)now.Day, // DD (byte)now.Hour, // HH (byte)now.Minute, // mm (byte)now.Second // ss }; SendFrame(CMD_HANDSHAKE_RESPONSE, timeData); Debug.Log($"立即发送握手应答+时间同步: {now:yyyy-MM-dd HH:mm:ss}"); IsHandshakeCompleted = true; OnHandshakeCompleted?.Invoke(); } private void ProcessHandshakeResponse(byte[] data) { Debug.Log($"收到握手应答,数据长度: {data.Length}"); if (data.Length >= 6) { // 解析时间同步数据 var year = 2000 + data[0]; var month = data[1]; var day = data[2]; var hour = data[3]; var minute = data[4]; var second = data[5]; Debug.Log($"握手成功,时间同步: {year:D4}-{month:D2}-{day:D2} {hour:D2}:{minute:D2}:{second:D2}"); } IsHandshakeCompleted = true; OnHandshakeCompleted?.Invoke(); } private void ProcessHeartbeatReport(byte[] data) { // 减少详细日志,只在必要时输出 OptimizedLog($"[心跳处理] 开始处理心跳上报,数据长度: {data.Length}"); if (data.Length >= 15) { try { // 正确的BFI解析:数据格式为小端序 // 原始数据:CE 4A B8 42 -> 按小端序解析 float littleEndianBfi = BitConverter.ToSingle(data, 0); float bfi = littleEndianBfi; // 如果结果异常,尝试大端序解析 if (!IsValidBfi(bfi)) { // 尝试大端序解析 var bigEndianBytes = new byte[4] { data[3], data[2], data[1], data[0] }; float bigEndianBfi = BitConverter.ToSingle(bigEndianBytes, 0); if (IsValidBfi(bigEndianBfi)) { bfi = bigEndianBfi; OptimizedLog($"[心跳解析] 小端序异常({littleEndianBfi:F2}),改用大端序: {bfi:F2}", true); } else { bfi = _lastValidBFI; OptimizedLog($"[心跳解析] BFI异常(小端={littleEndianBfi}, 大端={bigEndianBfi}),回退到最近有效值: {bfi:F2}", true); } } else { _lastValidBFI = bfi; } // 按照DCS协议解析心跳数据 var status = new DeviceStatusData { BFI = bfi, // BFI值 BatteryLevel = data[4], // 偏移4: 1字节电量% BatteryVoltage = BitConverter.ToUInt16(data, 5), // 偏移5: 2字节电压mV (小端序) Temperature = BitConverter.ToInt16(data, 7), // 偏移7: 2字节温度(0.1°C) (小端序) LaserStatus = data[9], // 偏移9: 1字节激光状态 PowerType = data[10], // 偏移10: 1字节电源类型 ReservedData = BitConverter.ToUInt32(data, 11) // 偏移11: 4字节保留 }; // 数据合理性检查和修正 if (float.IsNaN(status.BFI) || float.IsInfinity(status.BFI)) { OptimizedLog($"[心跳解析] BFI值异常: {status.BFI}, 设置为默认值50.0", true); status.BFI = 50.0f; } else if (status.BFI < MIN_VALID_BFI || status.BFI > MAX_VALID_BFI) { OptimizedLog($"[心跳解析] BFI值超出合理范围: {status.BFI:F2}, 回退到最近有效值: {_lastValidBFI:F2}", true); status.BFI = _lastValidBFI; } else { _lastValidBFI = status.BFI; } if (status.BatteryLevel > 100) { OptimizedLog($"[心跳解析] 电量值异常: {status.BatteryLevel}%, 限制到100%", true); status.BatteryLevel = 100; } // 输出详细解析结果(仅在调试模式或异常情况下) if (_enableVerboseLogging || status.BatteryLevel == 0) { OptimizedLog($"[心跳解析成功] BFI={status.BFI:F2}, 电量={status.BatteryLevel}%, " + $"电压={status.BatteryVoltage}mV, 温度={status.Temperature * 0.1f:F1}°C, " + $"激光={GetLaserStatusText(status.LaserStatus)}, 电源={GetPowerTypeText(status.PowerType)}", true); // 输出原始数据用于调试 // var hexData = BitConverter.ToString(data, 0, Math.Min(15, data.Length)).Replace("-", " "); // OptimizedLog($"[心跳原始数据] {hexData}", true); } _lastHeartbeat = DateTime.Now; // 安全地触发事件,防止事件处理异常导致崩溃 try { OnDeviceStatusReceived?.Invoke(status); } catch (Exception eventEx) { OptimizedLog($"[心跳处理] 事件处理异常: {eventEx.Message}", true); } // 延迟发送心跳应答 SendHeartbeatResponse(); } catch (Exception ex) { OptimizedLog($"[心跳解析错误] {ex.Message}", true); var hexData = BitConverter.ToString(data, 0, Math.Min(data.Length, 20)).Replace("-", " "); OptimizedLog($"[错误数据] {hexData}", true); } } else { OptimizedLog($"[心跳错误] 心跳上报数据长度不足: {data.Length}, 期望>=15", true); } } private bool IsValidBfi(float value) { return !float.IsNaN(value) && !float.IsInfinity(value) && value >= MIN_VALID_BFI && value <= MAX_VALID_BFI; } private string GetLaserStatusText(byte status) { return status switch { 0 => "异常", 1 => "工作", 2 => "停止", _ => "未知" }; } private string GetPowerTypeText(byte type) { return type switch { 0 => "AC", 1 => "电池", _ => "未知" }; } private void ProcessDataReport(byte[] data) { Debug.Log($"处理数据上报,数据长度: {data.Length}"); if (data.Length >= 15) { var report = new DeviceDataReport { BFI = BitConverter.ToSingle(data, 0), BatteryLevel = data[4], Voltage = BitConverter.ToUInt16(data, 5), Temperature = BitConverter.ToInt16(data, 7), LaserStatus = data[9], PowerStatus = data[10], ExtensionData = BitConverter.ToUInt32(data, 11) }; Debug.Log($"数据上报: BFI={report.BFI:F1}, 电量={report.BatteryLevel}%, " + $"电压={report.Voltage}mV, 温度={report.Temperature * 0.1f:F1}°C, " + $"激光={report.LaserStatus}, 电源={report.PowerStatus}"); // OnDeviceDataReceived?.Invoke(report); } else { Debug.LogWarning($"数据上报数据长度不足: {data.Length}, 期望>=15"); } } private void ProcessAlarmStatusFeedback(byte[] data) { Debug.Log($"处理报警状态反馈,数据长度: {data.Length}"); if (data.Length >= 2) { // 根据实际测试,报警状态反馈可能只有2个字节 var alarm = new AlarmStatusReport { AlarmCode = data[0], AlarmPriority = data.Length > 1 ? data[1] : (byte)1, // 默认中等优先级 AlarmState = data.Length > 2 ? data[2] : (byte)1, // 默认触发状态 Timestamp = DateTime.Now }; Debug.Log($"报警反馈: 代码=0x{alarm.AlarmCode:X2}, 优先级={alarm.AlarmPriority}, 状态={alarm.AlarmState}"); // OnAlarmStatusReceived?.Invoke(alarm); } else { Debug.LogWarning($"报警状态反馈数据长度不足: {data.Length}"); } } private void CheckHeartbeat(object state) { if (IsConnected && IsHandshakeCompleted) { var timeSinceLastHeartbeat = DateTime.Now - _lastHeartbeat; if (timeSinceLastHeartbeat.TotalSeconds > 30) // 30秒无心跳认为通信异常(考虑报警发送延迟) { Debug.LogWarning($"[心跳超时] 距离上次心跳{timeSinceLastHeartbeat.TotalSeconds:F1}秒,通信异常"); #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogError("Heartbeat", $"心跳超时: 距离上次{timeSinceLastHeartbeat.TotalSeconds:F1}秒"); #endif OnCommunicationError?.Invoke(); } } } public void Dispose() { // _heartbeatTimer?.Dispose(); Disconnect(); } // 系统状态监控和诊断方法 public void PrintSystemStatus() { OptimizedLog("=== 串口通信系统状态诊断 ===", true); OptimizedLog($"[连接状态] 连接: {IsConnected}, 握手: {IsHandshakeCompleted}", true); OptimizedLog($"[队列状态] 发送队列: {_sendQueue.Count}/{MAX_SEND_QUEUE_SIZE}, 接收队列: {_receiveQueue.Count}/{MAX_RECEIVE_QUEUE_SIZE}", true); OptimizedLog($"[线程状态] 读取线程: {(_readThread?.IsAlive == true ? "运行中" : "已停止")}", true); var timeSinceLastHeartbeat = DateTime.Now - _lastHeartbeat; OptimizedLog($"[心跳状态] 距离上次心跳: {timeSinceLastHeartbeat.TotalSeconds:F1}秒", true); var timeSinceLastResponse = DateTime.Now - _lastHeartbeatResponseTime; OptimizedLog($"[应答状态] 距离上次应答: {timeSinceLastResponse.TotalSeconds:F1}秒", true); var timeSinceLastSend = DateTime.Now - _lastSendTime; OptimizedLog($"[发送状态] 距离上次发送: {timeSinceLastSend.TotalMilliseconds:F0}ms", true); // 内存使用情况 var memoryUsage = System.GC.GetTotalMemory(false) / (1024 * 1024); // MB OptimizedLog($"[内存状态] 当前使用: {memoryUsage:F1}MB", true); OptimizedLog("=== 诊断完成 ===", true); } // 获取队列统计信息 public string GetQueueStatistics() { return $"发送队列: {_sendQueue.Count}/{MAX_SEND_QUEUE_SIZE}, " + $"接收队列: {_receiveQueue.Count}/{MAX_RECEIVE_QUEUE_SIZE}, " + $"连接状态: {(IsConnected ? "已连接" : "未连接")}, " + $"握手状态: {(IsHandshakeCompleted ? "已完成" : "未完成")}"; } // 强制清理队列(紧急情况使用) public void ForceCleanQueues() { int sendCount = 0, receiveCount = 0; while (_sendQueue.TryDequeue(out _)) { sendCount++; } while (_receiveQueue.TryDequeue(out _)) { receiveCount++; } OptimizedLog($"[强制清理] 清除了 {sendCount} 个发送帧, {receiveCount} 个接收帧", true); // 强制垃圾回收 System.GC.Collect(); System.GC.WaitForPendingFinalizers(); } // 内存监控方法 public void MonitorMemoryUsage() { var memoryBefore = System.GC.GetTotalMemory(false); var memoryAfter = System.GC.GetTotalMemory(true); // 强制垃圾回收 var memoryBeforeMB = memoryBefore / (1024f * 1024f); var memoryAfterMB = memoryAfter / (1024f * 1024f); var freedMB = (memoryBefore - memoryAfter) / (1024f * 1024f); #if UNITY_ANDROID && !UNITY_EDITOR AndroidFileLogger.Instance?.LogInfo("SerialComm", $"内存清理: {memoryBeforeMB:F1}MB -> {memoryAfterMB:F1}MB, 释放: {freedMB:F1}MB, " + $"队列状态: {GetQueueStatus()}"); #endif OptimizedLog($"[内存监控] 清理前: {memoryBeforeMB:F1}MB, 清理后: {memoryAfterMB:F1}MB, 释放: {freedMB:F1}MB", true); } /// /// 获取队列状态信息 /// public string GetQueueStatus() { return $"发送队列:{_sendQueue.Count}, 接收队列:{_receiveQueue.Count}, 连接状态:{(_serialPort?.IsOpen == true ? "已连接" : "未连接")}"; } private enum FrameState { WaitHeader1, WaitHeader2, ReadCommand, ReadLength, ReadData, ReadChecksum, ReadTail1, ReadTail2 } }