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