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
}
}