630 lines
18 KiB
C#
630 lines
18 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using UnityEngine;
|
||
using BLEProtocol;
|
||
|
||
/// <summary>
|
||
/// OTA版本信息
|
||
/// </summary>
|
||
[Serializable]
|
||
public class OTAVersionInfo
|
||
{
|
||
public uint FirmwareVersion; // 固件版本 (4字节)
|
||
public uint HardwareVersion; // 硬件版本 (4字节)
|
||
public uint OTAVersion; // OTA版本 (4字节)
|
||
|
||
public override string ToString()
|
||
{
|
||
return $"固件版本: {FirmwareVersion}, 硬件版本: {HardwareVersion}, OTA版本: {OTAVersion}";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// OTA升级状态
|
||
/// </summary>
|
||
public enum OTAState
|
||
{
|
||
Idle, // 空闲
|
||
QueryingVersion, // 查询版本中
|
||
Starting, // 开始升级中
|
||
Transferring, // 传输数据中
|
||
Ending, // 结束传输中
|
||
Cancelling, // 取消升级中
|
||
Completed, // 完成
|
||
Error // 错误
|
||
}
|
||
|
||
/// <summary>
|
||
/// OTA升级管理器 - 处理固件OTA升级
|
||
/// </summary>
|
||
public class OTAManager : MonoBehaviour
|
||
{
|
||
public static OTAManager Instance { get; private set; }
|
||
|
||
// 事件定义
|
||
public event Action<OTAVersionInfo> OnVersionInfoReceived; // 收到版本信息
|
||
public event Action<bool> OnStartUpgradeResult; // 开始升级结果
|
||
public event Action<int, int, int, int> OnTransferProgress; // 传输进度 (当前包, 总包数, 重试次数, 最大重试次数)
|
||
public event Action<bool> OnTransferComplete; // 传输完成
|
||
public event Action<bool> OnCancelResult; // 取消结果
|
||
public event Action<string> OnError; // 错误事件
|
||
public event Action<OTAState> OnStateChanged; // 状态变化
|
||
|
||
// 状态
|
||
public OTAState CurrentState { get; private set; } = OTAState.Idle;
|
||
|
||
// 升级参数
|
||
private byte[] _firmwareData; // 固件数据
|
||
private uint _firmwareVersion; // 固件版本
|
||
private int _currentPacketIndex; // 当前包序号
|
||
private int _totalPackets; // 总包数
|
||
private const int PACKET_SIZE = 512; // 每包数据大小(根据设备MTU调整)
|
||
|
||
private void Awake()
|
||
{
|
||
if (Instance == null)
|
||
{
|
||
Instance = this;
|
||
DontDestroyOnLoad(gameObject);
|
||
}
|
||
else
|
||
{
|
||
Destroy(gameObject);
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
if (Instance == this)
|
||
{
|
||
Instance = null;
|
||
}
|
||
}
|
||
|
||
#region OTA命令接口
|
||
|
||
/// <summary>
|
||
/// 查询OTA版本信息 (命令 0x80)
|
||
/// </summary>
|
||
public void QueryVersionInfo(Action<OTAVersionInfo> callback = null)
|
||
{
|
||
if (!CheckConnection()) return;
|
||
|
||
SetState(OTAState.QueryingVersion);
|
||
|
||
var frame = new BLEFrame
|
||
{
|
||
Header1 = BLEConstants.FRAME_HEADER_1,
|
||
Header2 = BLEConstants.FRAME_HEADER_2,
|
||
Command = BLEConstants.CMD_OTA_QUERY_VERSION,
|
||
ReadWrite = BLEConstants.RW_READ,
|
||
Length = 0,
|
||
Data = null
|
||
};
|
||
|
||
SendFrame(frame, (response) =>
|
||
{
|
||
if (response.IsSuccess && response.Data != null && response.Data.Length >= 12)
|
||
{
|
||
var info = new OTAVersionInfo
|
||
{
|
||
FirmwareVersion = BitConverter.ToUInt32(response.Data, 0),
|
||
HardwareVersion = BitConverter.ToUInt32(response.Data, 4),
|
||
OTAVersion = BitConverter.ToUInt32(response.Data, 8)
|
||
};
|
||
SetState(OTAState.Idle);
|
||
OnVersionInfoReceived?.Invoke(info);
|
||
callback?.Invoke(info);
|
||
}
|
||
else
|
||
{
|
||
SetState(OTAState.Error);
|
||
OnError?.Invoke("查询OTA版本信息失败");
|
||
callback?.Invoke(null);
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始OTA升级 (命令 0x81)
|
||
/// </summary>
|
||
/// <param name="firmwareData">固件数据</param>
|
||
/// <param name="firmwareVersion">固件版本</param>
|
||
/// <param name="callback">回调</param>
|
||
public void StartUpgrade(byte[] firmwareData, uint firmwareVersion, Action<bool> callback = null)
|
||
{
|
||
if (!CheckConnection())
|
||
{
|
||
callback?.Invoke(false);
|
||
return;
|
||
}
|
||
|
||
if (firmwareData == null || firmwareData.Length == 0)
|
||
{
|
||
LogError("固件数据为空");
|
||
callback?.Invoke(false);
|
||
return;
|
||
}
|
||
|
||
_firmwareData = firmwareData;
|
||
_firmwareVersion = firmwareVersion;
|
||
_currentPacketIndex = 0;
|
||
_totalPackets = (int)Math.Ceiling((double)firmwareData.Length / PACKET_SIZE);
|
||
|
||
SetState(OTAState.Starting);
|
||
|
||
// 计算CRC16
|
||
ushort crc16 = CalculateCRC16(firmwareData);
|
||
|
||
// 构建数据: 版本号(4) + 大小(4) + CRC16(2)
|
||
byte[] data = new byte[10];
|
||
BitConverter.GetBytes(firmwareVersion).CopyTo(data, 0);
|
||
BitConverter.GetBytes((uint)firmwareData.Length).CopyTo(data, 4);
|
||
BitConverter.GetBytes(crc16).CopyTo(data, 8);
|
||
|
||
var frame = new BLEFrame
|
||
{
|
||
Header1 = BLEConstants.FRAME_HEADER_1,
|
||
Header2 = BLEConstants.FRAME_HEADER_2,
|
||
Command = BLEConstants.CMD_OTA_START,
|
||
ReadWrite = BLEConstants.RW_WRITE,
|
||
Length = (byte)data.Length,
|
||
Data = data
|
||
};
|
||
|
||
// 使用协程来处理响应和延迟
|
||
StartCoroutine(StartUpgradeCoroutineInternal(frame, callback));
|
||
}
|
||
|
||
private IEnumerator StartUpgradeCoroutineInternal(BLEFrame frame, Action<bool> callback)
|
||
{
|
||
BLEResponse response = new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR };
|
||
bool completed = false;
|
||
|
||
SendFrame(frame, (resp) =>
|
||
{
|
||
response = resp;
|
||
completed = true;
|
||
});
|
||
|
||
yield return new WaitUntil(() => completed);
|
||
|
||
bool success = response.IsSuccess;
|
||
if (success)
|
||
{
|
||
// 检查连接状态,如果断开则等待重连
|
||
int retryCount = 0;
|
||
const int maxRetries = 10;
|
||
while (BluetoothManager.Instance != null && !BluetoothManager.Instance.IsConnected && retryCount < maxRetries)
|
||
{
|
||
yield return new WaitForSeconds(0.5f);
|
||
retryCount++;
|
||
}
|
||
|
||
if (BluetoothManager.Instance == null || !BluetoothManager.Instance.IsConnected)
|
||
{
|
||
SetState(OTAState.Error);
|
||
OnStartUpgradeResult?.Invoke(false);
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
// 实际测试连接是否可用 - 发送一个查询版本命令
|
||
bool testCompleted = false;
|
||
bool testSuccess = false;
|
||
|
||
var testFrame = new BLEFrame
|
||
{
|
||
Header1 = BLEConstants.FRAME_HEADER_1,
|
||
Header2 = BLEConstants.FRAME_HEADER_2,
|
||
Command = BLEConstants.CMD_OTA_QUERY_VERSION,
|
||
ReadWrite = BLEConstants.RW_READ,
|
||
Length = 0,
|
||
Data = null
|
||
};
|
||
|
||
SendFrame(testFrame, (testResponse) =>
|
||
{
|
||
testSuccess = testResponse.IsSuccess;
|
||
testCompleted = true;
|
||
});
|
||
|
||
// 等待最多3秒
|
||
float testStartTime = Time.time;
|
||
while (!testCompleted && Time.time - testStartTime < 3f)
|
||
{
|
||
yield return null;
|
||
}
|
||
|
||
if (!testCompleted)
|
||
{
|
||
LogError("[OTA-DEBUG] 连接测试超时,BLE连接可能已断开");
|
||
SetState(OTAState.Error);
|
||
OnStartUpgradeResult?.Invoke(false);
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
if (!testSuccess)
|
||
{
|
||
SetState(OTAState.Error);
|
||
OnStartUpgradeResult?.Invoke(false);
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
SetState(OTAState.Transferring);
|
||
OnStartUpgradeResult?.Invoke(true);
|
||
callback?.Invoke(true);
|
||
}
|
||
else
|
||
{
|
||
SetState(OTAState.Error);
|
||
OnStartUpgradeResult?.Invoke(false);
|
||
callback?.Invoke(false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 传输固件数据包 (命令 0x82)
|
||
/// </summary>
|
||
public IEnumerator TransferFirmware(Action<bool> callback = null)
|
||
{
|
||
if (_firmwareData == null)
|
||
{
|
||
LogError("没有固件数据");
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
SetState(OTAState.Transferring);
|
||
|
||
// 每包实际数据长度(不包括包序号)
|
||
const int DATA_PER_PACKET = 253; // 255 - 2 (包序号)
|
||
|
||
// 重新计算总包数
|
||
_totalPackets = (_firmwareData.Length + DATA_PER_PACKET - 1) / DATA_PER_PACKET;
|
||
|
||
for (int i = 0; i < _totalPackets; i++)
|
||
{
|
||
_currentPacketIndex = i;
|
||
int offset = i * DATA_PER_PACKET; // 使用实际数据长度计算偏移
|
||
int length = Mathf.Min(DATA_PER_PACKET, _firmwareData.Length - offset);
|
||
|
||
// 构建数据包: 包序号(2) + 数据(N)
|
||
byte[] packetData = new byte[2 + length];
|
||
BitConverter.GetBytes((ushort)i).CopyTo(packetData, 0);
|
||
Buffer.BlockCopy(_firmwareData, offset, packetData, 2, length);
|
||
|
||
var frame = new BLEFrame
|
||
{
|
||
Header1 = BLEConstants.FRAME_HEADER_1,
|
||
Header2 = BLEConstants.FRAME_HEADER_2,
|
||
Command = BLEConstants.CMD_OTA_TRANSFER_DATA,
|
||
ReadWrite = BLEConstants.RW_WRITE,
|
||
Length = (byte)packetData.Length,
|
||
Data = packetData
|
||
};
|
||
|
||
// 发送数据包,带重试机制
|
||
bool packetSuccess = false;
|
||
int retryCount = 0;
|
||
const int maxRetries = 5;
|
||
|
||
while (!packetSuccess && retryCount < maxRetries)
|
||
{
|
||
if (retryCount > 0)
|
||
{
|
||
OnTransferProgress?.Invoke(i + 1, _totalPackets, retryCount, maxRetries);
|
||
yield return new WaitForSeconds(0.5f);
|
||
}
|
||
|
||
yield return SendFrameCoroutine(frame, (response) =>
|
||
{
|
||
packetSuccess = response.IsSuccess;
|
||
});
|
||
|
||
if (!packetSuccess)
|
||
{
|
||
retryCount++;
|
||
}
|
||
}
|
||
|
||
if (!packetSuccess)
|
||
{
|
||
SetState(OTAState.Error);
|
||
OnError?.Invoke($"传输包 {_currentPacketIndex} 失败");
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
// 报告进度(无重试)
|
||
OnTransferProgress?.Invoke(i + 1, _totalPackets, 0, 0);
|
||
|
||
// 每包之间添加小延迟,避免设备处理不过来
|
||
if (i < _totalPackets - 1)
|
||
{
|
||
yield return new WaitForSeconds(0.01f);
|
||
}
|
||
}
|
||
|
||
SetState(OTAState.Ending);
|
||
OnTransferComplete?.Invoke(true);
|
||
callback?.Invoke(true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 结束传输 (命令 0x83)
|
||
/// </summary>
|
||
public void EndTransfer(Action<bool> callback = null)
|
||
{
|
||
if (!CheckConnection())
|
||
{
|
||
callback?.Invoke(false);
|
||
return;
|
||
}
|
||
|
||
SetState(OTAState.Ending);
|
||
|
||
// 数据: 总大小(4) + CRC16(2)
|
||
byte[] data = new byte[6];
|
||
BitConverter.GetBytes((uint)_firmwareData.Length).CopyTo(data, 0);
|
||
ushort crc16 = CalculateCRC16(_firmwareData);
|
||
BitConverter.GetBytes(crc16).CopyTo(data, 4);
|
||
|
||
var frame = new BLEFrame
|
||
{
|
||
Header1 = BLEConstants.FRAME_HEADER_1,
|
||
Header2 = BLEConstants.FRAME_HEADER_2,
|
||
Command = BLEConstants.CMD_OTA_END_TRANSFER,
|
||
ReadWrite = BLEConstants.RW_WRITE,
|
||
Length = (byte)data.Length,
|
||
Data = data
|
||
};
|
||
|
||
SendFrame(frame, (response) =>
|
||
{
|
||
bool success = response.IsSuccess;
|
||
if (success)
|
||
{
|
||
SetState(OTAState.Completed);
|
||
}
|
||
else
|
||
{
|
||
SetState(OTAState.Error);
|
||
}
|
||
callback?.Invoke(success);
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消升级 (命令 0x85)
|
||
/// </summary>
|
||
public void CancelUpgrade(Action<bool> callback = null)
|
||
{
|
||
if (!CheckConnection())
|
||
{
|
||
callback?.Invoke(false);
|
||
return;
|
||
}
|
||
|
||
SetState(OTAState.Cancelling);
|
||
|
||
// 数据: 取消标志(1)
|
||
byte[] data = new byte[] { 0x01 };
|
||
|
||
var frame = new BLEFrame
|
||
{
|
||
Header1 = BLEConstants.FRAME_HEADER_1,
|
||
Header2 = BLEConstants.FRAME_HEADER_2,
|
||
Command = BLEConstants.CMD_OTA_CANCEL,
|
||
ReadWrite = BLEConstants.RW_WRITE,
|
||
Length = (byte)data.Length,
|
||
Data = data
|
||
};
|
||
|
||
SendFrame(frame, (response) =>
|
||
{
|
||
bool success = response.IsSuccess;
|
||
if (success)
|
||
{
|
||
SetState(OTAState.Idle);
|
||
_firmwareData = null;
|
||
}
|
||
else
|
||
{
|
||
SetState(OTAState.Error);
|
||
}
|
||
OnCancelResult?.Invoke(success);
|
||
callback?.Invoke(success);
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 完整升级流程
|
||
|
||
/// <summary>
|
||
/// 执行完整OTA升级流程
|
||
/// </summary>
|
||
public IEnumerator PerformFullUpgrade(byte[] firmwareData, uint firmwareVersion, Action<bool> callback = null)
|
||
{
|
||
// 0. 先取消可能存在的未完成升级
|
||
bool cancelSuccess = false;
|
||
yield return CancelUpgradeCoroutine((success) => cancelSuccess = success);
|
||
if (cancelSuccess)
|
||
{
|
||
yield return new WaitForSeconds(1f);
|
||
}
|
||
|
||
// 1. 查询版本信息
|
||
OTAVersionInfo versionInfo = null;
|
||
yield return QueryVersionInfoCoroutine((info) => versionInfo = info);
|
||
|
||
if (versionInfo == null)
|
||
{
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
// 2. 开始升级
|
||
bool startSuccess = false;
|
||
yield return StartUpgradeCoroutine(firmwareData, firmwareVersion, (success) => startSuccess = success);
|
||
|
||
if (!startSuccess)
|
||
{
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
// 3. 传输数据
|
||
bool transferSuccess = false;
|
||
yield return TransferFirmware((success) => transferSuccess = success);
|
||
|
||
if (!transferSuccess)
|
||
{
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
// 4. 结束传输
|
||
bool endSuccess = false;
|
||
yield return EndTransferCoroutine((success) => endSuccess = success);
|
||
|
||
if (!endSuccess)
|
||
{
|
||
callback?.Invoke(false);
|
||
yield break;
|
||
}
|
||
|
||
callback?.Invoke(true);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
private bool CheckConnection()
|
||
{
|
||
if (BLECommunicationManager.Instance == null) return false;
|
||
if (BluetoothManager.Instance == null || !BluetoothManager.Instance.IsConnected) return false;
|
||
return true;
|
||
}
|
||
|
||
private void SetState(OTAState newState)
|
||
{
|
||
if (CurrentState != newState)
|
||
{
|
||
CurrentState = newState;
|
||
OnStateChanged?.Invoke(newState);
|
||
}
|
||
}
|
||
|
||
private void SendFrame(BLEFrame frame, Action<BLEResponse> callback)
|
||
{
|
||
BLECommunicationManager.Instance.SendFrameInternal(frame, callback);
|
||
}
|
||
|
||
private IEnumerator SendFrameCoroutine(BLEFrame frame, Action<BLEResponse> callback)
|
||
{
|
||
bool completed = false;
|
||
BLEResponse response = new BLEResponse { Status = BLEConstants.STATUS_DEVICE_ERROR };
|
||
|
||
SendFrame(frame, (resp) =>
|
||
{
|
||
response = resp;
|
||
completed = true;
|
||
});
|
||
|
||
yield return new WaitUntil(() => completed);
|
||
callback?.Invoke(response);
|
||
}
|
||
|
||
private IEnumerator QueryVersionInfoCoroutine(Action<OTAVersionInfo> callback)
|
||
{
|
||
OTAVersionInfo info = null;
|
||
QueryVersionInfo((result) => info = result);
|
||
yield return new WaitUntil(() => CurrentState != OTAState.QueryingVersion || info != null);
|
||
callback?.Invoke(info);
|
||
}
|
||
|
||
private IEnumerator StartUpgradeCoroutine(byte[] firmwareData, uint firmwareVersion, Action<bool> callback)
|
||
{
|
||
bool completed = false;
|
||
bool success = false;
|
||
|
||
StartUpgrade(firmwareData, firmwareVersion, (result) =>
|
||
{
|
||
success = result;
|
||
completed = true;
|
||
});
|
||
|
||
yield return new WaitUntil(() => completed);
|
||
callback?.Invoke(success);
|
||
}
|
||
|
||
private IEnumerator CancelUpgradeCoroutine(Action<bool> callback)
|
||
{
|
||
bool completed = false;
|
||
bool success = false;
|
||
|
||
CancelUpgrade((result) =>
|
||
{
|
||
success = result;
|
||
completed = true;
|
||
});
|
||
|
||
yield return new WaitUntil(() => completed);
|
||
callback?.Invoke(success);
|
||
}
|
||
|
||
private IEnumerator EndTransferCoroutine(Action<bool> callback)
|
||
{
|
||
bool completed = false;
|
||
bool success = false;
|
||
|
||
EndTransfer((result) =>
|
||
{
|
||
success = result;
|
||
completed = true;
|
||
});
|
||
|
||
yield return new WaitUntil(() => completed);
|
||
callback?.Invoke(success);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算CRC16校验
|
||
/// </summary>
|
||
private ushort CalculateCRC16(byte[] data)
|
||
{
|
||
ushort crc = 0xFFFF;
|
||
for (int i = 0; i < data.Length; i++)
|
||
{
|
||
crc ^= (ushort)(data[i] << 8);
|
||
for (int j = 0; j < 8; j++)
|
||
{
|
||
if ((crc & 0x8000) != 0)
|
||
crc = (ushort)((crc << 1) ^ 0x1021);
|
||
else
|
||
crc <<= 1;
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
private void Log(string message)
|
||
{
|
||
Debug.Log($"[OTAManager] {message}");
|
||
}
|
||
|
||
private void LogError(string message)
|
||
{
|
||
Debug.LogError($"[OTAManager] {message}");
|
||
}
|
||
|
||
#endregion
|
||
}
|