VOID
HealthThermometerServiceContent::TemperatureMeasurementEvent(
    _In_ BTH_LE_GATT_EVENT_TYPE EventType,
    _In_ PVOID EventOutParameter
    )
{
    HRESULT hr = S_OK;
    PBLUETOOTH_GATT_VALUE_CHANGED_EVENT ValueChangedEventParameters = NULL;
    TEMPERATURE_MEASUREMENT  Measurement = {0};
    IPortableDeviceValues * pEventParams = NULL;
    BYTE* pBuffer = NULL;
    DWORD cbBuffer = 0;       

    if (CharacteristicValueChangedEvent != EventType) {
        return;
    }

    ValueChangedEventParameters = (PBLUETOOTH_GATT_VALUE_CHANGED_EVENT)EventOutParameter;    


    //
    // Our value is at least 5 bytes
    //
    if (5 > ValueChangedEventParameters->CharacteristicValue->DataSize) {
        hr = E_FAIL;
        CHECK_HR(hr, "Invalid data size: %d, expencting at least 5 bytes",
            ValueChangedEventParameters->CharacteristicValue->DataSize);
    }

    if (SUCCEEDED(hr))
    {
        ULONG mantissa = 0;
        LONG exponent = 0;
        
        mantissa = (ULONG)(((ULONG)ValueChangedEventParameters->CharacteristicValue->Data[3] << (ULONG)16)
                | ((ULONG)ValueChangedEventParameters->CharacteristicValue->Data[2] << (ULONG)8)
                | ((ULONG)ValueChangedEventParameters->CharacteristicValue->Data[1] << (ULONG)0));
        exponent = 0xFFFFFF00 | (LONG)ValueChangedEventParameters->CharacteristicValue->Data[4];

        Measurement.Value = (double)mantissa * pow((double)10.0, (double)exponent);
        TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "Received a value change event, new value [%.2f]", Measurement.Value);
    }
    
    //
    // Get the timestamp
    //
    if (SUCCEEDED(hr))
    {
        FILETIME fTime;

        GetSystemTimeAsFileTime(&fTime);

        ConvertFileTimeToUlonglong(&fTime, &Measurement.TimeStamp);
        
    }

    //
    // CoCreate a collection to store the property set event parameters.
    //
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pEventParams);
        CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
    }            

    if (SUCCEEDED(hr))
    {    
        hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, EVENT_HealthThermometerService_TemperatureMeasurement);
        CHECK_HR(hr, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");
    }

    //
    // Set the timestamp
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetUnsignedLargeIntegerValue(EVENT_PARAMETER_HealthTemperatureService_Measurement_TimeStamp, Measurement.TimeStamp);
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthTemperatureService_Measurement_TimeStamp");
    }

    //
    // Set the measurement Value
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetFloatValue(EVENT_PARAMETER_HealthTemperatureService_Measurement_Value, (float)Measurement.Value);
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthTemperatureService_Measurement_Value");
    }
    
    //
    // Adding this event parameter will allow WPD to scope this event to the container functional object    
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, ParentPersistentUniqueID);
        CHECK_HR(hr, "Failed to add WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID");
    }

    //
    // Adding this event parameter will allow WPD to scope this event to the container functional object
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, SERVICE_OBJECT_ID);
        CHECK_HR(hr, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID");
    }
    
    //
    // Create a buffer with the serialized parameters
    //
    if (SUCCEEDED(hr))
    {
        hr = m_pWpdSerializer->GetBufferFromIPortableDeviceValues(pEventParams, &pBuffer, &cbBuffer);
        CHECK_HR(hr, "Failed to get buffer from IPortableDeviceValues");
    }

    if (SUCCEEDED(hr) && NULL == pBuffer)
    {
        hr = E_FAIL;
        CHECK_HR(hr, "pBuffer is NULL");
    }

    //
    // Send the event
    //
    if (SUCCEEDED(hr))
    {
        hr = m_pDevice->PostEvent(WPD_EVENT_NOTIFICATION, WdfEventBroadcast, pBuffer, cbBuffer);
        CHECK_HR(hr, "Failed to post WPD (broadcast) event");
    }
    
    
    //
    // Cleanup
    //
    if (NULL != pBuffer)
    {
        CoTaskMemFree(pBuffer);
        pBuffer = NULL;
    }

    if (NULL != pEventParams)
    {
        pEventParams->Release();
        pEventParams = NULL;
    }
}
VOID 
HealthBloodPressureServiceContent::BloodPressureMeasurementEvent(
    _In_ BTH_LE_GATT_EVENT_TYPE EventType,
    _In_ PVOID EventOutParameter
    )
{
    HRESULT hr = S_OK;
    PBLUETOOTH_GATT_VALUE_CHANGED_EVENT ValueChangedEventParameters = NULL;
    USHORT temp = 0;
    BLOODPRESSURE_MEASUREMENT Measurement = {0};
    IPortableDeviceValues * pEventParams = NULL;
    BYTE* pBuffer = NULL;
    DWORD cbBuffer = 0;    

    if (CharacteristicValueChangedEvent != EventType) {
        return;
    }

    ValueChangedEventParameters = (PBLUETOOTH_GATT_VALUE_CHANGED_EVENT)EventOutParameter;    

    //
    // Our value is at least 7 bytes long
    //
    if (7 > ValueChangedEventParameters->CharacteristicValue->DataSize) {
        hr = E_FAIL;
        CHECK_HR(hr, "Invalid data size");
    }

    if (SUCCEEDED(hr))
    {

        //
        // The first byte is the Flag field
        //
        Measurement.Flags = ValueChangedEventParameters->CharacteristicValue->Data[0];

        //
        // Systolic
        //
        RtlRetrieveUshort(&temp, &ValueChangedEventParameters->CharacteristicValue->Data[1]);        
        Measurement.Systolic = UshortToFloat(temp);

        //
        // Diastolic
        //
        RtlRetrieveUshort(&temp, &ValueChangedEventParameters->CharacteristicValue->Data[3]);        
        Measurement.Diastolic = UshortToFloat(temp);

        //
        // Mean Arterial Pressure
        //
        RtlRetrieveUshort(&temp, &ValueChangedEventParameters->CharacteristicValue->Data[5]);        
        Measurement.MeanArterialPressure = UshortToFloat(temp);

    }
    
    //
    // Get the timestamp
    //
    if (SUCCEEDED(hr))
    {
        BOOL isTimeProcessed = FALSE;
        //
        // If the time is provided by the BloodPressureMeasurement, use it.
        // If this flag is set, the value must be at least 14 bytes long
        //
        if ((0x02 == (Measurement.Flags & 0x02)) &&
            (14 <= ValueChangedEventParameters->CharacteristicValue->DataSize)) {

            SYSTEMTIME systemTime = {0};
            FILETIME fTime = {0};

            //
            // This value is expressed in the org.bluetooth.characteristic.date_time format.
            //
            
            //
            // Year (2 bytes)
            //
            RtlRetrieveUshort(&temp, &ValueChangedEventParameters->CharacteristicValue->Data[7]);
            systemTime.wYear = temp;            

            //
            // Month (1 byte)
            //
            systemTime.wMonth = ValueChangedEventParameters->CharacteristicValue->Data[9];

            //
            // Day (1 byte)
            //
            systemTime.wDay = ValueChangedEventParameters->CharacteristicValue->Data[10]; 

            //
            // Hours (1 byte)
            //
            systemTime.wHour = ValueChangedEventParameters->CharacteristicValue->Data[11];

            //
            // Minutes (1 byte)
            //
            systemTime.wMinute = ValueChangedEventParameters->CharacteristicValue->Data[12];            

            //
            // Seconds (1 byte)
            //
            systemTime.wSecond = ValueChangedEventParameters->CharacteristicValue->Data[13];

            //
            // Convert into FILETIME
            //
            if (SystemTimeToFileTime(&systemTime, &fTime)) {
                
                ConvertFileTimeToUlonglong(&fTime, &Measurement.TimeStamp);
                isTimeProcessed = TRUE;
            }
        } 

        //
        // If the measurement didn't contain a timestamp or we failed to process it,
        // generate it based on the current time.
        //
        if (!isTimeProcessed) {

            FILETIME fTime;

            GetSystemTimeAsFileTime(&fTime);

            ConvertFileTimeToUlonglong(&fTime, &Measurement.TimeStamp);
        }
        
    }  

    //
    // CoCreate a collection to store the property set event parameters.
    //
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pEventParams);
        CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
    }            

    if (SUCCEEDED(hr))
    {    
        hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, EVENT_HealthBloodPressureService_Measurement);
        CHECK_HR(hr, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID");
    }

    //
    // Set the timestamp
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetUnsignedLargeIntegerValue(EVENT_PARAMETER_HealthBloodPressureService_Measurement_TimeStamp, Measurement.TimeStamp);
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthBloodPressureService_Measurement_TimeStamp");
    }
    
    //
    // Set the measurement type
    //
    if (SUCCEEDED(hr))
    {
        if (0x01 == (Measurement.Flags & 0x01)) {
            hr = pEventParams->SetStringValue(EVENT_PARAMETER_HealthBloodPressureService_Measurement_Type, L"kPa");
        } else {
            hr = pEventParams->SetStringValue(EVENT_PARAMETER_HealthBloodPressureService_Measurement_Type, L"mmHg");            
        }
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthBloodPressureService_Measurement_Type");
    }
    
    //
    // Set the Systolic value
    //
    if (SUCCEEDED(hr))
    {
        float ftemp = (float)Measurement.Systolic;
        hr = pEventParams->SetFloatValue(EVENT_PARAMETER_HealthBloodPressureService_Measurement_Systolic, ftemp);
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthBloodPressureService_Measurement_Systolic");        
    }
    
    //
    // Set the Diastolic value
    //
    if (SUCCEEDED(hr))
    {
        float ftemp = (float)Measurement.Diastolic;    
        hr = pEventParams->SetFloatValue(EVENT_PARAMETER_HealthBloodPressureService_Measurement_Diastolic, ftemp);
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthBloodPressureService_Measurement_Diastolic");        
    }    
    
    //
    // Set the Mean Arterial Pressure value
    //
    if (SUCCEEDED(hr))
    {
        float ftemp = (float)Measurement.MeanArterialPressure;
        hr = pEventParams->SetFloatValue(EVENT_PARAMETER_HealthBloodPressureService_Measurement_MeanArterialPressure, ftemp);
        CHECK_HR(hr, "Failed to add EVENT_PARAMETER_HealthBloodPressureService_Measurement_MeanArterialPressure");        
    }
    
    //
    // Adding this event parameter will allow WPD to scope this event to the container functional object    
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, ParentPersistentUniqueID);
        CHECK_HR(hr, "Failed to add WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID");
    }

    //
    // Adding this event parameter will allow WPD to scope this event to the container functional object
    //
    if (SUCCEEDED(hr))
    {
        hr = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, SERVICE_OBJECT_ID);
        CHECK_HR(hr, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID");
    }
    
    //
    // Create a buffer with the serialized parameters
    //
    if (SUCCEEDED(hr))
    {
        hr = m_pWpdSerializer->GetBufferFromIPortableDeviceValues(pEventParams, &pBuffer, &cbBuffer);
        CHECK_HR(hr, "Failed to get buffer from IPortableDeviceValues");
    }

    if (SUCCEEDED(hr) && NULL == pBuffer)
    {
        hr = E_FAIL;
        CHECK_HR(hr, "pBuffer is NULL");
    }

    //
    // Send the event
    //
    if (SUCCEEDED(hr))
    {
        hr = m_pDevice->PostEvent(WPD_EVENT_NOTIFICATION, WdfEventBroadcast, pBuffer, cbBuffer);
        CHECK_HR(hr, "Failed to post WPD (broadcast) event");
    }
    
    
    //
    // Cleanup
    //
    if (NULL != pBuffer)
    {
        CoTaskMemFree(pBuffer);
        pBuffer = NULL;
    }

    if (NULL != pEventParams)
    {
        pEventParams->Release();
        pEventParams = NULL;
    }
}