DCS/ruiyiweiUX/Assets/Scripts/Services/SerialCommunicationService.cs

1244 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.IO.Ports;
using System.Threading;
using UnityEngine;
/// <summary>
/// 发送帧数据结构
/// </summary>
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;
}
}
/// <summary>
/// 串口通信服务实现 - 基于DCS项目串口通信协议
/// </summary>
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<DeviceStatusData> OnDeviceStatusReceived;
// public event Action<DeviceDataReport> OnDeviceDataReceived;
// public event Action<AlarmStatusReport> 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<byte[]> _receiveQueue = new ConcurrentQueue<byte[]>();
private readonly object _sendLock = new object();
// 接收队列大小限制,防止内存积压
private const int MAX_RECEIVE_QUEUE_SIZE = 50;
// 发送队列相关
private readonly ConcurrentQueue<SendFrameData> _sendQueue = new ConcurrentQueue<SendFrameData>();
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 ? "" : "")}");
}
/// <summary>
/// 优化的日志输出,减少安卓板内存压力
/// </summary>
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);
}
/// <summary>
/// 立即发送帧(跳过队列,用于紧急情况)
/// </summary>
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}");
}
}
/// <summary>
/// 处理发送队列,确保帧间间隔
/// </summary>
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;
}
}
}
/// <summary>
/// 直接发送帧(内部方法)
/// </summary>
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;
}
}
/// <summary>
/// 获取命令名称
/// </summary>
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}个待处理");
}
}
/// <summary>
/// 在Unity主线程中处理接收队列和发送队列
/// </summary>
public void Update()
{
ProcessReceiveQueue();
ProcessSendQueue();
// 定期内存清理
var now = DateTime.Now;
if ((now - _lastMemoryCleanup).TotalMinutes >= MEMORY_CLEANUP_INTERVAL_MINUTES)
{
PerformMemoryCleanup();
MonitorMemoryUsage(); // 监控内存使用情况
_lastMemoryCleanup = now;
}
}
/// <summary>
/// 定期内存清理,防止长期运行内存积累
/// </summary>
private void PerformMemoryCleanup()
{
OptimizedLog("[内存清理] 开始定期清理...", true);
int initialSendCount = _sendQueue.Count;
int initialReceiveCount = _receiveQueue.Count;
// 清理发送队列中过多的心跳应答(保留最新的几个)
var tempSendList = new List<SendFrameData>();
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);
}
/// <summary>
/// 获取队列状态信息
/// </summary>
public string GetQueueStatus()
{
return $"发送队列:{_sendQueue.Count}, 接收队列:{_receiveQueue.Count}, 连接状态:{(_serialPort?.IsOpen == true ? "" : "")}";
}
private enum FrameState
{
WaitHeader1,
WaitHeader2,
ReadCommand,
ReadLength,
ReadData,
ReadChecksum,
ReadTail1,
ReadTail2
}
}