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.
1375 lines
52 KiB
1375 lines
52 KiB
|
|
// |
|
// Bluetooth.m |
|
// BluetoothDemo |
|
// |
|
// Created by mac on 17/3/9. |
|
// Copyright © 2017年 . All rights reserved. |
|
// |
|
|
|
#import "Bluetooth.h" |
|
#import "L2DataParse.h" |
|
#import <SVProgressHUD/SVProgressHUD.h> |
|
#import "NSString+methods.h" |
|
#import "BLETools.h" |
|
#import "BLEConst.h" |
|
#import "BLEBaseBl.h" |
|
#import "Lookfit-Swift.h" |
|
#import <CoreLocation/CoreLocation.h> |
|
#import "User_Http.h" |
|
|
|
|
|
NSString *kFLT_BLE_FOUND = @"FLT_BLE_FOUND"; //发现设备 |
|
NSString *kFLT_BLE_PAIRED = @"FLT_BLE_PAIRED"; //BLE已配对 |
|
NSString *kFLT_BLE_CONNECTED = @"FLT_BLE_CONNECTED"; //BLE已连接 |
|
NSString *kFLT_BLE_DISCONNECTED = @"FLT_BLE_DISCONNECTED"; //BLE断开连接 |
|
|
|
NSString *FLT_BLE_SERVICE = @"AE00"; //服务号 |
|
NSString *FLT_BLE_RCSP_W = @"AE01"; //命令“写”通道 |
|
NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道 |
|
|
|
// 超时间隔 |
|
#define TimeOutNumber 5 |
|
// 连接延时 |
|
#define ConnectPeripheralTimerName @"Bluetooth_ConnectPeripheralTimerName" |
|
#define ConnectPeripheralTimerName2 @"Bluetooth_ConnectPeripheralTimerName2" |
|
|
|
@interface Bluetooth () <CBPeripheralDelegate,CBCentralManagerDelegate>//CLLocationManagerDelegate |
|
|
|
@property (nonatomic, strong) CBPeripheralManager *peripheralManager; |
|
|
|
/** 蓝牙请求计数 */ |
|
@property (nonatomic, assign) NSInteger timeNumber; |
|
/** 是否正在执行 */ |
|
@property (nonatomic, assign) BOOL isExecuting; |
|
/** 超时定时器 */ |
|
@property (nonatomic, strong) NSTimer *checkoutTimer; |
|
/** 当前执行的命令 */ |
|
@property (nonatomic, strong) BlueToothItem *currenItem; |
|
/** 当前接收的数据 */ |
|
@property (nonatomic, strong) NSMutableData *currenData; |
|
/** 总数据包 */ |
|
@property (nonatomic, strong) NSMutableData *totalData; |
|
/** crc校验是否成功 */ |
|
@property (nonatomic, assign) BOOL isCrc; |
|
/** 防止手表多次调用特征值变化 */ |
|
@property (nonatomic, assign) BOOL isChangeCharacteristic; |
|
/** 重连外设 */ |
|
@property (nonatomic, strong) CBPeripheral *reconnectPeripheral; |
|
/** 是否DFU扫描 */ |
|
@property (nonatomic, assign) BOOL isDFUScan; |
|
|
|
//@property (nonatomic ,strong) CLLocationManager *locationManager; |
|
// |
|
//@property (nonatomic ,strong) CLBeaconRegion *beaconRegion; |
|
|
|
/** */ |
|
@property (nonatomic, assign) BOOL is_JL; |
|
|
|
|
|
/// 手表表盘资源文件 |
|
@property (nonatomic ,strong) NSArray *mWatchList; |
|
|
|
/// 是否正在升级 |
|
@property (nonatomic ,assign) BOOL isOtaUpdate; |
|
|
|
/// <#arguments#> |
|
@property (nonatomic ,strong) NSString *mOtaPath; |
|
|
|
@property (nonatomic ,strong) NSString *otaString; |
|
|
|
@end |
|
|
|
@implementation Bluetooth |
|
|
|
|
|
|
|
+ (nullable instancetype)shareInstance { |
|
static Bluetooth *share = nil; |
|
static dispatch_once_t oneToken; |
|
dispatch_once(&oneToken, ^{ |
|
share = [[self alloc] init]; |
|
}); |
|
return share; |
|
} |
|
|
|
- (id)init { |
|
self = [super init]; |
|
if (self) { |
|
self.isIndependentOff = NO; |
|
self.bleRequestSid = 0; |
|
self.bluetoothItems = [NSMutableArray array]; |
|
self.currenData = [NSMutableData new]; |
|
self.totalData = [NSMutableData new]; |
|
self.cMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey: @"myCentralManagerIdentifier"}]; |
|
self.isChangeCharacteristic = NO; |
|
self.is_JL = NO; |
|
|
|
|
|
self.mAssist = [[JL_Assist alloc] init]; |
|
self.mAssist.mNeedPaired = YES; //是否需要握手配对 |
|
self.mAssist.mPairKey = nil; //配对秘钥 |
|
self.mAssist.mService = FLT_BLE_SERVICE; //服务号 |
|
self.mAssist.mRcsp_W = FLT_BLE_RCSP_W; //特征「写」 |
|
self.mAssist.mRcsp_R = FLT_BLE_RCSP_R; //特征「读」 |
|
|
|
// 监听时间改变 |
|
[[NSNotificationCenter defaultCenter] addObserver:self |
|
selector:@selector(timeDidChanged) |
|
name:UIApplicationSignificantTimeChangeNotification |
|
object:nil]; |
|
} |
|
return self; |
|
} |
|
|
|
|
|
/** |
|
实时同步时间到设备 |
|
*/ |
|
- (void)timeDidChanged{ |
|
if ([[Bluetooth shareInstance] isConnected]) { |
|
// 延迟请求,防止时制切换未反应 |
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
|
[[[BLEBaseBl alloc] init] setIngTimeWithBleCmdType:BleCMD_setIngTime]; |
|
}); |
|
} |
|
// 更改了时间通知主要界面刷新界面,1为时间标示 |
|
[[NSNotificationCenter defaultCenter] postNotificationName:SycnDatas object:@(1)]; |
|
} |
|
|
|
/** |
|
扫描外设Peripherals |
|
|
|
@param serviceUUIDs 过滤的服务UUID |
|
@param isDFU 是否DFU升级使用 |
|
*/ |
|
- (void)scanForPeripheralsWithdiscoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs |
|
IsDFU:(BOOL)isDFU { |
|
// 停止扫描 |
|
[self cancelScan]; |
|
self.isDFUScan = isDFU; |
|
// 获取系统已配对的手环平台设备,存在则加入 |
|
NSMutableArray *connectedArray = [self getPairedConnectedArray]; |
|
for (BLEModel *bleModel in connectedArray) { |
|
[self.delegate onDiscoverToPeripheralsBleModel:bleModel]; |
|
} |
|
// DFU模式不使用uuid的筛选扫描 |
|
if (isDFU || [[NSUserDefaults standardUserDefaults] objectForKey:DFUMASString]) |
|
{ |
|
NSDictionary *options= [NSDictionary dictionaryWithObject:@NO forKey:CBCentralManagerScanOptionAllowDuplicatesKey]; |
|
[self.cMgr scanForPeripheralsWithServices:nil options:options]; |
|
return; |
|
} |
|
NSArray *uuids = @[[CBUUID UUIDWithString:BraceletServicesUUID]]; |
|
NSDictionary *scanForPeripheralsWithOptions = @{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}; |
|
[self.cMgr scanForPeripheralsWithServices:uuids options:scanForPeripheralsWithOptions]; |
|
} |
|
|
|
|
|
#pragma mark - coreBlueTooth --系统代理 |
|
|
|
- (void)centralManagerDidUpdateState:(CBCentralManager *)central |
|
{ |
|
self.state = central.state; |
|
[self.mAssist assistUpdateState:central.state]; |
|
if (central.state == CBManagerStatePoweredOn) |
|
{ |
|
// 蓝牙开始重连 |
|
[self startAndStopReconnect:YES]; |
|
} |
|
else if (central.state == CBManagerStatePoweredOff) |
|
{ |
|
[[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtPowerOff object:@"PowerOff"]; |
|
CGFloat systemVision = DEVICEVERSION; |
|
if (systemVision <10 || systemVision >= 11) { |
|
if (self.isConnected) { |
|
// 连接断开发出通知 |
|
self.isConnected = NO; |
|
[[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtDisconnect object:nil]; |
|
// 清空所有 |
|
self.writecharacteristic = nil; |
|
// 断开连接后处理,当前队列若存在则清空并且停止定时器 |
|
if (self.bluetoothItems.count) { |
|
[self.bluetoothItems removeAllObjects]; |
|
[self.checkoutTimer invalidate]; |
|
self.timeNumber = 0; |
|
_executing = NO; |
|
} |
|
} |
|
} |
|
[self startAndStopReconnect: NO]; |
|
} |
|
} |
|
|
|
//搜索蓝牙设备 |
|
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral |
|
advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI |
|
{ |
|
NSLog(@"[蓝牙通讯] 搜索到了设备:%@ \n设备UUID:%@ \n advertisementData:%@",peripheral.name, |
|
peripheral.identifier.UUIDString, |
|
advertisementData); |
|
NSString *ble_name = advertisementData[@"kCBAdvDataLocalName"]; |
|
|
|
NSString *MACString = [[NSString alloc] init]; |
|
NSData *manufacturerData = advertisementData[@"kCBAdvDataManufacturerData"]; |
|
if (manufacturerData && manufacturerData.length >= 8) |
|
{ |
|
NSData *MACData = [advertisementData[@"kCBAdvDataManufacturerData"] subdataWithRange:NSMakeRange(2, 6)]; |
|
MACString = [BluetoothTool getPositiveSequenceMacWithData:MACData]; |
|
} |
|
BLEModel *bleModel = [[BLEModel alloc] initWithCentral:central |
|
Peripheral:peripheral |
|
AdvertisementData:advertisementData |
|
RSSI:RSSI |
|
UUIDString:peripheral.identifier.UUIDString |
|
MACString:MACString]; |
|
|
|
// 未完成升级设备 || 需要重连的设备 |
|
if ((self.isDFUScan && [peripheral.name isEqual:@"DfuTarg"]) || [bleModel.MACString isEqualToString:[UserDefaultsManager getLocalDeviceMacID]]) { |
|
__weak typeof(self) weakSelf = self; |
|
[GCDTimer.ocShareInstance scheduledDispatchTimerWithTimerName:ConnectPeripheralTimerName2 timeInterval:self.reconnectTime queue:dispatch_get_main_queue() repeats:false immediately:false action:^{ |
|
[weakSelf connectedWithBleModel:bleModel]; |
|
weakSelf.reconnectTime = 0; |
|
}]; |
|
} |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(onDiscoverToPeripheralsBleModel:)]) |
|
{ |
|
[self.delegate onDiscoverToPeripheralsBleModel:bleModel]; |
|
} |
|
// ota升级过程,回连使用 |
|
if (manufacturerData != nil && self.is_JL) |
|
//if ([JL_BLEAction otaBleMacAddress:self.lastBleMacAddress isEqualToCBAdvDataManufacturerData:manufacturerData]) |
|
{ |
|
// self.is_JL = NO; |
|
[self connectedWithBleModel:bleModel]; |
|
} |
|
|
|
} |
|
|
|
|
|
// 蓝牙状态的保存和恢复,进入后台和重新启动有关 |
|
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict |
|
{ |
|
|
|
} |
|
|
|
// 已经连接外设 |
|
- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral |
|
{ |
|
NSLog(@"[蓝牙通讯] BLE Connected ---> Device %@", peripheral.name); |
|
peripheral.delegate = self; |
|
[peripheral discoverServices:nil]; |
|
[self cancelScan]; |
|
// [peripheral discoverServices:[NSArray arrayWithObject:[CBUUID UUIDWithString:ServicesUUID]]];//再去发现服务 |
|
// 若是Dfu模式外设则在此处发出连接成功的通知 |
|
if ([peripheral.name rangeOfString:@"DfuTarg"].location != NSNotFound) |
|
{ |
|
NSLog(@"DFUl连接成功!!!!"); |
|
self.isConnected = YES; |
|
[[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtDFUConnectSuccess object:nil]; |
|
} |
|
|
|
} |
|
// 连接外设失败 |
|
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error |
|
{ |
|
NSLog(@"[蓝牙通讯] 设备:%@--连接失败",peripheral.name); |
|
|
|
//if (self.isIndependentOff) |
|
[SVProgressHUD showInfoWithStatus:NSLocalizedString(@"连接失败", nil)]; |
|
// 连接失败发出通知 |
|
[[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtConnectFail object:peripheral.name]; |
|
self.isConnected = NO; |
|
//self.isRay = NO; |
|
} |
|
// 已经断开连接 |
|
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { |
|
NSLog(@"[蓝牙通讯] 设备:%@--断开连接",peripheral.name); |
|
|
|
/*--- JLSDK ADD ---*/ |
|
[self.mAssist assistDisconnectPeripheral:peripheral]; |
|
|
|
// 连接断开发出通知 |
|
self.isConnected = NO; |
|
//self.isRay = NO; |
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtDisconnect object:peripheral.name]; |
|
// 清空所有 |
|
self.writecharacteristic = nil; |
|
// 断开连接后处理,当前队列若存在则清空并且停止定时器 |
|
if (self.bluetoothItems.count) |
|
{ |
|
|
|
[self.bluetoothItems removeAllObjects]; |
|
[self.checkoutTimer invalidate]; |
|
self.timeNumber = 0; |
|
_executing = NO; |
|
} |
|
//if (!self.isIndependentOff) |
|
[self startAndStopReconnect:YES]; |
|
} |
|
|
|
|
|
// characteristic订阅状态改变 |
|
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error |
|
{ |
|
NSLog(@"[蓝牙通讯] //characteristic订阅状态改变的block---- %@",characteristic.value); |
|
// 判断是否监听设备广播通知成功 -->成功才发送连接成功通知 |
|
if (characteristic.isNotifying && [characteristic.UUID isEqual:[CBUUID UUIDWithString:NotifycharacteristicUUIDString]]) |
|
{ |
|
NSLog(@"[蓝牙通讯] 连接成功!!!!!!"); |
|
// 连接成功保存UUID,做重连使用 |
|
[[NSUserDefaults standardUserDefaults] setObject:peripheral.identifier.UUIDString forKey:AutomaticReconnectionDevice]; |
|
// 订阅了读数据才发出连接成功通知 |
|
self.isConnected = YES; |
|
self.reconnectPeripheral = peripheral; |
|
[[NSNotificationCenter defaultCenter] postNotificationName:BluetoothNotificationAtConnectSuccess object:self.currenModel]; |
|
} |
|
[self jlTimeClose]; |
|
self.is_JL = NO; |
|
__weak typeof(self) weakSelf = self; |
|
[self.mAssist assistUpdateCharacteristic:characteristic Peripheral:peripheral Result:^(BOOL isPaired) { |
|
if (isPaired == YES) |
|
{ |
|
[[NSUserDefaults standardUserDefaults] setObject:peripheral.identifier.UUIDString forKey:AutomaticReconnectionDevice]; |
|
NSLog(@"[蓝牙通讯] 配对成功"); |
|
self.reconnectPeripheral = peripheral; |
|
[JL_Tools post:kFLT_BLE_PAIRED Object:peripheral]; |
|
JL_EntityM *entity = [[JL_EntityM alloc] init]; |
|
[entity setBlePeripheral:peripheral]; |
|
self.mAssist.mCmdManager.mEntity = entity; |
|
weakSelf.lastBleMacAddress = nil; |
|
|
|
[JL_Tools delay:0.5 Task:^{ |
|
[self getInfo]; |
|
}]; |
|
} |
|
else |
|
{ |
|
[weakSelf.cMgr cancelPeripheralConnection:peripheral]; |
|
} |
|
}]; |
|
|
|
} |
|
|
|
|
|
|
|
//外设传来数据 |
|
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { |
|
// NSLog(@" 设备发过来的 characteristic name:%@ value is:%@",characteristic.UUID,characteristic.value); |
|
|
|
/*--- JLSDK ADD ---*/ |
|
[self.mAssist assistUpdateValueForCharacteristic:characteristic]; |
|
|
|
if (characteristic.value.length && [characteristic.UUID isEqual:[CBUUID UUIDWithString:NotifycharacteristicUUIDString]]) { |
|
// 接收到的数据处理 |
|
[self handleReceiveData:[characteristic value]]; |
|
} |
|
// FRQ OTA是否存在数据 |
|
if (characteristic.value.length && [characteristic.UUID isEqual:[CBUUID UUIDWithString:kFRQReadCharacteristicUUID]]) { |
|
[[NSNotificationCenter defaultCenter] postNotificationName:FRQReadCharacteristicUUIDNotify object:characteristic.value]; |
|
} |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(OnReadValueForCharacteristic:characteristicsL:)]) { |
|
[self.delegate OnReadValueForCharacteristic:peripheral characteristicsL:characteristic]; |
|
} |
|
|
|
|
|
} |
|
|
|
//发现服务 |
|
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error |
|
{ |
|
if (error) { NSLog(@"Err: Discovered services fail."); return; } |
|
|
|
for (CBService *service in peripheral.services) |
|
{ |
|
NSLog(@"[蓝牙通讯] BLE Service ---> %@", service.UUID.UUIDString); |
|
[peripheral discoverCharacteristics:nil forService:service]; |
|
} |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(onDiscoverServices:)]) |
|
{ |
|
[self.delegate onDiscoverServices:peripheral]; |
|
} |
|
|
|
|
|
} |
|
|
|
//发现特征值 |
|
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error |
|
{ |
|
[self.mAssist assistDiscoverCharacteristicsForService:service Peripheral:peripheral]; |
|
|
|
if (IsDelegate(self, OnDiscoverCharacteristics:Service:)) { |
|
[self.delegate OnDiscoverCharacteristics:peripheral Service:service]; |
|
} |
|
if ([service.UUID isEqual:[CBUUID UUIDWithString:ServicesUUID]]) { |
|
for (CBCharacteristic *characteristic in service.characteristics ) { |
|
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:NotifycharacteristicUUIDString]]) { |
|
[peripheral setNotifyValue:true forCharacteristic:characteristic]; |
|
} |
|
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:WirtecharacteristicUUIDString]]) { |
|
self.writecharacteristic = characteristic; |
|
} |
|
} |
|
} |
|
// FRQ OTA |
|
if ([service.UUID isEqual:[CBUUID UUIDWithString:kFRQServiceUUID]]) { |
|
for (CBCharacteristic *characteristic in service.characteristics ) { |
|
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kFRQReadCharacteristicUUID]]) { |
|
[peripheral setNotifyValue:true forCharacteristic:characteristic]; |
|
} |
|
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kFRQWriteCharacteristicUUID]]) { |
|
self.frqOTAwritecharacteristic = characteristic; |
|
} |
|
} |
|
} |
|
|
|
|
|
} |
|
|
|
//写入值的监听 |
|
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error { |
|
NSLog(@"写入成功"); |
|
} |
|
|
|
/** |
|
停止扫描 |
|
*/ |
|
- (void)cancelScan { |
|
[self.cMgr stopScan]; |
|
} |
|
|
|
//-(void)centralManager:(CBCentralManager *)central connectionEventDidOccur:(CBConnectionEvent)event |
|
// forPeripheral:(CBPeripheral *)peripheral |
|
//{ |
|
// if (event == CBConnectionEventPeerConnected) |
|
// { |
|
// NSLog(@"---> 系统界面,已连接设备:%@",peripheral.name); |
|
// if (peripheral.name.length > 0) { |
|
// [self connectBLE:peripheral]; |
|
// } |
|
// } |
|
// |
|
// if (event == CBConnectionEventPeerDisconnected) { |
|
// NSLog(@"---> 系统界面,断开设备:%@",peripheral.name); |
|
// } |
|
//} |
|
|
|
//-(void)connectBLE:(CBPeripheral*)peripheral |
|
//{ |
|
// self.reconnectPeripheral = peripheral; |
|
// [self.reconnectPeripheral setDelegate:self]; |
|
// |
|
// [self.cMgr connectPeripheral:self.reconnectPeripheral |
|
// options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES), |
|
// CBConnectPeripheralOptionNotifyOnConnectionKey:@(YES) |
|
// }]; |
|
// [self.cMgr stopScan]; |
|
// |
|
// NSLog(@"BLE Connecting... Name ---> %@ UUID:%@",peripheral.name,peripheral.identifier.UUIDString); |
|
//} |
|
|
|
/** |
|
连接设备 |
|
@param bleModel ble模型 |
|
*/ |
|
- (void)connectedWithBleModel:(BLEModel *)bleModel |
|
{ |
|
self.currenModel = bleModel; |
|
self.reconnectPeripheral = bleModel.peripheral; |
|
// 连接设备 |
|
NSLog(@"BLE Connecting... Name ---> %@", bleModel.peripheral.name); |
|
[bleModel.peripheral setDelegate:self]; |
|
//[self.cMgr connectPeripheral:bleModel.peripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}]; |
|
if (@available(iOS 13.0, *)) { |
|
[self.cMgr connectPeripheral:bleModel.peripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES),CBConnectPeripheralOptionEnableTransportBridgingKey:@(YES)}]; |
|
} else { |
|
[self.cMgr connectPeripheral:bleModel.peripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}]; |
|
} |
|
// 停止扫描 |
|
[self cancelScan]; |
|
} |
|
|
|
/// 断开当前连接的设备 |
|
/// @param isUnbind 是否解绑 |
|
- (void)disConnectedCurrenDevice:(BOOL)isUnbind |
|
{ |
|
if (isUnbind) { |
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:AutomaticReconnectionDevice]; |
|
} |
|
// 调用则立刻设置为断开 |
|
self.isConnected = NO; |
|
// 存在设备则断开 |
|
if (self.reconnectPeripheral) |
|
{ |
|
[self.cMgr cancelPeripheralConnection:self.reconnectPeripheral]; |
|
if (isUnbind) |
|
{ |
|
self.reconnectPeripheral = nil; |
|
self.currenModel = nil; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
蓝牙设备重连 |
|
|
|
@param isStart 开启停止 |
|
*/ |
|
- (void)startAndStopReconnect:(BOOL)isStart |
|
{ |
|
if (!isStart || self.state != CBManagerStatePoweredOn) { |
|
// 取消重连 |
|
if (self.reconnectPeripheral) { |
|
[self.cMgr cancelPeripheralConnection:self.reconnectPeripheral]; |
|
} |
|
return; |
|
} |
|
// 获取本地设备 |
|
NSString *reconnectionUUIDSrting = [[NSUserDefaults standardUserDefaults] objectForKey:AutomaticReconnectionDevice]; |
|
if (!reconnectionUUIDSrting) { |
|
return; |
|
} |
|
// 重连系统已配对设备 |
|
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:reconnectionUUIDSrting]; |
|
NSArray *systemPeripherals = [self.cMgr retrievePeripheralsWithIdentifiers:@[uuid]]; |
|
if (systemPeripherals.count) |
|
{ |
|
CBPeripheral *peripheral = systemPeripherals.firstObject; |
|
BLEModel *bleModel = [[BLEModel alloc] initWithCentral:self.cMgr |
|
Peripheral:peripheral |
|
AdvertisementData:0 |
|
RSSI:[NSNumber numberWithInteger:0] |
|
UUIDString:peripheral.identifier.UUIDString |
|
MACString:nil]; |
|
self.currenModel = bleModel; |
|
self.reconnectPeripheral = peripheral; |
|
__weak typeof(self) weakSelf = self; |
|
[GCDTimer.ocShareInstance scheduledDispatchTimerWithTimerName:ConnectPeripheralTimerName timeInterval:self.reconnectTime queue:dispatch_get_main_queue() repeats:false immediately:false action:^{ |
|
weakSelf.reconnectPeripheral.delegate = self; |
|
[weakSelf.cMgr connectPeripheral:weakSelf.reconnectPeripheral options:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES), |
|
CBConnectPeripheralOptionNotifyOnConnectionKey:@(YES), |
|
CBConnectPeripheralOptionNotifyOnNotificationKey:@(YES)}]; |
|
weakSelf.reconnectTime = 0; |
|
[self cancelScan]; |
|
}]; |
|
} |
|
// 扫描设备 |
|
[self scanForPeripheralsWithdiscoverServices:nil IsDFU:true]; |
|
} |
|
|
|
|
|
//========== 处理蓝牙指令 ========== // |
|
|
|
/** |
|
写入数据 |
|
|
|
@param bleItem 蓝牙命令模型 |
|
*/ |
|
- (void)writeWithBleItem:(BlueToothItem *)bleItem { |
|
if (self.isConnected) { |
|
[self.bluetoothItems addObject:bleItem]; |
|
[self loopRequest]; |
|
return; |
|
} |
|
// 代理超时返回 |
|
if (IsDelegate(bleItem.dataSource, bluetoothManager:sequenceID:didTimeOutWithData:cmdId:)) { |
|
[bleItem.dataSource bluetoothManager:self sequenceID:bleItem.sid didTimeOutWithData:nil cmdId:bleItem.cmd]; |
|
} |
|
} |
|
|
|
/// 清空指令队列 |
|
- (void)removeCmdItem { |
|
// 当前队列若存在则清空并且停止定时器 |
|
if (self.bluetoothItems.count) { |
|
[self.bluetoothItems removeAllObjects]; |
|
[self.checkoutTimer invalidate]; |
|
self.timeNumber = 0; |
|
_executing = NO; |
|
} |
|
} |
|
|
|
- (void)firmwareCompleteResetDelegate { |
|
self.cMgr.delegate = self; |
|
} |
|
|
|
/** |
|
循环串行 执行蓝牙请求(先进先出) |
|
*/ |
|
- (void)loopRequest |
|
{ |
|
if (_bluetoothItems.count > 0 && _executing == NO) { |
|
BlueToothItem *bleItem = _bluetoothItems.firstObject; |
|
if (!bleItem.isExecuting) { |
|
|
|
self.currenItem = bleItem; |
|
|
|
// 分包发送数据 |
|
[self handleWithByteBuffer:self.currenItem.byteBuffer]; |
|
|
|
bleItem.isExecuting = YES; |
|
_executing = YES; |
|
|
|
// 定时器开启 3秒超时 |
|
self.timeNumber = 0; |
|
CGFloat timeOut = 1.0; |
|
NSTimer *checkoutTimer = [NSTimer scheduledTimerWithTimeInterval:timeOut target:self selector:@selector(clickTimeout:) userInfo:nil repeats:YES]; |
|
self.checkoutTimer = checkoutTimer; |
|
NSLog(@"[蓝牙通讯] 发送了蓝牙指令 %@ 序号是 %ld 指令是 %ld",bleItem.byteBuffer,bleItem.sid,bleItem.cmd); |
|
} |
|
} |
|
} |
|
|
|
|
|
/** |
|
超时处理,进入此方法则重发 |
|
*/ |
|
- (void)clickTimeout:(NSTimer *)timer { |
|
|
|
self.timeNumber++; |
|
|
|
NSLog(@"[蓝牙通讯] 超时了,第 %ld 秒",self.timeNumber); |
|
|
|
// 超时五秒后重发 |
|
if (self.timeNumber > TimeOutNumber) { |
|
|
|
// 重发三次 |
|
if (self.currenItem.requestCount < 2) { |
|
|
|
self.timeNumber = 0; |
|
|
|
NSLog(@"[蓝牙通讯] 重新发送了 %ld",self.currenItem.requestCount); |
|
|
|
self.currenItem.requestCount++; |
|
|
|
// 重新发送时置空数据包 |
|
self.currenData = [NSMutableData new]; |
|
self.totalData = [NSMutableData new]; |
|
|
|
// 分包发送数据 |
|
[self handleWithByteBuffer:self.currenItem.byteBuffer]; |
|
|
|
}else { |
|
// 超时隐藏提示框 |
|
[SVProgressHUD dismiss]; |
|
// [SVProgressHUD showInfoWithStatus:NSLocalizedString(@"请求超时", nil)]; |
|
// // 超时断开设备 |
|
// [self disConnectedCurrenDevice:false]; |
|
// 代理超时返回 |
|
if (IsDelegate(self.currenItem.dataSource, bluetoothManager:sequenceID:didTimeOutWithData:cmdId:)) { |
|
|
|
[self.currenItem.dataSource bluetoothManager:self sequenceID:self.currenItem.sid didTimeOutWithData:nil cmdId:self.currenItem.cmd]; |
|
} |
|
// 超时处理 |
|
[self completeAndTimeouthandleWithItem:self.currenItem]; |
|
} |
|
} |
|
} |
|
|
|
|
|
/** |
|
分包数据发送处理 |
|
*/ |
|
- (void)handleWithByteBuffer:(NSMutableData *)byteBuffer |
|
{ |
|
// 发送时默认回调数据校验成功 |
|
self.isCrc = YES; |
|
// 大于20字节则分包发送 |
|
if (byteBuffer.length > 20) |
|
{ |
|
NSInteger count = byteBuffer.length / 20; |
|
for (int i = 0; i <= count; i++) |
|
{ |
|
NSData *data = [[NSData alloc] init]; |
|
if (i < count) |
|
{ |
|
data = [byteBuffer subdataWithRange:NSMakeRange(i * 20, 20)]; |
|
} |
|
else if (byteBuffer.length % 20) |
|
{ |
|
// 最后不足20字节的数据 |
|
data = [byteBuffer subdataWithRange:NSMakeRange(i * 20, byteBuffer.length % 20)]; |
|
} |
|
if (data) |
|
{ |
|
if (self.writecharacteristic) |
|
{ |
|
// 写入数据 |
|
[self.reconnectPeripheral writeValue:data forCharacteristic:self.writecharacteristic type:CBCharacteristicWriteWithoutResponse]; |
|
// NSLog(@"写入数据 %@",data); |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if (self.writecharacteristic) |
|
{ |
|
// 写入数据 |
|
[self.reconnectPeripheral writeValue:byteBuffer forCharacteristic:self.writecharacteristic type:CBCharacteristicWriteWithoutResponse]; |
|
//NSLog(@"写入数据 %@",byteBuffer); |
|
} |
|
} |
|
|
|
// for (int i = 0; i < byteBuffer.length; i += 20) |
|
// { |
|
// // 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小 |
|
// if ((i + 20) < byteBuffer.length) |
|
// { |
|
// NSString *rangeStr = [NSString stringWithFormat:@"%i,%i", i, 20]; |
|
// NSData *subData = [byteBuffer subdataWithRange:NSRangeFromString(rangeStr)]; |
|
// NSLog(@"%@",subData); |
|
// if (self.writecharacteristic) |
|
// { |
|
// // 写入数据 |
|
// [self.reconnectPeripheral writeValue:subData forCharacteristic:self.writecharacteristic type:CBCharacteristicWriteWithoutResponse]; |
|
// NSLog(@"写入数据 %@",subData); |
|
// } |
|
// //根据接收模块的处理能力做相应延时 |
|
// usleep(20 * 1000); |
|
// } |
|
// else { |
|
// if (self.writecharacteristic) |
|
// { |
|
// // 写入数据 |
|
// [self.reconnectPeripheral writeValue:byteBuffer forCharacteristic:self.writecharacteristic type:CBCharacteristicWriteWithoutResponse]; |
|
// NSLog(@"写入数据 %@",byteBuffer); |
|
// } |
|
// usleep(20 * 1000); |
|
// } |
|
// } |
|
} |
|
|
|
|
|
/** |
|
任务完成 || 超时 处理 |
|
|
|
@param item 当前蓝牙命令 |
|
*/ |
|
- (void)completeAndTimeouthandleWithItem:(BlueToothItem *)item { |
|
|
|
[self.checkoutTimer invalidate]; |
|
_executing = NO; |
|
self.currenData = [NSMutableData new]; |
|
self.totalData = [NSMutableData new]; |
|
[self.bluetoothItems removeObject:item]; |
|
[self loopRequest]; |
|
} |
|
|
|
/** |
|
根据序号查找当前队列是否存在 |
|
*/ |
|
- (BlueToothItem *)requestItemFromIndex:(int)index { |
|
for (BlueToothItem *item in _bluetoothItems) |
|
{ |
|
if (item.sid == index) { |
|
return item; |
|
} |
|
} |
|
return nil; |
|
} |
|
|
|
/** |
|
处理收到的数据 |
|
|
|
@param data 返回的数据 |
|
*/ |
|
#pragma mark ---- 处理收到的数据 |
|
- (void)handleReceiveData:(NSData *)data { |
|
NSLog(@"[蓝牙通讯] 接收到的数据------%@",data); |
|
// 如果存在队列 收到数据则重新计算超时 |
|
if (_bluetoothItems.count) { |
|
self.timeNumber = 0; |
|
} |
|
// 处理命令后的返回数据 |
|
[self.currenData appendData:data]; |
|
// 判断数据是否完整 |
|
Byte *currentDataBytes = (Byte *)[self.currenData bytes]; |
|
int contenLenght = (currentDataBytes[2] & 0xff) << 8 | (currentDataBytes[3] & 0xff); |
|
int actualLenght = (int)self.currenData.length - 8; |
|
// NSLog(@"现在的数据------ %@",self.currenData); |
|
// NSLog(@"应该有的长度------ %d 现在的长度-------- %d",contenLenght,actualLenght); |
|
//Byte *buffer = (Byte *)[self.currenData bytes]; |
|
// 接收到的数据包完整 |
|
if (contenLenght == actualLenght) { |
|
// 判断序号是否存在当前队列 |
|
int sid = (currentDataBytes[6] & 0xff) << 8 | (currentDataBytes[7] & 0xff); |
|
BlueToothItem *item = [self requestItemFromIndex:sid]; |
|
if (item) { |
|
// 判断err,1:成功 0:失败 |
|
BOOL err = [self checkDataWithData:self.currenData ByteIndex:1 bitIndex:2]; |
|
if (err) { |
|
// 大于200bytes字节的数据不做CRC处理 |
|
if (contenLenght <= 200) { |
|
// CRC校验(失败则返回重发) |
|
if (![BluetoothTool checkCRCWithData:self.currenData]) { |
|
self.isCrc = NO; |
|
} |
|
} |
|
// 判断是否有后续数据包返回 1:有 0:无 |
|
BOOL isData = [self checkDataWithData:self.currenData ByteIndex:1 bitIndex:1]; |
|
if (!isData && !self.isCrc) { |
|
// 不存在后续数据并且数据检验失败直接重发 |
|
return; |
|
} |
|
if (isData) { |
|
// 还有后续数据则将当前完整数据包以分隔符拼接 |
|
[self.totalData appendData:self.currenData]; |
|
self.currenData = [NSMutableData new]; |
|
[self.totalData appendData:[DataSeparator dataUsingEncoding:NSUTF8StringEncoding]]; |
|
return; |
|
}else{ |
|
// 拼接最后完整数据包 |
|
[self.totalData appendData:self.currenData]; |
|
// 代理返回成功数据 |
|
if (IsDelegate(item.dataSource, bluetoothManager:sequenceID:didCompleteWithData:cmdId:)) { |
|
[self.currenItem.dataSource bluetoothManager:self sequenceID:item.sid didCompleteWithData:self.totalData cmdId:item.cmd]; |
|
} |
|
// 当前数据处理完成 |
|
[self completeAndTimeouthandleWithItem:item]; |
|
} |
|
} |
|
}else { |
|
// 不存在此队列(设备主动发送过来的数据) |
|
[self handleDeviceControlWithData:self.currenData]; |
|
self.currenData = [NSMutableData new]; |
|
} |
|
}else { |
|
// 数据不完整返回拼接 |
|
return; |
|
} |
|
} |
|
|
|
/** |
|
处理设备发送过来的指令(找手机,拍照功能,实时数据) |
|
*/ |
|
- (void)handleDeviceControlWithData:(NSMutableData *)data { |
|
|
|
NSLog(@"处理设备发送过的指令 ------- %@",data); |
|
// 检查是否为指令 |
|
if (((Byte*)([data bytes]))[0] == 0xCB) { |
|
|
|
// 判断err是否正确 |
|
BOOL err = [self checkDataWithData:data ByteIndex:1 bitIndex:2]; |
|
|
|
if (err) { |
|
|
|
Byte key = ((Byte*)([data bytes]))[10]; |
|
|
|
BleCMD blecmd = 0; |
|
|
|
switch (key) { |
|
case 0x46: //!< 拍照开关 0X46 |
|
{ |
|
blecmd = BleCMD_cameraSwitch; |
|
} |
|
break; |
|
|
|
case 0x47: //!< 拍照 0X47 |
|
{ |
|
blecmd = BleCMD_cameraON; |
|
} |
|
break; |
|
|
|
case 0x48: //!< 退出拍照 0X48 |
|
{ |
|
blecmd = BleCMD_cameraOFF; |
|
} |
|
break; |
|
|
|
case 0x51: //!< 查找手机 0X51 |
|
{ |
|
blecmd = BleCMD_findPhone; |
|
} |
|
break; |
|
|
|
case 0xB4: //!< 实时计步,心率,血压,血氧-> 同步实时数据 |
|
case 0xB3: |
|
case 0xB6: |
|
case 0xB7: |
|
case 0xB8: |
|
{ |
|
blecmd = BleCMD_realTimesync; |
|
} |
|
break; |
|
|
|
case 0x01: //!< 抬手亮屏设置 |
|
{ |
|
blecmd = BleCMD_gesturesControl; |
|
} |
|
break; |
|
|
|
case 0xA5: //!< 辅助运动,的运动数据 |
|
{ |
|
//blecmd = BleCMD_sportModelData; |
|
blecmd = BleCMD_syncHealth; |
|
} |
|
break; |
|
|
|
case 0x41: //!< 电量 |
|
{ |
|
blecmd = BleCMD_batteryBack; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
// 将设备控制的指令类型通知出去 |
|
if (blecmd) { |
|
if (blecmd == BleCMD_realTimesync) |
|
[BLEBaseBl.shareInstance readProfileWithBleCmdType:BleCMD_readProfile]; |
|
|
|
// 处理设备端发来的实时步数,心率,血压血氧 |
|
if (blecmd == BleCMD_realTimesync||blecmd == BleCMD_syncHealth) { |
|
NSDictionary *dict = [L2DataParse L2_ParseData:data type:blecmd]; |
|
BleCMD reportCMD = [dict[@"DataType"] integerValue]; |
|
[[NSNotificationCenter defaultCenter] postNotificationName:DeviceReportRealTimeHealthData object:@(reportCMD) userInfo:dict]; |
|
|
|
|
|
return; |
|
} |
|
// 回复设备发出通知 |
|
[self checkCRCHandleWithData:data confirm:YES]; |
|
[[NSNotificationCenter defaultCenter] postNotificationName:DeviceControlNotification object:@(blecmd) userInfo:@{@"data": data}]; |
|
return; |
|
} |
|
} else { |
|
[self checkCRCHandleWithData:data confirm:NO];// 回复设备发出通知 |
|
} |
|
} else { |
|
return; // 非请求返回的数据也不是设备发送过来的数据不处理 |
|
} |
|
} |
|
|
|
/** |
|
处理设备的运动状态 |
|
*/ |
|
- (void)handleSportStateWithData:(NSMutableData *)data { |
|
// Byte *buffer = (Byte *)[data bytes]; |
|
// int sportState = buffer[13]; |
|
// |
|
// UserGlobal *userGlobal = [UserGlobal ocShareInstance]; |
|
// if (!userGlobal.locationAide) { |
|
// userGlobal.locationAide = [[LocationAide alloc]init]; |
|
// } |
|
// if (sportState == 1) {//运动结束 |
|
// [userGlobal.locationAide stopLocation]; |
|
// } else {//运动开始 |
|
// [userGlobal.locationAide stratLocation]; |
|
// userGlobal.locationAide.gpsSignalBlock = ^(NSInteger signal) { |
|
// [[[BLEBaseBl alloc] init] GPSSignalStrengthWithBleCmdType:BleCMD_GPSSignalStrength dialType:signal]; |
|
// NSLog(@"signal = %ld",(long)signal); |
|
// }; |
|
// } |
|
} |
|
|
|
/** |
|
处理设备发送过来的数据 |
|
*/ |
|
- (void)handleDeviceDataCMD:(BleCMD)blecmd Data:(NSMutableData *)data { |
|
NSDictionary *dict = [L2DataParse L2_ParseData:data type:blecmd]; |
|
BleCMD reportCMD = [dict[@"DataType"] integerValue]; |
|
[[NSNotificationCenter defaultCenter] postNotificationName:DeviceReportRealTimeHealthData object:@(reportCMD) userInfo:dict]; |
|
} |
|
|
|
/** |
|
检查data中某个字节中的某位的值 1:YES 0:NO |
|
|
|
@param data 数据包 |
|
@param byteIndex 字节的下标 |
|
@param bitIndex 位的下标(从左到又) |
|
*/ |
|
- (BOOL)checkDataWithData:(NSMutableData *)data ByteIndex:(NSInteger)byteIndex bitIndex:(NSInteger)bitIndex { |
|
int num = ((Byte*)([data bytes]))[byteIndex]; |
|
UInt8 confirm = (UInt8)num; |
|
confirm = confirm << bitIndex; |
|
confirm = confirm >> 7; |
|
return confirm; |
|
} |
|
|
|
/** |
|
CRC校验成功或失败后回复设备端 |
|
*/ |
|
- (void)checkCRCHandleWithData:(NSMutableData *)data confirm:(BOOL)confirm { |
|
if(data.length <=12) { //l1层就有13个字节了。不够长的数据是垃圾数据 |
|
return; |
|
} |
|
Byte byte = confirm == YES ? [BluetoothTool stitchWithErr:1 Ack:1 Vision:0] : [BluetoothTool stitchWithErr:0 Ack:1 Vision:0]; |
|
// 替换L1层第二个字节 |
|
[data replaceBytesInRange:NSMakeRange(1, 1) withBytes:&byte length:1]; |
|
// 替换长度为不带参数的5 |
|
Byte buffer[2]; |
|
buffer[0] = 5 >> 8; |
|
buffer[1] = 5 & 0xff; |
|
[data replaceBytesInRange:NSMakeRange(2, 2) withBytes:buffer length:2]; |
|
Byte dataLength[2]; |
|
dataLength[0] = 0; |
|
dataLength[1] = 0; |
|
[data replaceBytesInRange:NSMakeRange(11, 2) withBytes:dataLength length:2]; |
|
// NSLog(@"发送给设备端的确认码--------%@ 是否正确:%d",[data subdataWithRange:NSMakeRange(0, 13)],confirm); |
|
// 发送L1层确认码给设备端 |
|
[self.reconnectPeripheral writeValue:[data subdataWithRange:NSMakeRange(0, 13)] forCharacteristic:self.writecharacteristic type:CBCharacteristicWriteWithoutResponse]; |
|
} |
|
|
|
/** |
|
获取系统已配对的设备 |
|
*/ |
|
- (NSMutableArray *)getPairedConnectedArray { |
|
NSMutableArray *connectedArray = [[NSMutableArray alloc] init]; |
|
NSArray *braceletDeviceArr = [self.cMgr retrieveConnectedPeripheralsWithServices:@[[CBUUID UUIDWithString:ServicesUUID]]]; |
|
for (NSInteger i = 0; i < braceletDeviceArr.count; i ++) { |
|
CBPeripheral *peripheral = braceletDeviceArr[i]; |
|
BLEModel *bleModel = [[BLEModel alloc] initWithCentral:self.cMgr |
|
Peripheral:peripheral |
|
AdvertisementData:0 |
|
RSSI:[NSNumber numberWithInteger:0] |
|
UUIDString:peripheral.identifier.UUIDString |
|
MACString:nil]; |
|
bleModel.isPaired = YES; |
|
[connectedArray addObject:bleModel]; |
|
} |
|
return connectedArray; |
|
} |
|
|
|
|
|
|
|
|
|
#pragma mark 杰里升级 |
|
/// 打开表盘文件 |
|
- (void)openDialFile |
|
{ |
|
[DialManager openDialFileSystemWithCmdManager:self.mAssist.mCmdManager withResult:^(DialOperateType type, float progress) { |
|
if (type == DialOperateTypeUnnecessary) { |
|
NSLog(@"已开启!"); |
|
} |
|
if (type == DialOperateTypeFail || type == DialOperateTypeCmdFail) { |
|
NSLog(@"获取失败!"); |
|
} |
|
if (type == DialOperateTypeSuccess) { |
|
NSLog(@"获取成功!"); |
|
} |
|
}]; |
|
} |
|
|
|
|
|
|
|
-(void)getInfo |
|
{ |
|
[self.mAssist.mCmdManager cmdTargetFeatureResult:^(JL_CMDStatus status, uint8_t sn, NSData * _Nullable data) { |
|
|
|
JLModel_Device *model = [self.mAssist.mCmdManager outputDeviceModel]; |
|
|
|
if (status == JL_CMDStatusSuccess) { |
|
JL_OtaStatus upSt = model.otaStatus; |
|
self.mOtaPath = [[NSUserDefaults standardUserDefaults] objectForKey:@"OTAPATH"]; |
|
if (upSt == JL_OtaStatusForce) { |
|
NSLog(@"---> 进入强制升级."); |
|
//[JL_Tools delay:5 Task:^{ |
|
[self onUpdateOTA]; |
|
//}]; |
|
return; |
|
}else{ |
|
if (model.otaHeadset == JL_OtaHeadsetYES) { |
|
NSLog(@"---> 进入强制升级: OTA另一只耳机."); |
|
// [DFUITools showText:@"需要强制升级!" onView:self.view delay:1.0]; |
|
// [JL_Tools post:@"kJL_OTA_CONNECT" Object:nil]; |
|
// [JL_Tools delay:5 Task:^{ |
|
[self onUpdateOTA]; |
|
//}]; |
|
return; |
|
} |
|
} |
|
NSLog(@"---> 设备正常使用..."); |
|
[self openDialFile]; |
|
[JL_Tools mainTask:^{ |
|
//[DFUITools showText:@"设备正常使用" onView:self.view delay:1.0]; |
|
|
|
/*--- 获取公共信息 ---*/ |
|
[self.mAssist.mCmdManager cmdGetSystemInfo:JL_FunctionCodeCOMMON Result:nil]; |
|
}]; |
|
}else{ |
|
NSLog(@"---> ERROR:设备信息获取错误!"); |
|
} |
|
}]; |
|
} |
|
|
|
#pragma mark - OTA升级监听超时定时器(也可不监听超时) |
|
static NSTimer *otaTimer = nil; |
|
static int otaTimeout= 0; |
|
static NSTimer *jlTimer = nil; |
|
- (void)otaTimeCheck { |
|
otaTimeout = 0; |
|
if (otaTimer == nil) { |
|
otaTimer = [JL_Tools timingStart:@selector(otaTimeAdd) target:self Time:1.0]; |
|
} |
|
} |
|
|
|
#pragma mark 关闭超时检测 |
|
- (void)otaTimeClose { |
|
[JL_Tools timingStop:otaTimer]; |
|
otaTimeout = 0; |
|
otaTimer = nil; |
|
} |
|
|
|
- (void)otaTimeAdd { |
|
otaTimeout++; |
|
if (otaTimeout == 10) { |
|
[self otaTimeClose]; |
|
NSLog(@"OTA ---> 超时了!!!"); |
|
[SVProgressHUD showErrorWithStatus:@"升级超时"]; |
|
} |
|
} |
|
|
|
///////////////////////////////////////// |
|
- (void)ljTimeCheck { |
|
if (jlTimer == nil) { |
|
jlTimer = [JL_Tools timingStart:@selector(scanForPeripherals) target:self Time:1.0]; |
|
} |
|
} |
|
|
|
- (void)scanForPeripherals |
|
{ |
|
[self scanForPeripheralsWithdiscoverServices:nil IsDFU:YES]; |
|
} |
|
|
|
- (void)jlTimeClose |
|
{ |
|
[JL_Tools timingStop:jlTimer]; |
|
jlTimer = nil; |
|
} |
|
|
|
|
|
- (void)otaFuncWithFilePaths:(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.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(@"JL-校验文件中 %@",txt); |
|
[SVProgressHUD showWithStatus:[NSString stringWithFormat:@"%@ %@",NSLocalizedString(@"校验文件", nil),txt]]; |
|
} |
|
if (result == JL_OTAResultUpgrading) |
|
{ |
|
NSLog(@"JL-正在升级 %@",txt); |
|
[SVProgressHUD showWithStatus:[NSString stringWithFormat:@"%@ %@",NSLocalizedString(@"正在升级", nil),txt]]; |
|
} |
|
|
|
[self otaTimeCheck];//增加超时检测 |
|
} |
|
else if (result == JL_OTAResultPrepared) |
|
{ |
|
NSLog(@"JL----> 检验文件【完成】"); |
|
[self otaTimeCheck];//增加超时检测 |
|
} |
|
else if (result == JL_OTAResultReconnect) |
|
{ |
|
NSLog(@"JL----> OTA正在回连设备... "); |
|
[self startAndStopReconnect:YES]; |
|
[self otaTimeCheck];//关闭超时检测 |
|
} |
|
else if (result == JL_OTAResultReconnectWithMacAddr) |
|
{ |
|
[SVProgressHUD dismiss]; |
|
[SVProgressHUD showWithStatus:NSLocalizedString(@"正在重新连接", nil)]; |
|
JLModel_Device *model = [self.mAssist.mCmdManager outputDeviceModel]; |
|
NSLog(@"JL----> OTA正在通过Mac Addr方式回连设备... \n%@",model); |
|
//[self startAndStopReconnect:YES]; |
|
self.lastBleMacAddress = model.bleAddr; |
|
self.is_JL = YES; |
|
[self ljTimeCheck]; |
|
|
|
[self otaTimeCheck];//关闭超时检测 |
|
} |
|
else if (result == JL_OTAResultReboot) |
|
{ |
|
[SVProgressHUD dismiss]; |
|
//[SVProgressHUD showWithStatus:NSLocalizedString(@"设备重启,正在回连", nil)]; |
|
NSLog(@"JL---->设备重启."); |
|
[self otaTimeClose];//关闭超时检测 |
|
} |
|
else if (result == JL_OTAResultSuccess || result == JL_OTAResultFail) |
|
{ |
|
if ([weakSelf.jl_delegate respondsToSelector:@selector(otaProgressWithOtaResult:withProgress:)]) { |
|
[weakSelf.jl_delegate otaProgressWithOtaResult:result withProgress:progress]; |
|
} |
|
} |
|
else |
|
{ |
|
[SVProgressHUD showErrorWithStatus:NSLocalizedString(@"发生错误,升级失败", nil)]; |
|
// 其余错误码详细 Command+点击JL_OTAResult 查看说明 |
|
NSLog(@"JL-ota update result: %d", result); |
|
} |
|
|
|
|
|
|
|
}]; |
|
} |
|
|
|
- (void)startScanBLE |
|
{ |
|
NSLog(@"BLE ---> startScanBLE."); |
|
if (self.cMgr) { |
|
if (self.cMgr.state == CBManagerStatePoweredOn) { |
|
[self.cMgr scanForPeripheralsWithServices:nil options:nil]; |
|
} else { |
|
__weak typeof(self) weakSelf = self; |
|
dispatch_after(0.5, dispatch_get_main_queue(), ^{ |
|
if (weakSelf.cMgr.state == CBManagerStatePoweredOn) { |
|
[weakSelf.cMgr scanForPeripheralsWithServices:nil options:nil]; |
|
} |
|
}); |
|
} |
|
} |
|
} |
|
|
|
|
|
- (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 = self.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)downloadZipUrl:(NSString *)url updatePath:(NSString *)path |
|
{ |
|
[[User_Http shareInstance] downloadUrl:url Path:path Result:^(float progress, JLHTTP_Result result) { |
|
[JL_Tools mainTask:^{ |
|
//self.updateTxt.text = kJL_TXT("下载固件"); |
|
if (result == JLHTTP_ResultDownload) { |
|
// self.progressTxt.text = [NSString stringWithFormat:@"%.1f%%",progress*100.0]; |
|
// self.progressView.progress = progress; |
|
//[DFUITools showTransparentHUDOnWindowWithLabel:[NSString stringWithFormat:@"%.1f%%",progress*100.0]]; |
|
[SVProgressHUD showWithStatus:[NSString stringWithFormat:@"%@ %.1f%%",NSLocalizedString(@"下载中", nil),progress*100.0]]; |
|
} |
|
if (result == JLHTTP_ResultSuccess) { |
|
/*--- 现在完直接升级 ---*/ |
|
[self otaFuncWithFilePaths:path]; |
|
} |
|
if (result == JLHTTP_ResultFail) { |
|
[SVProgressHUD showErrorWithStatus:@"下载失败"]; |
|
} |
|
}]; |
|
}]; |
|
} |
|
|
|
@end
|
|
|