| /*  | 
|  * Copyright Cypress Semiconductor Corporation, 2014-2015 All rights reserved.  | 
|  *  | 
|  * This software, associated documentation and materials ("Software") is  | 
|  * owned by Cypress Semiconductor Corporation ("Cypress") and is  | 
|  * protected by and subject to worldwide patent protection (UnitedStates and foreign), United States copyright laws and international  | 
|  * treaty provisions. Therefore, unless otherwise specified in a separate license agreement between you and Cypress, this Software  | 
|  * must be treated like any other copyrighted material. Reproduction,  | 
|  * modification, translation, compilation, or representation of this  | 
|  * Software in any other form (e.g., paper, magnetic, optical, silicon)  | 
|  * is prohibited without Cypress's express written permission.  | 
|  *  | 
|  * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY  | 
|  * KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,  | 
|  * NONINFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS  | 
|  * FOR A PARTICULAR PURPOSE. Cypress reserves the right to make changes  | 
|  * to the Software without notice. Cypress does not assume any liability  | 
|  * arising out of the application or use of Software or any product or  | 
|  * circuit described in the Software. Cypress does not authorize its  | 
|  * products for use as critical components in any products where a  | 
|  * malfunction or failure may reasonably be expected to result in  | 
|  * significant injury or death ("High Risk Product"). By including  | 
|  * Cypress's product in a High Risk Product, the manufacturer of such  | 
|  * system or application assumes all risk of such use and in doing so  | 
|  * indemnifies Cypress against all liability.  | 
|  *  | 
|  * Use of this Software may be limited by and subject to the applicable  | 
|  * Cypress software license agreement.  | 
|  *  | 
|  *  | 
|  */  | 
| #import "CSCModel.h"  | 
| #import "CBMoralManager.h"  | 
| #import "Constants.h"  | 
|   | 
| #define WHEEL_CIRCUMFERENCE    2.1  | 
|   | 
| /*!  | 
|  *  @class CSCModel  | 
|  *  | 
|  *  @discussion Class to handle the cycling speed and cadence service related operations  | 
|  *  | 
|  */  | 
|   | 
| @interface CSCModel () <cbCharacteristicManagerDelegate>  | 
| {  | 
|     void(^cbCharacteristicHandler)(BOOL success, NSError *error);  | 
|     void(^cbCharacteristicDiscoverHandler)(BOOL success, NSError *error);  | 
|       | 
|     CBCharacteristic *CSCCharacteristic;  | 
|       | 
|     float previousRevolution, previousEventTime;  | 
| }  | 
|   | 
|   | 
| @end  | 
|   | 
| @implementation CSCModel  | 
|   | 
|   | 
| @synthesize coveredDistance;  | 
| @synthesize cadence;  | 
|   | 
|   | 
| - (instancetype)init  | 
| {  | 
|     self = [super init];  | 
|     if (self) {  | 
|           | 
|         previousEventTime = 0.0;  | 
|         previousRevolution = 0.0;  | 
|     }  | 
|     return self;  | 
| }  | 
| /*!  | 
|  *  @method startDiscoverChar:  | 
|  *  | 
|  *  @discussion Discovers the specified characteristics of a service..  | 
|  */  | 
|   | 
| -(void)startDiscoverChar:(void (^) (BOOL success, NSError *error))handler  | 
| {  | 
|     cbCharacteristicDiscoverHandler = handler;  | 
|     [[CBMoralManager sharedManager] setCbCharacteristicDelegate:self];  | 
|     [[[CBMoralManager sharedManager] myPeripheral] discoverCharacteristics:nil forService:[[CBMoralManager sharedManager] myService]];  | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method updateCharacteristicWithHandler:  | 
|  *  | 
|  *  @discussion Sets notifications or indications for the value of a specified characteristic.  | 
|  */  | 
|   | 
|   | 
| -(void)updateCharacteristicWithHandler:(void (^) (BOOL success, NSError *error))handler  | 
| {  | 
|     cbCharacteristicHandler = handler;  | 
|       | 
|     if (CSCCharacteristic)  | 
|     {  | 
|         [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:CSC_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:CSC_CHARACTERISTIC_UUID] descriptor:nil operation:START_NOTIFY];  | 
|         [[[CBMoralManager sharedManager] myPeripheral] setNotifyValue:YES forCharacteristic:CSCCharacteristic];  | 
|     }  | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method stopUpdate  | 
|  *  | 
|  *  @discussion Stop notifications or indications for the value of a specified characteristic.  | 
|  */  | 
|   | 
| -(void)stopUpdate  | 
| {  | 
|     cbCharacteristicHandler = nil;  | 
|       | 
|     if (CSCCharacteristic)  | 
|     {  | 
|         if (CSCCharacteristic.isNotifying)  | 
|         {  | 
|             [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:CSC_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:CSC_CHARACTERISTIC_UUID] descriptor:nil operation:STOP_NOTIFY];  | 
|             [[[CBMoralManager sharedManager] myPeripheral] setNotifyValue:NO forCharacteristic:CSCCharacteristic];  | 
|         }  | 
|     }  | 
| }  | 
|   | 
| #pragma mark - CBCharacteristicManager delegate  | 
|   | 
| /*!  | 
|  *  @method peripheral: didDiscoverCharacteristicsForService: error:  | 
|  *  | 
|  *  @discussion Method invoked when characteristics are discovered for a service  | 
|  *  | 
|  */  | 
| -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error  | 
| {  | 
|     if ([service.UUID isEqual:CSC_SERVICE_UUID])  | 
|     {  | 
|         for (CBCharacteristic *aChar in service.characteristics)  | 
|         {  | 
|             // Checking for the required characteristic  | 
|             if ([aChar.UUID isEqual:CSC_CHARACTERISTIC_UUID])  | 
|             {  | 
|                 CSCCharacteristic = aChar ;  | 
|                 cbCharacteristicDiscoverHandler(YES,nil);  | 
|             }  | 
|         }  | 
|         cbCharacteristicDiscoverHandler(NO,error);  | 
|   | 
|     }  | 
|     else  | 
|     {  | 
|         cbCharacteristicDiscoverHandler(NO,error);  | 
|     }  | 
|       | 
| }  | 
|   | 
| /*!  | 
|  *  @method peripheral: didUpdateValueForCharacteristic: error:  | 
|  *  | 
|  *  @discussion Method invoked when the characteristic value changes  | 
|  *  | 
|  */  | 
|   | 
| -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error  | 
| {  | 
|     if ([characteristic.UUID isEqual:CSC_CHARACTERISTIC_UUID])  | 
|     {  | 
|         if(error == nil)  | 
|         {  | 
|             [self getCSCData:characteristic]; // Parse the data received from the characteristic  | 
|             cbCharacteristicHandler(YES,nil);  | 
|         }  | 
|         else  | 
|         {  | 
|             cbCharacteristicHandler(NO,error);  | 
|         }  | 
|     }  | 
| }  | 
|   | 
|   | 
| /*!  | 
|  *  @method getCSCData:  | 
|  *  | 
|  *  @discussion  Method to parse the characteristic value. The CSC Measurement characteristic (CSC refers to Cycling Speed and Cadence) is a variable length structure containing a Flags field and, based on the contents of the Flags field, may contain one or more additional fields.  | 
|  *  | 
|  */  | 
|   | 
| -(void) getCSCData:(CBCharacteristic *)characteristic  | 
| {  | 
|     NSData *data =[characteristic value];  | 
|     const uint8_t *reportData = [data bytes];  | 
|       | 
|     int bitPosition = 1;  | 
|     // Checking Cumulative Wheel Revolutions present  | 
|       | 
|     if ((reportData[0] & 0x01) == 1)  | 
|     {  | 
|         // Cumulative Wheel Revolutions present  | 
|         uint32_t  wheelRevolutionsCount = (uint32_t)CFSwapInt32LittleToHost(*(uint32_t *)&reportData[bitPosition]);  | 
|         bitPosition += 6;  | 
|           | 
|         if (wheelRevolutionsCount)  | 
|         {  | 
|             self.coveredDistance = (float)wheelRevolutionsCount * WHEEL_CIRCUMFERENCE;  | 
|         }  | 
|     }  | 
|     | 
|     if ((reportData[0] & 0x02) > 0)  | 
|     {  | 
|         // Cumulative Crank Revolutions present  | 
|         uint16_t CrankRevolutionsCount = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[bitPosition]));  | 
|         bitPosition += 2;  | 
|           | 
|         uint16_t LastEvent = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[bitPosition]));  | 
|         [self calculateRPMForCrankrevolutions:CrankRevolutionsCount eventTime:LastEvent];  | 
|     }  | 
|       | 
|     [Utilities logDataWithService:[ResourceHandler getServiceNameForUUID:CSC_SERVICE_UUID] characteristic:[ResourceHandler getCharacteristicNameForUUID:CSC_CHARACTERISTIC_UUID] descriptor:nil operation:[NSString stringWithFormat:@"%@%@ %@",NOTIFY_RESPONSE,DATA_SEPERATOR,[Utilities convertDataToLoggerFormat:data]]];  | 
|   | 
| }  | 
|   | 
| /*!  | 
|  *  @method calculateRPMForCrankrevolutions: eventTime:  | 
|  *  | 
|  *  @discussion Method to calculate rpm from crank revolutions  | 
|  *  | 
|  */  | 
| -(void) calculateRPMForCrankrevolutions:(float)currentRevolution eventTime:(float)lastEventTime  | 
| {  | 
|       | 
|     if (lastEventTime == previousEventTime)  | 
|     {  | 
|         return;  | 
|     }  | 
|       | 
|     if (previousEventTime == 0.0 && previousRevolution == 0.0)  | 
|     {  | 
|         previousEventTime = lastEventTime;  | 
|         previousRevolution = currentRevolution;  | 
|     }  | 
|     else  | 
|     {  | 
|         float timeDelta, rpm;  | 
|         if (lastEventTime > previousEventTime)  | 
|         {  | 
|             timeDelta = (lastEventTime - previousEventTime)/1024.0;  | 
|         }  | 
|         else  | 
|             timeDelta = ((65535.0 + lastEventTime) - previousEventTime)/1024.0;  | 
|           | 
|         rpm = (currentRevolution - previousRevolution) * (60.0 / timeDelta);  | 
|           | 
|         if (rpm > 0.0)  | 
|         {  | 
|             cadence = (int) rpm;  | 
|         }  | 
|           | 
|         previousEventTime = lastEventTime;  | 
|         previousRevolution = currentRevolution;  | 
|     }  | 
| }  | 
|   | 
|   | 
|   | 
|   | 
| @end  |