// // JL_RunSDK.m // JL_OTA_InnerBle // // Created by 凌煊峰 on 2021/10/9. // #import "JL_RunSDK.h" #import "BluetoothFireBoltt.h" #import @interface JL_RunSDK() @property (strong, nonatomic) NSString *selectedOtaFilePath; /// 手表表盘资源文件 @property (nonatomic ,strong) NSArray *mWatchList; /// 是否正在升级 @property (nonatomic ,assign) BOOL isOtaUpdate; /// <#arguments#> @property (nonatomic ,strong) NSString *mOtaPath; @property (nonatomic ,strong) NSString *otaString; @property (nonatomic ,strong) JL_Timer *mTimer; @end @implementation JL_RunSDK static JL_RunSDK *singleton = nil; + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleton = [[self alloc] init]; }); return singleton; } - (instancetype)init { self = [super init]; if (self) { self.bt_ble = [[BluetoothFireBoltt alloc] init]; self.mTimer = [[JL_Timer alloc] init]; } return self; } +(BOOL)isConnectDevice { JL_RunSDK *bleSDK = [JL_RunSDK sharedInstance]; BluetoothFireBoltt *bt_ble = bleSDK.bt_ble; /*--- 判断是否已连接设备 ---*/ if (bt_ble.currenModel == nil) { [SVProgressHUD showErrorWithStatus:@"请先连设备!"]; // UIWindow *win = [DFUITools getWindow]; // [DFUITools showText: onView:win delay:1.0]; return NO; } return YES; } +(BOOL)isNeedUpdateResource{ /*--- 设备信息 ---*/ JL_ManagerM *mCmdManager = singleton.bt_ble.mAssist.mCmdManager; JLModel_Device *model = [mCmdManager outputDeviceModel]; /*--- BLE是否需要更新资源 ---*/ if (model.otaWatch == JL_OtaWatchYES) { [SVProgressHUD showErrorWithStatus:@"需要升级手表!"]; // UIWindow *win = [DFUITools getWindow]; // [DFUITools showText:@"需要升级手表!" onView:win delay:1.0]; return YES; } return NO; } +(BOOL)isNeedUpdateResource_1{ /*--- 设备信息 ---*/ JL_ManagerM *mCmdManager = singleton.bt_ble.mAssist.mCmdManager; JLModel_Device *model = [mCmdManager outputDeviceModel]; /*--- BLE是否需要更新资源 ---*/ if (model.otaWatch == JL_OtaWatchYES) { return YES; } return NO; } +(BOOL)isNeedUpdateOTA{ /*--- 设备信息 ---*/ JL_ManagerM *mCmdManager = singleton.bt_ble.mAssist.mCmdManager; JLModel_Device *model = [mCmdManager outputDeviceModel]; /*--- BLE是否需要OTA升级 ---*/ if (model.otaStatus == JL_OtaStatusForce) { // UIWindow *win = [DFUITools getWindow]; // [DFUITools showText:@"需要升级手表!" onView:win delay:1.0]; [SVProgressHUD showErrorWithStatus:@"需要升级手表!"]; return YES; } return NO; } +(BOOL)isNeedUpdateOTA_1{ /*--- 设备信息 ---*/ JL_ManagerM *mCmdManager = singleton.bt_ble.mAssist.mCmdManager; JLModel_Device *model = [mCmdManager outputDeviceModel]; /*--- BLE是否需要OTA升级 ---*/ if (model.otaStatus == JL_OtaStatusForce) { return YES; } return NO; } - (void)otaFuncWithFilePath:(NSString *)otaFilePath { [[NSUserDefaults standardUserDefaults] setObject:otaFilePath forKey:@"OTAPATH"]; self.mOtaPath = otaFilePath; NSString *lastZip = [[self.mOtaPath lastPathComponent] stringByReplacingOccurrencesOfString:@".zip" withString:@""]; NSString *lastPath = [JL_Tools listPath:NSDocumentDirectory MiddlePath:lastZip File:@""]; [JL_Tools removePath:lastPath]; // [self.mTimer threadContinue]; NSArray *zipArr = [FatfsObject unzipFileAtPath:self.mOtaPath toDestination:lastPath]; if (zipArr.count == 0) { [SVProgressHUD showErrorWithStatus:@"文件解压出错"]; return; } if (zipArr.count > 1) { [DialManager listFile:^(DialOperateType type, NSArray * _Nullable array) { if (type == DialOperateTypeSuccess && array.count > 0) { self.mWatchList = array; [JL_Tools subTask:^{ [self onUpdateResource:^{ [self onUpdateOTA]; }]; }]; } }]; } else { /*--- 只需要【OTA升级】 ---*/ [JL_Tools mainTask:^{ [self onUpdateOTA]; }]; } } - (void)onUpdateOTA { NSData *otaData = [self outputDataOfOtaPath:self.mOtaPath]; __weak typeof(self) weakSelf = self; [self.bt_ble.mAssist.mCmdManager.mOTAManager cmdOTAData:otaData Result:^(JL_OTAResult result, float progress) { if (result == JL_OTAResultUpgrading || result == JL_OTAResultPreparing) { NSString *txt = [NSString stringWithFormat:@"%.1f%%",progress*100.0f]; if (result == JL_OTAResultPreparing) NSLog(@"校验文件中 %@",txt); if (result == JL_OTAResultUpgrading) NSLog(@"正在升级 %@",txt); [self otaTimeCheck];//增加超时检测 } else if (result == JL_OTAResultPrepared) { NSLog(@"---> 检验文件【完成】"); [self otaTimeCheck];//增加超时检测 } else if (result == JL_OTAResultReconnect) { NSLog(@"---> OTA正在回连设备... "); [self otaTimeCheck];//关闭超时检测 } else if (result == JL_OTAResultReconnectWithMacAddr) { NSLog(@"---> OTA正在通过Mac Addr方式回连设备... "); [self otaTimeCheck];//关闭超时检测 } else if (result == JL_OTAResultSuccess) { NSLog(@"--->升级成功."); } else if (result == JL_OTAResultReboot) { NSLog(@"--->设备重启."); [self otaTimeClose];//关闭超时检测 } else if (result == JL_OTAResultFail) { [self otaTimeClose]; NSLog(@"--->升级失败"); }else { // 其余错误码详细 Command+点击JL_OTAResult 查看说明 NSLog(@"ota update result: %d", result); } if ([weakSelf.delegate respondsToSelector:@selector(otaProgressWithOtaResult:withProgress:)]) { [weakSelf.delegate otaProgressWithOtaResult:result withProgress:progress]; } }]; } - (NSData*)outputDataOfOtaPath:(NSString*)path { NSString *folderName = [[path lastPathComponent] stringByReplacingOccurrencesOfString:@".zip" withString:@""]; NSString *zipPath = [JL_Tools listPath:NSDocumentDirectory MiddlePath:folderName File:@""]; [FatfsObject unzipFileAtPath:path toDestination:zipPath]; NSArray *zipArray = [JL_Tools subPaths:zipPath]; for (NSString *name in zipArray) { if ([name hasSuffix:@".ufw"]) { NSString *otaPath = [JL_Tools listPath:NSDocumentDirectory MiddlePath:folderName File:name]; NSLog(@"---->Start OTA:%@ ",otaPath); NSData *otaData = [[NSData alloc] initWithContentsOfFile:otaPath]; return otaData; } else { } } return nil; } typedef void(^OTA_VIEW_BK)(void); -(void)onUpdateResource:(OTA_VIEW_BK __nullable)result { self.isOtaUpdate = YES; JL_ManagerM *mCmdManager = BluetoothFireBoltt.shareInstance.mAssist.mCmdManager; /*--- 更新资源标志 ---*/ [mCmdManager.mFlashManager cmdWatchUpdateResource]; /*--- 展示手表更新资源UI ---*/ NSLog(@"--->Fats Update UI.(OTA)"); __block uint8_t m_flag = 0; [mCmdManager.mFlashManager cmdUpdateResourceFlashFlag:JL_FlashOperateFlagStart Result:^(uint8_t flag) { m_flag = flag; }]; if (m_flag != 0) { [JL_Tools mainTask:^{ //[DFUITools showText:@"升级请求失败!" onView:self.view delay:1.0]; [SVProgressHUD showErrorWithStatus:NSLocalizedString(@"升级请求失败!", nil)]; }]; self.isOtaUpdate = NO; return; } [DialManager updateResourcePath:self.mOtaPath List:self.mWatchList Result:^(DialUpdateResult updateResult, NSArray * _Nullable array, NSInteger index, float progress){ [JL_Tools mainTask:^{ if (updateResult == DialUpdateResultReplace) { [self otaTimeCheck];//增加超时检测 NSString *fileName = array[index]; [SVProgressHUD showWithStatus:[NSString stringWithFormat:@"正在更新表盘:%@(%d/%lu)\n%.1f%%", fileName,(int)index+1,(unsigned long)array.count,progress*100.0]]; return; } if (updateResult == DialUpdateResultAdd) { [self otaTimeCheck];//增加超时检测 NSString *fileName = array[index]; [SVProgressHUD showWithStatus:[NSString stringWithFormat:@"正在传输新表盘%@(%d/%lu)\n%.1f%%", fileName,(int)index+1,(unsigned long)array.count,progress*100.0]]; return; } if (updateResult == DialUpdateResultFinished) self.otaString = @"资源更新完成"; if (updateResult == DialUpdateResultNewest) self.otaString = @"资源已是最新"; if (updateResult == DialUpdateResultInvalid) self.otaString = @"无效资源文件"; if (updateResult == DialUpdateResultEmpty) self.otaString = @"资源文件为空"; if (updateResult == DialUpdateResultNoSpace) self.otaString = @"资源升级空间不足"; if (updateResult == DialUpdateResultZipError) self.otaString = @"ZIP资源文件错误"; [SVProgressHUD showErrorWithStatus:self.otaString]; // [JL_Tools delay:1.0 Task:^{ // [SVProgressHUD showErrorWithStatus:NSLocalizedString(self.otaString, nil)]; // if (result) result(); // }]; }]; }]; } -(void)transportFile:(NSString*)path fileName:(NSString *)fileName Result:(void(^_Nullable)(JL_BigFileResult result, float progress))callback { JL_ManagerM *mCmdManager = BluetoothFireBoltt.shareInstance.mAssist.mCmdManager; ///设置需要Unicode命名⻓⽂件名 [mCmdManager.mFileManager setIsOtherEncode:true]; ///设置编码格式 [mCmdManager.mFileManager setEncodeType:NSUTF16LittleEndianStringEncoding]; JLModel_Device *deviceModel = [mCmdManager outputDeviceModel]; if (deviceModel.audioFileType == JL_AudioFileTypeYES) { } [JL_Tools mainTask:^{ [mCmdManager.mFileManager setCurrentFileHandleType:[self getMusicTargetDev:deviceModel]]; //设置设备的环境变量 [mCmdManager.mFileManager cmdPreEnvironment:0x00 Result:^(JL_CMDStatus status, uint8_t sn, NSData * _Nullable data) { if (status == JL_CMDStatusSuccess) { //大文件传输API [mCmdManager.mFileManager cmdBigFileData:path WithFileName:fileName Result:^(JL_BigFileResult result,float progress) { callback(result,progress); if (result == JL_BigFileTransferEnd) { NSLog(@"传输完成,清除播放列表缓存"); [mCmdManager.mFileManager cmdCleanCacheType:[self getCardTypeDev:deviceModel]]; // [self loadRootFiles]; } }]; } }]; // [JL_Tools delay:2.0 Task:^{ // [self stopTransport]; // }]; }]; } - (void)stopTransport { JL_ManagerM *mCmdManager = BluetoothFireBoltt.shareInstance.mAssist.mCmdManager; [mCmdManager.mFileManager cmdStopBigFileData]; } static NSTimer *otaTimer = nil; static int otaTimeout= 0; -(void)otaTimeCheck{ otaTimeout = 0; if (otaTimer == nil) { otaTimer = [JL_Tools timingStart:@selector(otaTimeAdd) target:self Time:1.0]; } } -(void)otaTimeClose{ [JL_Tools timingStop:otaTimer]; otaTimeout = 0; otaTimer = nil; } -(void)otaTimeAdd{ otaTimeout++; if (otaTimeout == 20) { self.isOtaUpdate = NO; [self otaTimeClose]; [SVProgressHUD showErrorWithStatus:NSLocalizedString(@"OTA升级超时", nil)]; NSLog(@"OTA ---> 超时了!!!"); // [self removeOTAzip]; } } /** * 获取音乐文件传输句柄 */ - (JL_FileHandleType)getMusicTargetDev:(JLModel_Device*)deviceModel { if ([deviceModel.cardArray containsObject:@(JL_CardTypeSD_1)]) { return JL_FileHandleTypeSD_1; } else if ([deviceModel.cardArray containsObject:@(JL_CardTypeSD_0)] && [deviceModel.cardArray containsObject:@(JL_CardTypeSD_1)]) { return JL_FileHandleTypeSD_1; } else if ([deviceModel.cardArray containsObject:@(JL_CardTypeSD_0)]) { return JL_FileHandleTypeSD_0; } else if ([deviceModel.cardArray containsObject:@(JL_CardTypeUSB)]) { return JL_FileHandleTypeUSB; } return JL_FileHandleTypeSD_1; } /** * 获取音乐文件传输句柄 */ - (JL_CardType)getCardTypeDev:(JLModel_Device*)deviceModel { if ([deviceModel.cardArray containsObject:@(JL_CardTypeSD_1)]) { return JL_CardTypeSD_1; } else if ([deviceModel.cardArray containsObject:@(JL_CardTypeSD_0)] && [deviceModel.cardArray containsObject:@(JL_CardTypeSD_1)]) { return JL_CardTypeSD_1; } else if ([deviceModel.cardArray containsObject:@(JL_CardTypeSD_0)]) { return JL_CardTypeSD_0; } else if ([deviceModel.cardArray containsObject:@(JL_CardTypeUSB)]) { return JL_CardTypeUSB; } return JL_CardTypeSD_1; } -(void)loadRootFiles { JL_ManagerM *mCmdManager = BluetoothFireBoltt.shareInstance.mAssist.mCmdManager; // JLModel_Device *deviceModel = [mCmdManager outputDeviceModel]; // // JLModel_File *fileModel = [JLModel_File new]; // fileModel.fileType = JL_BrowseTypeFolder; // fileModel.cardType = JL_CardTypeSD_1; // fileModel.fileHandle = deviceModel.handleSD_1; // fileModel.fileName = @"SD Card"; // fileModel.folderName = @"SD Card"; // fileModel.fileClus = 0; // // //读取目录 // [mCmdManager.mFileManager cmdBrowseModel:fileModel Number:10 Result:nil]; [mCmdManager cmdGetSystemInfo:JL_FunctionCodeCOMMON SelectionBit:0x04 Result:nil]; [mCmdManager.mFileManager cmdBrowseMonitorResult:^(NSArray * _Nullable array, JL_BrowseReason reason) { switch (reason) { case JL_BrowseReasonReading:{ NSLog(@"---> 正在读取:%@",array); }break; case JL_BrowseReasonCommandEnd:{ NSLog(@"读取命令结束:%lu",(unsigned long)array.count); }break; case JL_BrowseReasonFolderEnd:{ NSLog(@"---> 目录读取结束:%lu",(unsigned long)array.count); }break; case JL_BrowseReasonBusy:{ NSLog(@"设备在忙"); }break; case JL_BrowseReasonDataFail:{ NSLog(@"数据读取失败"); }break; case JL_BrowseReasonPlaySuccess:{ NSLog(@"---> 播放成功"); }break; case JL_BrowseReasonUnknown:{ NSLog(@"未知错误"); } default: break; } }]; } - (BOOL) checkIfImage:(UIImage *)someImage { CGImageRef image = someImage.CGImage; size_t width = CGImageGetWidth(image); size_t height = CGImageGetHeight(image); GLubyte * imageData = malloc(width * height * 4); int bytesPerPixel = 4; int bytesPerRow = bytesPerPixel * width; int bitsPerComponent = 8; CGContextRef imageContext = CGBitmapContextCreate( imageData, width, height, bitsPerComponent, bytesPerRow, CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big ); CGContextSetBlendMode(imageContext, kCGBlendModeCopy); CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image); CGContextRelease(imageContext); int byteIndex = 0; BOOL imageExist = NO; for ( ; byteIndex < width*height*4; byteIndex += 4) { CGFloat red = ((GLubyte *)imageData)[byteIndex]/255.0f; CGFloat green = ((GLubyte *)imageData)[byteIndex + 1]/255.0f; CGFloat blue = ((GLubyte *)imageData)[byteIndex + 2]/255.0f; CGFloat alpha = ((GLubyte *)imageData)[byteIndex + 3]/255.0f; if( red != 1 || green != 1 || blue != 1 || alpha != 1 ){ imageExist = YES; break; } } free(imageData); return imageExist; } @end