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; } }