|   | 
|   | 
| #import <UIKit/UIKit.h>  | 
| #import "CBMoralManager.h"  | 
| #import "CBPeripheralExt.h"  | 
| #import "ResourceHandler.h"  | 
| #import "Utilities.h"  | 
|   | 
| /*!  | 
|  *  @class CBMoralManager  | 
|  *  | 
|  *  @discussion Class to co-ordinate all the peripheral related operations  | 
|  *  | 
|  */  | 
|   | 
| @interface CBMoralManager () <CBCentralManagerDelegate, CBPeripheralDelegate>  | 
| {  | 
|     CBCentralManager    *centralManager;  | 
|     NSMutableArray *peripheralListArray;  | 
|       | 
|     void (^cbCommunicationHandler)(BOOL success, NSError *error);  | 
|     BOOL isTimeOutAlert;  | 
| }  | 
| @end  | 
|   | 
| @implementation CBMoralManager  | 
|   | 
| @synthesize cbCharacteristicDelegate;  | 
| @synthesize myPeripheral;  | 
| @synthesize myService;  | 
| @synthesize myCharacteristic;  | 
| @synthesize myCharacteristic2;  | 
| @synthesize serviceUUIDDict;  | 
| @synthesize cbDiscoveryDelegate;  | 
| @synthesize foundPeripherals;  | 
| @synthesize foundServices;  | 
| @synthesize characteristicDescriptors;  | 
| @synthesize characteristicProperties;  | 
| @synthesize bootLoaderFilesArray;  | 
|   | 
|   | 
| #define k_SERVICE_UUID_PLIST_NAME @"ServiceUUIDPList"  | 
|   | 
| #pragma mark - Singleton Methods  | 
|   | 
| + (id)sharedManager {  | 
|     static CBMoralManager *sharedMyManager = nil;  | 
|     static dispatch_once_t onceToken;  | 
|     dispatch_once(&onceToken, ^{  | 
|         sharedMyManager = [[self alloc] init];  | 
|     });  | 
|     return sharedMyManager;  | 
| }  | 
|   | 
| - (id)init {  | 
|     if (self = [super init])  | 
|     {  | 
|         centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];  | 
|         foundPeripherals = [[NSMutableArray alloc] init];  | 
|         foundServices = [[NSMutableArray alloc] init];  | 
|         peripheralListArray = [[NSMutableArray alloc] init];  | 
|         serviceUUIDDict = [NSMutableDictionary dictionaryWithDictionary:[ResourceHandler getItemsFromPropertyList:k_SERVICE_UUID_PLIST_NAME]];  | 
|         bootLoaderFilesArray = nil;  | 
|     }  | 
|     return self;  | 
| }  | 
|   | 
|   | 
| #pragma mark - Discovery  | 
| /*                                Discovery                                   */  | 
| /****************************************************************************/  | 
|   | 
| /*!  | 
|  *  @method startScanning  | 
|  *  | 
|  *  @discussion  To scans for peripherals that are advertising services.  | 
|  *  | 
|  */  | 
|   | 
| - (void) startScanning  | 
| {  | 
|     NSLog(@"-------------------->startScanning");  | 
|       | 
|     if((NSInteger)[centralManager state] == CBCentralManagerStatePoweredOn)  | 
|     {  | 
|         [cbDiscoveryDelegate bluetoothStateUpdatedToState:YES];  | 
|         NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],CBCentralManagerScanOptionAllowDuplicatesKey, nil];  | 
|         [centralManager scanForPeripheralsWithServices:nil options:options];  | 
|     }  | 
|     else if ([centralManager state] == CBCentralManagerStateUnsupported)  | 
|     {  | 
|         [Utilities alert:@"CySmart" Message:@"This device does not support Bluetooth Low Energy"];  | 
|     }  | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method stopScanning  | 
|  *  | 
|  *  @discussion  To stop scanning for peripherals.  | 
|  *  | 
|  */  | 
|   | 
| - (void) stopScanning  | 
| {  | 
|       | 
|     [centralManager stopScan];  | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method peripheralWithPeripheral:advertisementData:RSSI  | 
|  *  | 
|  *  @param peripheral           A <code>CBPeripheral</code> object.  | 
|  *  @param advertisementData    A dictionary containing any advertisement and scan response data.  | 
|  *  @param RSSI                 The current RSSI of <i>peripheral</i>, in dBm. A value of <code>127</code> is reserved and indicates the RSSI  | 
|  *                                was not available.  | 
|  *  | 
|  *  @discussion  The methods handles the peripherals are to be displayed or not in the BLE Device List.  | 
|  Each time a new peripheral dicover will invoke [discoveryDidRefresh] method.  | 
|  *  | 
|  */  | 
|   | 
| -(void)peripheralWithPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI  | 
| {  | 
|       | 
|     if (![peripheralListArray containsObject:peripheral])  | 
|     {  | 
|         if(peripheral.state == CBPeripheralStateConnected)  | 
|         {  | 
|         }  | 
|         else  | 
|         {  | 
|             //Printing description of newPeriPheral->mAdvertisementData:  | 
|             //{  | 
|             //    kCBAdvDataIsConnectable = 1;  | 
|             //    kCBAdvDataLocalName = MORAL;  | 
|             //    kCBAdvDataServiceData =     {  | 
|             //        "0003CBBB-0000-1000-8000-00805F9B0131" = <bbcb>;  | 
|             //   };  | 
|             //}  | 
|               | 
|             CBPeripheralExt *newPeriPheral = [[CBPeripheralExt alloc] init];  | 
|             newPeriPheral.mPeripheral = [peripheral copy];  | 
|             newPeriPheral.mAdvertisementData = [advertisementData copy];  | 
|             newPeriPheral.mRSSI = [RSSI copy];  | 
|             [peripheralListArray addObject:peripheral];  | 
|             [foundPeripherals addObject:newPeriPheral];  | 
|             [cbDiscoveryDelegate discoveryDidRefresh];  | 
|         }  | 
|     }  | 
|       | 
| }  | 
|   | 
| //  - This is called with the CBPeripheral class as its main input parameter. This contains the information about a BLE peripheral.  | 
| - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI  | 
| {  | 
|     [self peripheralWithPeripheral:peripheral advertisementData:advertisementData RSSI:RSSI];  | 
| }  | 
|   | 
| #pragma mark - Connection/Disconnection  | 
|   | 
| /*!  | 
|  *  @method cancelTimeOutAlert  | 
|  *  | 
|  *  @discussion Method to cancel timeout alert  | 
|  *  | 
|  */  | 
| -(void)cancelTimeOutAlert  | 
| {  | 
|     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeOutMethodForConnect) object:nil];  | 
| }  | 
|   | 
| /*!  | 
|  *  @method timeOutMethodForConnect  | 
|  *  连接超时  | 
|  *  @discussion The methods invoke to cancel the connection request Because connection attempts do not time out,  | 
|  *  | 
|  */  | 
| -(void)timeOutMethodForConnect  | 
| {  | 
|     isTimeOutAlert = YES;  | 
|     [self cancelTimeOutAlert];  | 
|     [self disconnectPeripheral:myPeripheral];  | 
|     NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];  | 
|     [errorDetail setValue:@"The Connection has Timed Out unexpectedly" forKey:NSLocalizedDescriptionKey];  | 
|     NSError *error = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDetail];  | 
|     [self refreshPeripherals];  | 
|     cbCommunicationHandler(NO,error);  | 
| }  | 
|   | 
| /*                        Connection/Disconnection                            */  | 
| /****************************************************************************/  | 
|   | 
|   | 
| /*!  | 
|  *  @method connectPeripheral:CompletionBlock  | 
|  *  | 
|  *  @param peripheral    The peripheral to which the central is attempting to connect.  | 
|  *  | 
|  *  @discussion     Establishes a local connection to a peripheral.  | 
|  *  | 
|  */  | 
| - (void) connectPeripheral:(CBPeripheral*)peripheral CompletionBlock:(void (^)(BOOL success, NSError *error))completionHandler  | 
| {  | 
|       | 
|       if((NSInteger)[centralManager state] == CBCentralManagerStatePoweredOn)  | 
|       {  | 
|           cbCommunicationHandler = completionHandler ;  | 
|            | 
|           if ([peripheral state] == CBPeripheralStateDisconnected)  | 
|           {  | 
|               [centralManager connectPeripheral:peripheral options:nil];  | 
|               LOG_INFO(@"[%@] %@",peripheral.name,CONNECTION_REQUEST);  | 
|               //[[LoggerHandler LogManager] LogData:[NSString stringWithFormat:@"[%@] %@",peripheral.name,CONNECTION_REQUEST]];  | 
|           }  | 
|           else  | 
|           {  | 
|               [centralManager cancelPeripheralConnection:peripheral];  | 
|           }  | 
|             | 
|           [self performSelector:@selector(timeOutMethodForConnect) withObject:nil afterDelay:DEVICE_CONNECTION_TIMEOUT];  | 
|   | 
|       }  | 
|       | 
|   | 
| }  | 
|   | 
| /*!  | 
|  *  @method disconnectPeripheral:  | 
|  *  | 
|  *  @param peripheral    The peripheral which needs to be disconnected from central.  | 
|  *  | 
|  *  @discussion     Cancels an active or pending local connection to a peripheral.  | 
|  *  | 
|  */  | 
|   | 
| - (void) disconnectPeripheral:(CBPeripheral*)peripheral  | 
| {  | 
|     if(peripheral)  | 
|     {  | 
|         [centralManager cancelPeripheralConnection:peripheral];  | 
|     }  | 
| }  | 
|   | 
| - (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral  | 
| {  | 
|     myPeripheral =  nil;  | 
|     myPeripheral = [peripheral copy];  | 
|     myPeripheral.delegate = self ;  | 
|     [myPeripheral discoverServices:nil];  | 
|       | 
|     LOG_INFO(@"[%@] %@",peripheral.name,CONNECTION_ESTABLISH);  | 
|     LOG_INFO(@"[%@] %@",peripheral.name,SERVICE_DISCOVERY_REQUEST);  | 
| }  | 
|   | 
|   | 
| - (void) centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error  | 
| {  | 
|     NSLog(@"Attempted connection to peripheral %@ failed: %@", [peripheral name], [error localizedDescription]);  | 
|      [self cancelTimeOutAlert];  | 
|      cbCommunicationHandler(NO,error);  | 
| }  | 
|   | 
|   | 
| - (void) centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error  | 
| {  | 
|     [self cancelTimeOutAlert];  | 
|   | 
|     /*  Check whether the disconnection is done by the device */  | 
|     if (error == nil && !isTimeOutAlert)  | 
|     {  | 
|         NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];  | 
|         [errorDetail setValue:@"The BLE device is disconnected" forKey:NSLocalizedDescriptionKey];  | 
|         NSError *disconnectError = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDetail];  | 
|           | 
|         LOG_INFO(@"[%@] %@",peripheral.name,DISCONNECTION_REQUEST);  | 
|           | 
|         cbCommunicationHandler(NO,disconnectError);  | 
|     }  | 
|     else  | 
|     {  | 
|         isTimeOutAlert = NO;  | 
|           | 
|         // Checking whether the disconnected device has pending firmware upgrade  | 
|         if ([[CBMoralManager sharedManager] bootLoaderFilesArray] != nil && error != nil)  | 
|         {  | 
|             NSMutableDictionary *errorDict = [NSMutableDictionary dictionary];  | 
|             [errorDict setValue:[NSString stringWithFormat:@"%@%@",[error.userInfo objectForKey:NSLocalizedDescriptionKey],FIRMWARE_DISCONNECTION_ERROR] forKey:NSLocalizedDescriptionKey];  | 
|               | 
|             NSError *disconnectionError = [NSError errorWithDomain:@"myDomain" code:100 userInfo:errorDict];  | 
|             cbCommunicationHandler(NO,disconnectionError);  | 
|         }  | 
|         else  | 
|             cbCommunicationHandler(NO,error);  | 
|     }  | 
|   | 
|     [self redirectToRootviewcontroller];  | 
|     LOG_INFO(@"[%@] %@",peripheral.name,DISCONNECTED);  | 
|     [self clearDevices];  | 
| }  | 
|   | 
| /*!  | 
|  *  @method redirectToRootviewcontroller  | 
|  *  | 
|  *  @discussion     Pops all the view controllers on the stack except the root view controller and updates the display. This will redirect to BLE Devices Page which list all discovered peripherals,  | 
|  *  | 
|  */  | 
|   | 
| -(void)redirectToRootviewcontroller  | 
| {  | 
|     if(cbDiscoveryDelegate)  | 
|     {  | 
|         [[(UIViewController*)cbDiscoveryDelegate navigationController] popToRootViewControllerAnimated:YES];  | 
|     }  | 
|     else if(cbCharacteristicDelegate)  | 
|     {  | 
|         [[(UIViewController*)cbCharacteristicDelegate navigationController] popToRootViewControllerAnimated:YES];  | 
|     }  | 
| }  | 
|   | 
|   | 
|   | 
| #pragma mark - Disc Services  | 
|   | 
| /*    Represents the current state of a CBCentralManager.                     */  | 
| /****************************************************************************/  | 
|   | 
|   | 
|   | 
| // CBPeripheralDelegate - Invoked when you discover the peripheral's available services.  | 
|   | 
| - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error  | 
| {  | 
|     [self cancelTimeOutAlert];  | 
|     if(error == nil)  | 
|     {  | 
|         LOG_INFO(@"[%@] %@- %@",peripheral.name,SERVICE_DISCOVERY_STATUS,SERVICE_DISCOVERED);  | 
|         BOOL isCapsenseExist = NO;  | 
|         for (CBService *service in peripheral.services)  | 
|         {  | 
|   | 
|             if (![foundServices containsObject:service]) {  | 
|                 [foundServices addObject:service];  | 
|                 if([service.UUID isEqual:CAPSENSE_SERVICE_UUID])  | 
|                 {  | 
|                     isCapsenseExist = YES;  | 
|                     cbCharacteristicDelegate = nil;    | 
|                      [myPeripheral discoverCharacteristics:nil forService:service];  | 
|                 }  | 
|             }  | 
|         }  | 
|         if(isCapsenseExist == NO )  | 
|             cbCommunicationHandler(YES,nil);  | 
|     }  | 
|     else  | 
|     {  | 
|         LOG_INFO(@"[%@] %@- %@%@]",peripheral.name,SERVICE_DISCOVERY_STATUS,SERVICE_DISCOVERY_ERROR,[error.userInfo objectForKey:NSLocalizedDescriptionKey]);  | 
|   | 
|         cbCommunicationHandler(NO,error);  | 
|           | 
|     }  | 
|   | 
| }  | 
|   | 
|   | 
|   | 
|   | 
| /*    Represents the current state of a CBCentralManager.                     */  | 
| /****************************************************************************/  | 
|   | 
| #pragma mark - characteristic/  | 
|   | 
| - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error  | 
| {  | 
|       | 
|     if([cbCharacteristicDelegate isKindOfClass:[CBMoralManager class]] || cbCharacteristicDelegate == nil)  | 
|     {  | 
|         cbCommunicationHandler(YES,nil);  | 
|     }  | 
|     else  | 
|     {  | 
|         [cbCharacteristicDelegate peripheral:peripheral didDiscoverCharacteristicsForService:service error:error];  | 
|     }  | 
|       | 
| }  | 
| - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error  | 
| {  | 
|     if (error)  | 
|     {  | 
|         if (!characteristic.isNotifying)  | 
|         {  | 
|             [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:characteristic.service.UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:characteristic.UUID] descriptor:nil operation:[NSString stringWithFormat:@"%@- %@%@",READ_RESPONSE,READ_ERROR,[error.userInfo objectForKey:NSLocalizedDescriptionKey]]];  | 
|         }  | 
|     }  | 
|       | 
|     if([cbCharacteristicDelegate respondsToSelector:@selector(peripheral:didUpdateValueForCharacteristic:error:)])  | 
|         [cbCharacteristicDelegate peripheral:peripheral didUpdateValueForCharacteristic:characteristic error:error];  | 
| }  | 
|   | 
| - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error  | 
| {  | 
|     if([cbCharacteristicDelegate respondsToSelector:@selector(peripheral:didWriteValueForCharacteristic:error:)])  | 
|     [cbCharacteristicDelegate peripheral:peripheral didWriteValueForCharacteristic:characteristic error:error];  | 
|       | 
| }  | 
|   | 
| - (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error  | 
| {  | 
|     if([cbCharacteristicDelegate respondsToSelector:@selector(peripheral:didDiscoverDescriptorsForCharacteristic:error:)])  | 
|     [cbCharacteristicDelegate peripheral:peripheral didDiscoverDescriptorsForCharacteristic:characteristic error:error];  | 
| }  | 
|   | 
| -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error  | 
| {  | 
|     if (error)  | 
|     {  | 
|         [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:descriptor.characteristic.service.UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:descriptor.characteristic.UUID] descriptor:[Utilities getDiscriptorNameForUUID:descriptor.UUID] operation:[NSString stringWithFormat:@"%@- %@%@",READ_RESPONSE,READ_ERROR,[error.userInfo objectForKey:NSLocalizedDescriptionKey]]];  | 
|     }  | 
|     [cbCharacteristicDelegate peripheral:peripheral didUpdateValueForDescriptor:descriptor error:error];  | 
| }  | 
|   | 
|   | 
| #pragma mark - BLE State  | 
|   | 
| /*    Represents the current state of a CBCentralManager.                     */  | 
| /****************************************************************************/  | 
|   | 
| /*!  | 
|  *  @method clearDevices  | 
|  *  | 
|  *  @discussion     Clear all listed peripherals and services,  | 
|  *  | 
|  */  | 
| - (void) clearDevices  | 
| {  | 
|     [peripheralListArray removeAllObjects];  | 
|     [foundPeripherals removeAllObjects];  | 
|     [foundServices removeAllObjects];  | 
| }  | 
|   | 
| /*  | 
|  Invoked when the central manager’s state is updated. (required)  | 
|   | 
|  If the state is On then app start scanning for peripherals that are advertising services.  | 
|  If the state is Off then call method [clearDevices] and redirect to Home screen.  | 
|    | 
|  */  | 
|   | 
|   | 
| - (void) centralManagerDidUpdateState:(CBCentralManager *)central  | 
| {  | 
|       | 
|     switch ((NSInteger)[centralManager state])  | 
|     {  | 
|         case CBCentralManagerStatePoweredOff:  | 
|         {  | 
|             [self clearDevices];  | 
|             /* Tell user to power ON BT for functionality, but not on first run - the Framework will alert in that instance. */  | 
|             //Show Alert  | 
|             [self redirectToRootviewcontroller];  | 
|             [cbDiscoveryDelegate bluetoothStateUpdatedToState:NO];  | 
|             break;  | 
|         }  | 
|               | 
|         case CBCentralManagerStateUnauthorized:  | 
|         {  | 
|             /* Tell user the app is not allowed. */  | 
|             [Utilities alert:@"CySmart" Message:@"The app is not authorized  to use bluetooth low energy"];  | 
|             break;  | 
|         }  | 
|               | 
|         case CBCentralManagerStateUnknown:  | 
|         {  | 
|             /* Bad news, let's wait for another event. */  | 
|             [Utilities alert:@"CySmart" Message:@"The state of central manager is unknown."];  | 
|             break;  | 
|         }  | 
|               | 
|         case CBCentralManagerStatePoweredOn:  | 
|         {  | 
|             [cbDiscoveryDelegate bluetoothStateUpdatedToState:YES];  | 
|             //暂时不需要马上检索  | 
|             //[self startScanning];  | 
|             break;  | 
|         }  | 
|               | 
|         case CBCentralManagerStateResetting:  | 
|         {  | 
|             [self clearDevices];  | 
|             break;  | 
|         }  | 
|     }  | 
|       | 
| }  | 
|   | 
| /*!  | 
|  *  @method refreshPeripherals  | 
|  *  | 
|  *  @discussion      Clear all listed peripherals and services.  | 
|  Device scanning will add  peripherals that are advertising services.  | 
|  And check the status of Bluetooth and alert the user if Off,  | 
|  *  | 
|  */  | 
|   | 
| - (void) refreshPeripherals  | 
| {  | 
|     [self clearDevices];  | 
|     if([centralManager state] == CBCentralManagerStatePoweredOff)  | 
|     {  | 
|         [Utilities alert:WARNING Message:@"Turn On Bluetooth to Allow App to Connect to Accessories"];  | 
|     }  | 
| //    [[CBMoralManager sharedManager] stopScanning];  | 
|     [[CBMoralManager sharedManager] startScanning];  | 
| }  | 
|   | 
|   | 
|   | 
|   | 
| @end  |