Created
August 8, 2024 12:02
-
-
Save erdesigns-eu/1c95706c73ea25a36e7c0adfa392f147 to your computer and use it in GitHub Desktop.
Updated BLEServer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// BLEServer.cpp | |
// | |
// Copyright (C) 2024, Ernst Reidinga | |
// Copyright (C) 2017, Uri Shaked | |
// | |
#include "stdafx.h" | |
#include <iostream> | |
#include <Windows.Foundation.h> | |
#include <Windows.Devices.Bluetooth.h> | |
#include <Windows.Devices.Bluetooth.Advertisement.h> | |
#include <Windows.Devices.Radios.h> | |
#include <Windows.Data.JSON.h> | |
#include <wrl/wrappers/corewrappers.h> | |
#include <wrl/event.h> | |
#include <collection.h> | |
#include <ppltasks.h> | |
#include <string> | |
#include <sstream> | |
#include <iomanip> | |
#include <experimental/resumable> | |
#include <pplawait.h> | |
#include <cvt/wstring> | |
#include <codecvt> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <io.h> | |
using namespace Platform; | |
using namespace Windows::Foundation::Collections; | |
using namespace Windows::Devices; | |
using namespace Windows::Devices::Radios; | |
using namespace Windows::Data::Json; | |
using namespace concurrency; | |
Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher^ bleAdvertisementWatcher; | |
auto devices = ref new Collections::Map<String^, Bluetooth::BluetoothLEDevice^>(); | |
auto characteristicsMap = ref new Collections::Map<String^, Bluetooth::GenericAttributeProfile::GattCharacteristic^>(); | |
auto characteristicsListenerMap = ref new Collections::Map<String^, Windows::Foundation::EventRegistrationToken>(); | |
auto characteristicsSubscriptionMap = ref new Collections::Map<String^, JsonValue^>(); | |
std::wstring formatBluetoothAddress(unsigned long long BluetoothAddress) { | |
std::wostringstream ret; | |
ret << std::hex << std::setfill(L'0') | |
<< std::setw(2) << ((BluetoothAddress >> (5 * 8)) & 0xff) << ":" | |
<< std::setw(2) << ((BluetoothAddress >> (4 * 8)) & 0xff) << ":" | |
<< std::setw(2) << ((BluetoothAddress >> (3 * 8)) & 0xff) << ":" | |
<< std::setw(2) << ((BluetoothAddress >> (2 * 8)) & 0xff) << ":" | |
<< std::setw(2) << ((BluetoothAddress >> (1 * 8)) & 0xff) << ":" | |
<< std::setw(2) << ((BluetoothAddress >> (0 * 8)) & 0xff); | |
return ret.str(); | |
} | |
Guid parseUuid(String^ uuid) { | |
if (uuid->Length() == 4) { | |
unsigned int uuidShort = std::stoul(uuid->Data(), 0, 16); | |
return Bluetooth::BluetoothUuidHelper::FromShortId(uuidShort); | |
} | |
GUID rawguid; | |
if (SUCCEEDED(IIDFromString(uuid->Data(), &rawguid))) { | |
return Guid(rawguid); | |
} | |
else { | |
std::wstring msg = L"Invalid UUID: "; | |
msg += uuid->Data(); | |
throw ref new InvalidArgumentException(ref new String(msg.c_str())); | |
} | |
} | |
CRITICAL_SECTION OutputCriticalSection; | |
void writeObject(JsonObject^ jsonObject) { | |
String^ jsonString = jsonObject->Stringify(); | |
stdext::cvt::wstring_convert<std::codecvt_utf8<wchar_t>> convert; | |
std::string stringUtf8 = convert.to_bytes(jsonString->Data()); | |
auto len = stringUtf8.length(); | |
EnterCriticalSection(&OutputCriticalSection); | |
std::cout << char(len >> 0) | |
<< char(len >> 8) | |
<< char(len >> 16) | |
<< char(len >> 24); | |
std::cout << stringUtf8 << std::flush; | |
LeaveCriticalSection(&OutputCriticalSection); | |
} | |
concurrency::task<IJsonValue^> connectRequest(JsonObject ^command) { | |
String ^addressStr = command->GetNamedString("address", ""); | |
unsigned long long address = std::stoull(addressStr->Data(), 0, 16); | |
auto device = co_await Bluetooth::BluetoothLEDevice::FromBluetoothAddressAsync(address); | |
if (device == nullptr) { | |
throw ref new FailureException(ref new String(L"Device not found (null)")); | |
} | |
devices->Insert(device->DeviceId, device); | |
device->ConnectionStatusChanged += ref new Windows::Foundation::TypedEventHandler<Bluetooth::BluetoothLEDevice ^, Platform::Object ^>( | |
[](Windows::Devices::Bluetooth::BluetoothLEDevice^ device, Platform::Object^ eventArgs) { | |
if (device->ConnectionStatus == Bluetooth::BluetoothConnectionStatus::Disconnected) { | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("disconnectEvent")); | |
msg->Insert("device", JsonValue::CreateStringValue(device->DeviceId)); | |
writeObject(msg); | |
devices->Remove(device->DeviceId); | |
} | |
}); | |
return JsonValue::CreateStringValue(device->DeviceId); | |
} | |
Concurrency::task<IJsonValue^> disconnectRequest(JsonObject ^command) { | |
String ^deviceId = command->GetNamedString("device", ""); | |
if (!devices->HasKey(deviceId)) { | |
throw ref new FailureException(ref new String(L"Device not found")); | |
} | |
Bluetooth::BluetoothLEDevice^ device = devices->Lookup(deviceId); | |
// When disconnecting from a device, also remove all the characteristics from our cache. | |
auto newCharacteristicsMap = ref new Collections::Map<String^, Bluetooth::GenericAttributeProfile::GattCharacteristic^>(); | |
for (auto pair : characteristicsMap) | |
{ | |
bool removed = true; | |
try { | |
auto service = pair->Value->Service; | |
if (service->Device->DeviceId->Equals(device->DeviceId)) { | |
delete service->Device; | |
delete service; | |
} | |
else { | |
newCharacteristicsMap->Insert(pair->Key, pair->Value); | |
removed = false; | |
} | |
} | |
catch (...) { | |
// Service is probably already closed, so we just skip it and it will be removed from the list | |
} | |
if (removed) { | |
if (characteristicsListenerMap->HasKey(pair->Key)) { | |
characteristicsListenerMap->Remove(pair->Key); | |
} | |
if (characteristicsSubscriptionMap->HasKey(pair->Key)) { | |
characteristicsSubscriptionMap->Remove(pair->Key); | |
} | |
} | |
} | |
characteristicsMap = newCharacteristicsMap; | |
devices->Remove(deviceId); | |
return Concurrency::task_from_result<IJsonValue^>(JsonValue::CreateNullValue()); | |
} | |
concurrency::task<Bluetooth::GenericAttributeProfile::GattDeviceServicesResult^> findServices(JsonObject ^command) { | |
String ^deviceId = command->GetNamedString("device", ""); | |
if (!devices->HasKey(deviceId)) { | |
throw ref new FailureException(ref new String(L"Device not found")); | |
} | |
Bluetooth::BluetoothLEDevice^ device = devices->Lookup(deviceId); | |
if (command->HasKey("service")) { | |
return co_await device->GetGattServicesForUuidAsync(parseUuid(command->GetNamedString("service"))); | |
} | |
else { | |
return co_await device->GetGattServicesAsync(); | |
} | |
} | |
String^ characteristicKey(String^ device, String^ service, String^ characteristic) { | |
std::wstring result = device->Data(); | |
result += L"//"; | |
result += service->Data(); | |
result += L"//"; | |
result += characteristic->Data(); | |
return ref new String(result.c_str()); | |
} | |
String^ characteristicKey(JsonObject ^command) { | |
return characteristicKey(command->GetNamedString("device"), command->GetNamedString("service"), command->GetNamedString("characteristic")); | |
} | |
concurrency::task<Bluetooth::GenericAttributeProfile::GattCharacteristicsResult^> findCharacteristics(JsonObject ^command) { | |
if (!command->HasKey("service")) { | |
throw ref new InvalidArgumentException(ref new String(L"Service uuid must be provided")); | |
} | |
auto servicesResult = co_await findServices(command); | |
auto services = servicesResult->Services; | |
if (services->Size == 0) { | |
throw ref new FailureException(ref new String(L"Requested service not found")); | |
} | |
auto service = services->GetAt(0); | |
auto results = co_await service->GetCharacteristicsAsync(); | |
for (unsigned int i = 0; i < results->Characteristics->Size; i++) { | |
auto characteristic = results->Characteristics->GetAt(i); | |
auto key = characteristicKey(command->GetNamedString("device"), command->GetNamedString("service"), characteristic->Uuid.ToString()); | |
characteristicsMap->Insert(key, characteristic); | |
} | |
return results; | |
} | |
concurrency::task<Bluetooth::GenericAttributeProfile::GattCharacteristic^> getCharacteristic(JsonObject ^command) { | |
if (!command->HasKey("characteristic")) { | |
throw ref new InvalidArgumentException(ref new String(L"Characteristic uuid must be provided")); | |
} | |
auto key = characteristicKey(command); | |
if (!characteristicsMap->HasKey(key)) { | |
co_await findCharacteristics(command); | |
} | |
if (characteristicsMap->HasKey(key)) { | |
return characteristicsMap->Lookup(key); | |
} | |
throw ref new FailureException(ref new String(L"Requested characteristic not found")); | |
} | |
concurrency::task<IJsonValue^> servicesRequest(JsonObject ^command) { | |
auto servicesResult = co_await findServices(command); | |
auto result = ref new JsonArray(); | |
for (unsigned int i = 0; i < servicesResult->Services->Size; i++) { | |
result->Append(JsonValue::CreateStringValue(servicesResult->Services->GetAt(i)->Uuid.ToString())); | |
} | |
return result; | |
} | |
concurrency::task<IJsonValue^> charactersticsRequest(JsonObject ^command) { | |
auto characteristicsResult = co_await findCharacteristics(command); | |
auto result = ref new JsonArray(); | |
for (unsigned int i = 0; i < characteristicsResult->Characteristics->Size; i++) { | |
auto characteristic = characteristicsResult->Characteristics->GetAt(i); | |
auto characteristicJson = ref new JsonObject(); | |
auto properties = ref new JsonObject(); | |
auto props = (unsigned int)characteristic->CharacteristicProperties; | |
properties->SetNamedValue("broadcast", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Broadcast)); | |
properties->SetNamedValue("read", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Read)); | |
properties->SetNamedValue("writeWithoutResponse", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::WriteWithoutResponse)); | |
properties->SetNamedValue("write", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Write)); | |
properties->SetNamedValue("notify", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Notify)); | |
properties->SetNamedValue("indicate", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Indicate)); | |
properties->SetNamedValue("authenticatedSignedWrites", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::AuthenticatedSignedWrites)); | |
properties->SetNamedValue("reliableWrite", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::ReliableWrites)); | |
properties->SetNamedValue("writableAuxiliaries", JsonValue::CreateBooleanValue(props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::WritableAuxiliaries)); | |
characteristicJson->SetNamedValue("uuid", JsonValue::CreateStringValue(characteristic->Uuid.ToString())); | |
characteristicJson->SetNamedValue("properties", properties); | |
result->Append(characteristicJson); | |
} | |
return result; | |
} | |
concurrency::task<IJsonValue^> readRequest(JsonObject ^command) { | |
auto characteristic = co_await getCharacteristic(command); | |
auto result = co_await characteristic->ReadValueAsync(); | |
if (result->Status != Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) { | |
throw ref new FailureException(result->Status.ToString()); | |
} | |
auto reader = Windows::Storage::Streams::DataReader::FromBuffer(result->Value); | |
auto valueArray = ref new JsonArray(); | |
for (unsigned int i = 0; i < result->Value->Length; i++) { | |
valueArray->Append(JsonValue::CreateNumberValue(reader->ReadByte())); | |
} | |
return valueArray; | |
} | |
concurrency::task<IJsonValue^> writeRequest(JsonObject ^command) { | |
auto characteristic = co_await getCharacteristic(command); | |
auto writer = ref new Windows::Storage::Streams::DataWriter(); | |
auto dataArray = command->GetNamedArray("value"); | |
for (unsigned int i = 0; i < dataArray->Size; i++) { | |
writer->WriteByte((unsigned char)dataArray->GetNumberAt(i)); | |
} | |
bool writeWithoutResponse = (unsigned int)characteristic->CharacteristicProperties & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::WriteWithoutResponse; | |
auto writeType = writeWithoutResponse ? Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithoutResponse : Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithResponse; | |
auto status = co_await characteristic->WriteValueAsync(writer->DetachBuffer(), writeType); | |
if (status != Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) { | |
throw ref new FailureException(status.ToString()); | |
} | |
return JsonValue::CreateNullValue(); | |
} | |
unsigned long nextSubscriptionId = 1; | |
concurrency::task<IJsonValue^> subscribeRequest(JsonObject ^command) { | |
auto characteristic = co_await getCharacteristic(command); | |
auto props = (unsigned int)characteristic->CharacteristicProperties; | |
if (props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Notify) { | |
auto status = co_await characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::Notify); | |
if (status != Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) | |
{ | |
throw ref new FailureException(status.ToString()); | |
} | |
} | |
else if (props & (unsigned int)Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Indicate) { | |
auto status = co_await characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::Indicate); | |
if (status != Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) | |
{ | |
throw ref new FailureException(status.ToString()); | |
} | |
} | |
else { | |
throw ref new FailureException("Operation not supported."); | |
} | |
auto key = characteristicKey(command); | |
if (characteristicsSubscriptionMap->HasKey(key)) { | |
return characteristicsSubscriptionMap->Lookup(key); | |
} | |
auto subscriptionId = JsonValue::CreateNumberValue(nextSubscriptionId++); | |
Windows::Foundation::EventRegistrationToken cookie = | |
characteristic->ValueChanged += ref new Windows::Foundation::TypedEventHandler<Bluetooth::GenericAttributeProfile::GattCharacteristic^, Bluetooth::GenericAttributeProfile::GattValueChangedEventArgs ^>( | |
[subscriptionId](Bluetooth::GenericAttributeProfile::GattCharacteristic^ characteristic, Bluetooth::GenericAttributeProfile::GattValueChangedEventArgs^ eventArgs) { | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("valueChangedNotification")); | |
msg->Insert("subscriptionId", subscriptionId); | |
auto reader = Windows::Storage::Streams::DataReader::FromBuffer(eventArgs->CharacteristicValue); | |
auto valueArray = ref new JsonArray(); | |
for (unsigned int i = 0; i < eventArgs->CharacteristicValue->Length; i++) { | |
valueArray->Append(JsonValue::CreateNumberValue(reader->ReadByte())); | |
} | |
msg->Insert("value", valueArray); | |
writeObject(msg); | |
}); | |
characteristicsListenerMap->Insert(key, cookie); | |
characteristicsSubscriptionMap->Insert(key, subscriptionId); | |
return subscriptionId; | |
} | |
concurrency::task<IJsonValue^> unsubscribeRequest(JsonObject ^command) { | |
auto characteristic = co_await getCharacteristic(command); | |
auto status = co_await characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::None); | |
if (status != Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) | |
{ | |
throw ref new FailureException(status.ToString()); | |
} | |
auto key = characteristicKey(command); | |
if (characteristicsListenerMap->HasKey(key)) { | |
characteristic->ValueChanged -= characteristicsListenerMap->Lookup(key); | |
} | |
auto subscriptionId = characteristicsSubscriptionMap->Lookup(key); | |
characteristicsListenerMap->Remove(key); | |
characteristicsSubscriptionMap->Remove(key); | |
return subscriptionId; | |
} | |
concurrency::task<void> processCommand(JsonObject ^command) { | |
String ^cmd = command->GetNamedString("cmd", ""); | |
JsonObject^ response = ref new JsonObject(); | |
IJsonValue^ result = nullptr; | |
response->Insert("_type", JsonValue::CreateStringValue("response")); | |
response->Insert("_id", command->GetNamedValue("_id", JsonValue::CreateNullValue())); | |
try { | |
if (cmd->Equals("ping")) { | |
result = JsonValue::CreateStringValue("pong"); | |
} | |
if (cmd->Equals("scan")) { | |
bleAdvertisementWatcher->Start(); | |
result = JsonValue::CreateNullValue(); | |
} | |
if (cmd->Equals("stopScan")) { | |
bleAdvertisementWatcher->Stop(); | |
result = JsonValue::CreateNullValue(); | |
} | |
if (cmd->Equals("connect")) { | |
result = co_await connectRequest(command); | |
} | |
if (cmd->Equals("disconnect")) { | |
result = co_await disconnectRequest(command); | |
} | |
if (cmd->Equals("services")) { | |
result = co_await servicesRequest(command); | |
} | |
if (cmd->Equals("characteristics")) { | |
result = co_await charactersticsRequest(command); | |
} | |
if (cmd->Equals("read")) { | |
result = co_await readRequest(command); | |
} | |
if (cmd->Equals("write")) { | |
result = co_await writeRequest(command); | |
} | |
if (cmd->Equals("subscribe")) { | |
result = co_await subscribeRequest(command); | |
} | |
if (cmd->Equals("unsubscribe")) { | |
result = co_await unsubscribeRequest(command); | |
} | |
if (result != nullptr) { | |
response->Insert("result", result); | |
} | |
else { | |
response->Insert("error", JsonValue::CreateStringValue("Unknown command")); | |
} | |
writeObject(response); | |
} | |
catch (Exception^ e) { | |
response->Insert("error", JsonValue::CreateStringValue(e->ToString())); | |
writeObject(response); | |
} | |
catch (...) { | |
response->Insert("error", JsonValue::CreateStringValue("Unknown error")); | |
writeObject(response); | |
} | |
} | |
int main(Array<String^>^ args) { | |
Microsoft::WRL::Wrappers::RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); | |
CoInitializeSecurity( | |
nullptr, // This security descriptor string defines who has access to the COM object. | |
-1, | |
nullptr, | |
nullptr, | |
RPC_C_AUTHN_LEVEL_DEFAULT, | |
RPC_C_IMP_LEVEL_IDENTIFY, | |
NULL, | |
EOAC_NONE, | |
nullptr); | |
if (!InitializeCriticalSectionAndSpinCount(&OutputCriticalSection, 0x00000400)) { | |
return -1; | |
} | |
bleAdvertisementWatcher = ref new Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher(); | |
bleAdvertisementWatcher->ScanningMode = Bluetooth::Advertisement::BluetoothLEScanningMode::Active; | |
bleAdvertisementWatcher->Received += ref new Windows::Foundation::TypedEventHandler<Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher ^, Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs ^>( | |
[](Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher ^watcher, Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs^ eventArgs) { | |
unsigned int index = -1; | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("scanResult")); | |
msg->Insert("bluetoothAddress", JsonValue::CreateStringValue(ref new String(formatBluetoothAddress(eventArgs->BluetoothAddress).c_str()))); | |
msg->Insert("rssi", JsonValue::CreateNumberValue(eventArgs->RawSignalStrengthInDBm)); | |
// Timestamp calculation | |
msg->Insert("timestamp", JsonValue::CreateNumberValue(eventArgs->Timestamp.UniversalTime / 10000.0 + 11644480800000)); | |
msg->Insert("advType", JsonValue::CreateStringValue(eventArgs->AdvertisementType.ToString())); | |
msg->Insert("localName", JsonValue::CreateStringValue(eventArgs->Advertisement->LocalName)); | |
JsonArray^ serviceUuids = ref new JsonArray(); | |
for (unsigned int i = 0; i < eventArgs->Advertisement->ServiceUuids->Size; i++) { | |
serviceUuids->Append(JsonValue::CreateStringValue(eventArgs->Advertisement->ServiceUuids->GetAt(i).ToString())); | |
} | |
msg->Insert("serviceUuids", serviceUuids); | |
// Manufacturer data | |
JsonArray^ manufacturerDataArray = ref new JsonArray(); | |
for (auto manufacturerData : eventArgs->Advertisement->ManufacturerData) { | |
JsonObject^ manufacturerDataJson = ref new JsonObject(); | |
manufacturerDataJson->Insert("companyId", JsonValue::CreateNumberValue(manufacturerData->CompanyId)); | |
auto data = ref new JsonArray(); | |
auto reader = Windows::Storage::Streams::DataReader::FromBuffer(manufacturerData->Data); | |
while (reader->UnconsumedBufferLength > 0) { | |
data->Append(JsonValue::CreateNumberValue(reader->ReadByte())); | |
} | |
manufacturerDataJson->Insert("data", data); | |
manufacturerDataArray->Append(manufacturerDataJson); | |
} | |
msg->Insert("manufacturerData", manufacturerDataArray); | |
// Data sections | |
JsonArray^ dataSections = ref new JsonArray(); | |
for (auto dataSection : eventArgs->Advertisement->DataSections) { | |
JsonObject^ dataSectionJson = ref new JsonObject(); | |
dataSectionJson->Insert("dataType", JsonValue::CreateNumberValue(dataSection->DataType)); | |
auto data = ref new JsonArray(); | |
auto reader = Windows::Storage::Streams::DataReader::FromBuffer(dataSection->Data); | |
while (reader->UnconsumedBufferLength > 0) { | |
data->Append(JsonValue::CreateNumberValue(reader->ReadByte())); | |
} | |
dataSectionJson->Insert("data", data); | |
dataSections->Append(dataSectionJson); | |
} | |
msg->Insert("dataSections", dataSections); | |
writeObject(msg); | |
}); | |
// Added the ability to track the BLE Radio State | |
auto getRadiosOperation = Radio::GetRadiosAsync(); | |
create_task(getRadiosOperation).then( | |
[](task<IVectorView<Radio^>^> asyncInfo) | |
{ | |
auto radios = asyncInfo.get(); | |
for (Windows::Devices::Radios::Radio^ radio : radios) | |
{ | |
if (radio->Kind == RadioKind::Bluetooth) | |
{ | |
if (radio->State == Windows::Devices::Radios::RadioState::On) | |
{ | |
// Ble Radio On | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("Start")); | |
writeObject(msg); | |
} | |
else | |
{ | |
// Ble Radio Off | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("Stop")); | |
writeObject(msg); | |
} | |
radio->StateChanged += ref new Windows::Foundation::TypedEventHandler<Windows::Devices::Radios::Radio^, Platform::Object^>( | |
[](Windows::Devices::Radios::Radio^ sender, Platform::Object^ args) | |
{ | |
if (sender->State == Windows::Devices::Radios::RadioState::On) | |
{ | |
// Ble Radio Switched On | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("Start")); | |
writeObject(msg); | |
} | |
else | |
{ | |
// Ble Radio Switched Off | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("Stop")); | |
writeObject(msg); | |
} | |
} | |
); | |
} | |
} | |
} | |
); | |
// Set STDIN / STDOUT to binary mode | |
if ((_setmode(0, _O_BINARY) == -1) || (_setmode(1, _O_BINARY) == -1)) { | |
return -1; | |
} | |
stdext::cvt::wstring_convert<std::codecvt_utf8<wchar_t>> convert; | |
try { | |
while (!std::cin.eof()) { | |
unsigned int len = 0; | |
std::cin.read(reinterpret_cast<char *>(&len), 4); | |
if (len > 0) { | |
char *msgBuf = new char[len]; | |
std::cin.read(msgBuf, len); | |
String^ jsonStr = ref new String(convert.from_bytes(msgBuf, msgBuf + len).c_str()); | |
delete[] msgBuf; | |
JsonObject^ json = JsonObject::Parse(jsonStr); | |
processCommand(json); | |
} | |
} | |
} | |
catch (std::exception &e) { | |
JsonObject^ msg = ref new JsonObject(); | |
msg->Insert("_type", JsonValue::CreateStringValue("error")); | |
std::string eReason = std::string(e.what()); | |
std::wstring wReason = std::wstring(eReason.begin(), eReason.end()); | |
msg->Insert("error", JsonValue::CreateStringValue(ref new String(wReason.c_str()))); | |
writeObject(msg); | |
} | |
DeleteCriticalSection(&OutputCriticalSection); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment