1244 lines
46 KiB
C#
1244 lines
46 KiB
C#
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
|
||
}
|
||
} |