You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

616 lines
23 KiB

//
// FRIUpdateOTAManager.m
// FRQBluetoothKit
//
// Created by chunhai xu on 2021/1/3.
//
#import "FRIUpdateOTAManager.h"
#import "FRIConvert.h"
#import "FRBleAbility.h"
#import "FRIBinFileCheck.h"
#import "FRIDefine.h"
#import "BluetoothFireBoltt.h"
#import "FireBoltt-Swift.h"
#define kFRQCmdHeadLen 3 //cmd head = 3
#define kFRQRspHeadLen 4 //rsp head = 4
#define kFRQBaseAddressResultLen 4 //base addr len 4
#define kFRQUpdateAddressLen 4
#define kFRQRspResultCodeLen 1 //response result head code
#define ERASE_PAGE_SIZE 4096 //erase out page offset
#define BLE_SEND_MAX_LEN 20 //最大发送20字节
#define OTA_SUBPACKAGE_LEN 235 //ota subpackage len 235
// 计时器
#define FRIConnectSuccessNotify @"FRIUpdateOTAManager_connectSuccessNotify"
#define FRIHandlerTimeOutSuccess @"FRIUpdateOTAManager_handlerTimeOutSuccess"
typedef NS_ENUM(NSUInteger, FRQRspHeadCode) {
FRQRspHeadCodeOk = 0, //ok
FRQRspHeadCodeFail = 1, //fail
};
//响应数据
@interface FRITrunk : NSObject
@property(nonatomic, assign) uint8_t rsp_code; //response code 0: ok; 1: fail
@property(nonatomic, assign) uint32_t base_addr; //base address
@property(nonatomic, assign) uint32_t erase_base_addr; //erase base address
@property(nonatomic, strong) NSData *rsp_data; //rsp data
@property(nonatomic, assign) uint32_t update_addr; //ota update base address
@end
@implementation FRITrunk
@end
@interface FRIUpdateOTAManager ()
@property(nonatomic, assign, readwrite) FRIOTAStatus otaStatus; //!< ota操作状态
@property(nonatomic, strong, readwrite) CBPeripheral *curPeripheral; //蓝牙外设
@property(nonatomic, strong) FRITrunk *otaTrunk; //ota trunk
@property(nonatomic, strong) CBCharacteristic *writeCharacteristic; //写特征值
//@property(nonatomic, strong) CBCharacteristic *readCharacteristic; //读特征值
@end
@implementation FRIUpdateOTAManager
- (instancetype)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readCharacterNotify:) name:FRQReadCharacteristicUUIDNotify object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectSuccessNotify) name:BluetoothNotificationAtConnectSuccess object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disconnectNotify) name:BluetoothNotificationAtDisconnect object:nil];
}
return self;
}
static NSThread *shareThread = nil;
#pragma mark -- thread
- (NSThread *)otaThread {
@synchronized (self) {
if (!shareThread) {
shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(onOTAThreadStart) object:nil];
shareThread.name = @"com.frq.ota.thread";
}
}
return shareThread;
}
-(void)onOTAThreadStart{
@autoreleasepool {
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//保持循环执行
while ((self.otaStatus != FRIOTAStatusCanceled && self.otaStatus != FRIOTAStatusFinish && self.otaStatus !=FRIOTAStatusFailure)
&& [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
shareThread = nil;
}
}
#pragma mark -- getter and setter
- (FRITrunk *)otaTrunk{
if (!_otaTrunk) {
_otaTrunk = [[FRITrunk alloc] init];
}
return _otaTrunk;
}
-(void)resetOTAStatus{
self.otaStatus = FRIOTAStatusNotStart;
}
//写数据
-(void)_writeDataToPeripheral:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic
value:(NSData *)data{
//开始处理交互数据
if (!self.writeCharacteristic) {
[self postErrorWithCode:FRQErrorCode_connect msg:@"当前未找到蓝牙读写设备!"];
return;
}
FRILogS(@"😀😀😀😀send data<%@> = %@ to <%@>!!",@(data.length),[FRIConvert prexHexStrFromData:data], characteristic.UUID.UUIDString);
//这是一个NS_OPTIONS,就是可以同时用于好几个值,常见的有read,write,notify,indicate,知知道这几个基本就够用了,前连个是读写权限,后两个都是通知,两种不同的通知方式。
/*
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
};
*/
//只有 characteristic.properties 有write的权限才可以写
if(characteristic.properties & CBCharacteristicPropertyWrite){
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
else if (characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
}
else{
FRILogS(@"该字段不可写!");
}
}
//分包发送蓝牙数据
-(void)sendMsgWithSubPackage:(NSData*)msgData
Peripheral:(CBPeripheral*)peripheral
Characteristic:(CBCharacteristic*)character
{
for (int i = 0; i < msgData.length; i += BLE_SEND_MAX_LEN) {
NSData *subData = nil;
// 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小
if ((i + BLE_SEND_MAX_LEN) < [msgData length]) {
subData = [msgData subdataWithRange:NSMakeRange(i, BLE_SEND_MAX_LEN)];
}
else {
subData = [msgData subdataWithRange:NSMakeRange(i, [msgData length] - i)];
}
[self _writeDataToPeripheral:peripheral
characteristic:character
value:subData];
}
}
#pragma mark -- build head cmd
-(NSData *)buildCMDHeaderDataWithOpcode:(NSString *)opcodeHex len:(NSString *)lenHex {
NSMutableData *headData = [NSMutableData data];
NSData *opcodeData = [FRIConvert prexHexStrToData:opcodeHex];
[headData appendBytes:opcodeData.bytes length:1];
//大小端数据转化
NSString *bigEndianStr = [FRIConvert convertHexEndianToEndian:lenHex];
NSData *bigEndianData = [FRIConvert prexHexStrToData:bigEndianStr];
[headData appendBytes:bigEndianData.bytes length:2];
return headData;
}
-(NSData *)buildCmdDataForBaseAddress{
NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x01" len:@"0x0000"];
//build
NSMutableData *cmdData = [NSMutableData dataWithLength:kFRQCmdHeadLen + 6];
[cmdData replaceBytesInRange:NSMakeRange(0, kFRQCmdHeadLen) withBytes:cmdHead.bytes];//replace 3 bytes
return cmdData;
}
-(NSData *)buildCmdDataForErase{
NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x03" len:@"0x0004"];
NSMutableData *cmdData = [NSMutableData dataWithLength:kFRQCmdHeadLen + 4];
[cmdData replaceBytesInRange:NSMakeRange(0, kFRQCmdHeadLen) withBytes:cmdHead.bytes];//replace 3 bytes
//base address
NSString *hex = [FRIConvert noPrexHexStrFromDecimal:self.otaTrunk.erase_base_addr];
NSString *newHexStr = [FRIConvert addString:@"0" length:4*2 onString:hex]; //补位
//数据大小端转化
NSString *bigEndianStr = [FRIConvert convertHexEndianToEndian:newHexStr];
NSData *baseAddrData = [FRIConvert prexHexStrToData:bigEndianStr];
[cmdData replaceBytesInRange:NSMakeRange(kFRQCmdHeadLen, 4) withBytes:baseAddrData.bytes];
return cmdData;
}
-(NSData *)buildCmdUpdateData:(NSData *)subPackage fromAddr:(NSString *)addressHex{
NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x05" len:@"0x0241"];
NSMutableData *cmdData = [NSMutableData data];
[cmdData appendData:cmdHead]; //add head
//base addr
NSData *baseAddrData = [FRIConvert prexHexStrToData:addressHex];
[cmdData appendBytes:baseAddrData.bytes length:kFRQUpdateAddressLen]; // 4 bytes
//content len
NSString *lenHexStr = [FRIConvert noPrexHexStrFromDecimal:235];
NSString *newHexStr = [FRIConvert addString:@"0" length:2*2 onString:lenHexStr]; //补位
NSString *bigEndianLenStr = [FRIConvert convertHexEndianToEndian:newHexStr];
NSData *contentLenData = [FRIConvert prexHexStrToData:bigEndianLenStr];
[cmdData appendBytes:contentLenData.bytes length:2]; //2 bytes
//content
[cmdData appendData:subPackage]; //package data
return cmdData;
}
-(NSData *)buildRebootCmdData{
// NSData *cmdHead = [self buildCMDHeaderDataWithOpcode:@"0x09" len:@"0x0000"];
//此处需要特殊处理
uint32_t crc32 = [FRIBinFileCheck crc32ForBinFile:self.binData];
uint32_t length = (uint32_t)self.binData.length;
Byte rebootCmd[11];
rebootCmd[0] = (Byte) (9 & 0xff);
rebootCmd[1] = 0xa;
rebootCmd[2] = 0x00;
rebootCmd[3] = (Byte) (length & 0xff);
rebootCmd[4] = (Byte) ((length & 0xff00) >> 8);
rebootCmd[5] = (Byte) ((length & 0xff0000) >> 16);
rebootCmd[6] = (Byte) ((length & 0xff000000) >> 24);
rebootCmd[7] = (Byte) (crc32 & 0xff);
rebootCmd[8] = (Byte) ((crc32 & 0xff00) >> 8);
rebootCmd[9] = (Byte) ((crc32 & 0xff0000) >> 16);
rebootCmd[10] = (Byte) ((crc32 & 0xff000000) >> 24);
// [cmdData replaceBytesInRange:NSMakeRange(0, kFRQCmdHeadLen) withBytes:cmdHead.bytes]; //replace 3 bytes
return [NSData dataWithBytes:rebootCmd length:11];
}
#pragma mark --check bin data
#define FRICHECKHEXSTR @"0x0167"
#define FRIFlAGHEXSTR @"0x51525251"
-(BOOL)checkBinValidate:(NSData *)binData{
//data empty
if (binData.length <= 0) {
return NO;
}
int flagByteSize = 4;
//convert 16 to 10
uint32_t checkByteIndex = 0;
NSScanner* scanner = [NSScanner scannerWithString:FRICHECKHEXSTR];
[scanner scanHexInt:&checkByteIndex];
if (binData.length > checkByteIndex + flagByteSize) {
//read last 4 Bytes data
NSData *flagData = [binData subdataWithRange:NSMakeRange(checkByteIndex, flagByteSize)];
NSString *flagHexStr = [FRIConvert prexHexStrFromData:flagData];
if ([flagHexStr isEqualToString:FRIFlAGHEXSTR]) { //check flag byte
//bin is validate
return YES;
}
}
return NO;
}
#pragma mark -- handle response data
- (void)_sendOTARebootCmd {
NSData *rebootData = [self buildRebootCmdData];
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:rebootData];
}
- (void)_sendOTAFileDataCmd {
NSData *packageData = nil;
// 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小
int fromDataOffset = self.otaTrunk.update_addr - self.otaTrunk.base_addr;
if (self.otaTrunk.update_addr+OTA_SUBPACKAGE_LEN < self.otaTrunk.base_addr + self.binData.length) {
packageData = [self.binData subdataWithRange:NSMakeRange(fromDataOffset, OTA_SUBPACKAGE_LEN)];
}
else {
packageData = [self.binData subdataWithRange:NSMakeRange(fromDataOffset, self.otaTrunk.base_addr + self.binData.length - self.otaTrunk.update_addr)];
}
NSString *baseAddrHexStr = [FRIConvert noPrexHexStrFromDecimal:self.otaTrunk.update_addr];
NSString *newHexStr = [FRIConvert addString:@"0" length:kFRQUpdateAddressLen*2 onString:baseAddrHexStr]; //补位
//数据大小端转化
NSString *bigEndianStr = [FRIConvert convertHexEndianToEndian:newHexStr];
NSData *otaPackageData = [self buildCmdUpdateData:packageData fromAddr:bigEndianStr];
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:otaPackageData];
self.otaTrunk.update_addr += OTA_SUBPACKAGE_LEN;
}
- (void)_sendOTAEraseCmd {
NSData *eraseCmd = [self buildCmdDataForErase];
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:eraseCmd];
self.otaTrunk.erase_base_addr += ERASE_PAGE_SIZE;
}
- (void)_sendBaseAddrCmd {
NSData *baseAddrCmd = [self buildCmdDataForBaseAddress];
[self _writeDataToPeripheral:self.curPeripheral characteristic:self.writeCharacteristic value:baseAddrCmd];
}
/// 接受到OTA上报数据通知
- (void)readCharacterNotify:(NSNotification *)notify {
NSData *data = notify.object;
[self performSelector:@selector(_handleCharacteristicResponseData:) onThread:[self otaThread] withObject:data waitUntilDone:NO];
}
/// 连接成功
- (void)connectSuccessNotify {
if (self.otaStatus != FRIOTAStatusReboot) {
return;
}
FRILogS(@"[FRQOTA] connectSuccessNotify");
__weak typeof(self) weakSelf = self;
[GCDTimer.ocShareInstance scheduledDispatchTimerWithTimerName:FRIConnectSuccessNotify timeInterval:2.0 queue:dispatch_get_main_queue() repeats:false immediately:false action:^{
if (weakSelf.otaStatus != FRIOTAStatusReboot) {
return;
}
// 判断版本号,确认是否升级完成
NSString *currenVersion = [GlobalDeviceProfileModel.ocShareInstance currenVersion];
FRILogS(@"[FRQOTA] connectSuccessNotify currenVersion:%@ updateVersion:%@",currenVersion,self.updateVersion);
if ([currenVersion isEqualToString:self.updateVersion]) {
//completion
weakSelf.otaStatus = FRIOTAStatusFinish;
[weakSelf postOTAUpdateProgressChanged:100.0];
FRILogS(@"[FRQOTA] connectSuccessNotify 🏆🏆 重启完成 !!");
dispatch_async(dispatch_get_main_queue(), ^{
if ([weakSelf.delegate respondsToSelector:@selector(onOTAUpdateStatusCompletion:)]) {
[weakSelf.delegate onOTAUpdateStatusCompletion:weakSelf];
}
});
}
}];
}
/// 断开连接
- (void)disconnectNotify {
if (self.otaStatus == FRIOTAStatusNotStart ||
self.otaStatus == FRIOTAStatusGetBaseAddr ||
self.otaStatus == FRIOTAStatusEraseOut ||
self.otaStatus == FRIOTAStatusFileTransform ||
self.otaStatus == FRIOTAStatusNotStart ||
self.otaStatus == FRIOTAStatusCanceled) {
[self postErrorWithCode:FRQErrorCode_connect msg:@"升级过程断开连接"];
}
}
-(void)_handleCharacteristicResponseData:(NSData *)rspData{
FRILogS(@"[FRQOTA] >>> handle rsp hexString = %@ in thread: %@!!!",[FRIConvert prexHexStrFromData:rspData],[NSThread currentThread].name);
//base address data
if (rspData.length >= kFRQRspHeadLen + kFRQBaseAddressResultLen && (self.otaStatus == FRIOTAStatusGetBaseAddr || self.otaStatus == FRIOTAStatusEraseOut || self.otaStatus == FRIOTAStatusFileTransform || self.otaStatus == FRIOTAStatusReboot)) {
self.otaTrunk.rsp_data = [rspData subdataWithRange:NSMakeRange(kFRQRspHeadLen, kFRQBaseAddressResultLen)];
NSData *newRsp = [rspData subdataWithRange:NSMakeRange(0, 1)];
FRQRspHeadCode resultCode = [FRIConvert decimalFromData:newRsp].integerValue;
if (resultCode == FRQRspHeadCodeFail) { //success
FRILogS(@"[FRQOTA] !!!!!!!!! %@ -> %@ !!!!!!",newRsp,[FRIConvert decimalFromData:newRsp]);
[self postErrorWithCode:FRQErrorCode_ota msg:@"response data code failed"];
return;
}
}
else{
self.otaTrunk.rsp_data = nil;
}
switch (self.otaStatus) {
case FRIOTAStatusNotStart:
{
[self postOTAUpdateProgressChanged:5.0];
FRILogS(@"[FRQOTA] 🏆🏆 开始获取基地址操作 !!");
[self _sendBaseAddrCmd];
self.otaStatus = FRIOTAStatusGetBaseAddr;
}
break;
case FRIOTAStatusGetBaseAddr://step 1 get baseAddr
{
//基地址进行大端转小端
NSString *bigEndianHex = [FRIConvert noPrexHexStrFromData:self.otaTrunk.rsp_data];
NSString *fillEndianHexStr = [FRIConvert addString:@"0" length:kFRQBaseAddressResultLen*2 onString:bigEndianHex];
NSString *littleEndianHex = [FRIConvert convertHexEndianToEndian:fillEndianHexStr];
self.otaTrunk.base_addr = (uint32_t)[FRIConvert decimalFromHexStr:littleEndianHex].integerValue;
self.otaTrunk.erase_base_addr = self.otaTrunk.base_addr;
self.otaTrunk.update_addr = self.otaTrunk.base_addr;
[self postOTAUpdateProgressChanged:10.0];
FRILogS(@"[FRQOTA] 🥇 已获取基地址: %@",littleEndianHex);
self.otaStatus = FRIOTAStatusEraseOut;
//send erase cmd
FRILogS(@"[FRQOTA] 🏆🏆 开始擦除操作 !!");
[self _sendOTAEraseCmd];
}
break;
case FRIOTAStatusEraseOut: //step 2 erase data
{
if(self.otaTrunk.erase_base_addr < self.otaTrunk.base_addr + self.binData.length) {
float eraseProgress = (float)self.otaTrunk.erase_base_addr/(self.otaTrunk.base_addr + self.binData.length);
[self postOTAUpdateProgressChanged:eraseProgress * 20.0 + 10.0];
FRILogS(@"🥈 开始擦除地址: %@, 进度 %.2f",[FRIConvert noPrexHexStrFromDecimal:self.otaTrunk.erase_base_addr],eraseProgress);
[self _sendOTAEraseCmd]; //每次擦除后增加4096个偏移
}
else{
//发送文件传输操作
self.otaStatus =FRIOTAStatusFileTransform;
FRILogS(@"[FRQOTA] 🏆🏆 开始传输文件操作 !!");
[self _sendOTAFileDataCmd];
}
}
break;
case FRIOTAStatusFileTransform://step 3 transform file data
{
//分块传输
if (self.otaTrunk.update_addr < self.otaTrunk.base_addr + self.binData.length) {
FRILogS(@"[FRQOTA] 🥉 已传输文件传输大小 %d byte, bin文件总大小 %ld ",self.otaTrunk.update_addr - self.otaTrunk.base_addr,self.binData.length);
float transformProgress = (float)(self.otaTrunk.update_addr - self.otaTrunk.base_addr)/self.binData.length;
[self postOTAUpdateProgressChanged:transformProgress * 60.0 + 30.0];
[self _sendOTAFileDataCmd];
}
else{
self.otaStatus = FRIOTAStatusReboot;
FRILogS(@"[FRQOTA] 🏆🏆 开始重启操作 !!");
[self postOTAUpdateProgressChanged:100.0];
//send reboot cmd
[self _sendOTARebootCmd];
// 升级成功后立即重连会出现断开,因此重连延时
BluetoothFireBoltt.shareInstance.reconnectTime = 3;
// 容错处理,超过15s后未重连判定为升级成功
__weak typeof(self) weakSelf = self;
[GCDTimer.ocShareInstance scheduledDispatchTimerWithTimerName:FRIHandlerTimeOutSuccess timeInterval:15.0 queue:dispatch_get_main_queue() repeats:false immediately:false action:^{
if (weakSelf.otaStatus != FRIOTAStatusReboot) {
return;
}
FRILogS(@"[FRQOTA] FRIHandlerTimeOutSuccess 超时未重连判断升级成功");
weakSelf.otaStatus = FRIOTAStatusFinish;
[weakSelf postOTAUpdateProgressChanged:100.0];
dispatch_async(dispatch_get_main_queue(), ^{
if ([weakSelf.delegate respondsToSelector:@selector(onOTAUpdateStatusCompletion:)]) {
[weakSelf.delegate onOTAUpdateStatusCompletion:weakSelf];
}
});
}];
}
}
break;
case FRIOTAStatusReboot: //step 4 reboot
{
//completion
self.otaStatus = FRIOTAStatusFinish;
[self postOTAUpdateProgressChanged:100.0];
FRILogS(@"[FRQOTA] 🏆🏆 重启完成 !!");
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStatusCompletion:)]) {
[self.delegate onOTAUpdateStatusCompletion:self];
}
});
}
break;
default:
break;
}
}
#pragma mark --- 错误处理
-(void)postErrorWithCode:(FRQErrorCode)errCode msg:(NSString *)errMsg{
FRILogS(@"[FRQOTA] postErrorWithCode errCode:%ld errMsg:%@",errCode,errMsg);
self.otaStatus = FRIOTAStatusFailure;
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStatusFailure:error:)]) {
[self.delegate onOTAUpdateStatusFailure:self error:[self errorWithCode:errCode msg:errMsg]];
}
});
}
-(NSError *)errorWithCode:(NSInteger)code msg:(NSString *)errMsg{
NSString *localDescriptionStr = [NSString stringWithFormat:@"Code: %@, %@",@(code),errMsg];
return [NSError errorWithDomain:errMsg code:code userInfo:@{NSLocalizedFailureReasonErrorKey:errMsg,NSLocalizedDescriptionKey:localDescriptionStr}];
}
-(void)postOTAUpdateProgressChanged:(float)progress{
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStatusDidChange:withProgress:)]) {
[self.delegate onOTAUpdateStatusDidChange:self withProgress:progress];
}
});
}
#pragma mark -- public
-(void)startUpdateOTA:(CBPeripheral *)peripheral writeCharacteristic:(CBCharacteristic *)writeCharacteristic {
self.writeCharacteristic = writeCharacteristic;
self.otaStatus = FRIOTAStatusNotStart;
self.curPeripheral = peripheral;
if ([self.delegate respondsToSelector:@selector(onOTAUpdateStart:)]) {
[self.delegate onOTAUpdateStart:self];
}
//启动线程
if (![self otaThread].isExecuting) {
[[self otaThread] start];
}
//获取详情
[self performSelector:@selector(_handleCharacteristicResponseData:) onThread:[self otaThread] withObject:nil waitUntilDone:NO];
}
-(void)cancelOTAUpdate{
if (self.otaStatus != FRIOTAStatusNotStart
&& self.otaStatus != FRIOTAStatusFailure
&& self.otaStatus != FRIOTAStatusCanceled
&& self.otaStatus != FRIOTAStatusFinish) {
self.otaStatus = FRIOTAStatusCanceled; //状态设置后otaThread线程退出
}
}
@end