PS_Win_BLE : fix erreurs de compilation WinRT
- Suppression des 'using namespace Windows::*' au scope global (conflit avec le namespace Windows d'Unreal via AllowWindowsPlatformTypes) - Remplacement par un macro PS_BLE_WINRT_NS avec aliases locaux (WinBT, WinAdv, WinGAP, WinStr) utilisés dans chaque fonction - Ajout de FPS_BLEDeviceHandle et FPS_GattServiceHandle : wrappers heap-alloués pour les types WinRT qui suppriment operator new - Suppression warning C4265 (dtor non-virtual interne aux headers WinRT) - Plugin charge sans erreur dans UE 5.5 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d593bbd9fd
commit
113eddef46
@ -17,6 +17,10 @@
|
||||
"TargetAllowList": [
|
||||
"Editor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "PS_Win_BLE",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -49,6 +49,13 @@ public class PS_Win_BLE : ModuleRules
|
||||
"runtimeobject.lib" // RoInitialize / WinRT activation
|
||||
});
|
||||
|
||||
// C++/WinRT headers (winrt/Windows.Devices.Bluetooth.h, etc.)
|
||||
// Located in the Windows SDK — we pick the version used by UE5.5
|
||||
string WinSDKDir = "C:/Program Files (x86)/Windows Kits/10";
|
||||
string WinSDKVersion = "10.0.22621.0";
|
||||
PrivateIncludePaths.Add(Path.Combine(WinSDKDir, "Include", WinSDKVersion, "cppwinrt"));
|
||||
PrivateIncludePaths.Add(Path.Combine(WinSDKDir, "Include", WinSDKVersion, "winrt"));
|
||||
|
||||
// Allow WinRT headers in C++ code
|
||||
bEnableExceptions = true;
|
||||
}
|
||||
|
||||
@ -4,18 +4,6 @@
|
||||
#include "PS_BLEModule.h"
|
||||
#include "PS_BLEManager.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4668 4946 5204 5220)
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#pragma warning(pop)
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
using namespace winrt;
|
||||
#endif
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
UPS_BLE_Device::UPS_BLE_Device()
|
||||
|
||||
@ -8,15 +8,19 @@
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
// ─── WinRT includes (Windows only) ───────────────────────────────────────────
|
||||
// NOTE: AllowWindowsPlatformTypes must wrap the WinRT headers.
|
||||
// We do NOT place any "using namespace" at file scope because Unreal also
|
||||
// defines a ::Windows namespace (via AllowWindowsPlatformTypes) and that would
|
||||
// cause "ambiguous symbol" errors. All WinRT types are fully qualified with
|
||||
// winrt:: inside each function body.
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
// Unreal defines WIN32_LEAN_AND_MEAN which can strip some COM headers — we
|
||||
// add the specific ones we need without touching the Unreal macros.
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4668 4946 5204 5220)
|
||||
#pragma warning(disable: 4265 4668 4946 5204 5220) // 4265: WinRT internal non-virtual dtor (harmless)
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
@ -30,22 +34,38 @@
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::Devices::Bluetooth;
|
||||
using namespace Windows::Devices::Bluetooth::Advertisement;
|
||||
using namespace Windows::Devices::Bluetooth::GenericAttributeProfile;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Storage::Streams;
|
||||
// ─── Convenient namespace aliases used ONLY inside function bodies ────────────
|
||||
// (Defined as macros so we can paste them at the top of each #if PLATFORM_WINDOWS block)
|
||||
#define PS_BLE_WINRT_NS \
|
||||
namespace WinBT = winrt::Windows::Devices::Bluetooth; \
|
||||
namespace WinAdv = winrt::Windows::Devices::Bluetooth::Advertisement; \
|
||||
namespace WinGAP = winrt::Windows::Devices::Bluetooth::GenericAttributeProfile; \
|
||||
namespace WinFnd = winrt::Windows::Foundation; \
|
||||
namespace WinStr = winrt::Windows::Storage::Streams;
|
||||
|
||||
// ─── Scanner state (allocated on heap so WinRT types don't leak into header) ─
|
||||
// ─── Scanner state (heap-allocated so WinRT types don't appear in the header) ─
|
||||
struct FPS_ScannerState
|
||||
{
|
||||
BluetoothLEAdvertisementWatcher Watcher{ nullptr };
|
||||
winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher Watcher{ nullptr };
|
||||
winrt::event_token ReceivedToken;
|
||||
winrt::event_token StoppedToken;
|
||||
};
|
||||
|
||||
// ─── WinRT object wrappers ────────────────────────────────────────────────────
|
||||
// WinRT types delete operator new — we wrap them in plain structs so they can
|
||||
// live on the heap via regular new/delete (stored as void* in UObject headers).
|
||||
|
||||
struct FPS_BLEDeviceHandle
|
||||
{
|
||||
winrt::Windows::Devices::Bluetooth::BluetoothLEDevice Device{ nullptr };
|
||||
winrt::event_token ConnectionStatusToken;
|
||||
};
|
||||
|
||||
struct FPS_GattServiceHandle
|
||||
{
|
||||
winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService Service{ nullptr };
|
||||
};
|
||||
|
||||
#endif // PLATFORM_WINDOWS
|
||||
|
||||
#define LOCTEXT_NAMESPACE "PS_Win_BLE"
|
||||
@ -112,12 +132,14 @@ UPS_BLE_Manager* UPS_BLE_Module::GetBLEManager(const UPS_BLE_Module* Mod)
|
||||
void UPS_BLE_Module::ScannerCleanup()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
PS_BLE_WINRT_NS
|
||||
if (ScannerHandle)
|
||||
{
|
||||
FPS_ScannerState* State = static_cast<FPS_ScannerState*>(ScannerHandle);
|
||||
try
|
||||
{
|
||||
if (State->Watcher && State->Watcher.Status() == BluetoothLEAdvertisementWatcherStatus::Started)
|
||||
if (State->Watcher &&
|
||||
State->Watcher.Status() == WinAdv::BluetoothLEAdvertisementWatcherStatus::Started)
|
||||
{
|
||||
State->Watcher.Stop();
|
||||
}
|
||||
@ -132,24 +154,24 @@ void UPS_BLE_Module::ScannerCleanup()
|
||||
bool UPS_BLE_Module::StartDiscoveryLive(UPS_BLE_Manager* Ref, int32 DurationMs, const FString& Filter)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
PS_BLE_WINRT_NS
|
||||
if (!Ref) return false;
|
||||
ScannerCleanup();
|
||||
|
||||
FPS_ScannerState* State = new FPS_ScannerState();
|
||||
State->Watcher = BluetoothLEAdvertisementWatcher();
|
||||
State->Watcher.ScanningMode(BluetoothLEScanningMode::Active);
|
||||
State->Watcher = WinAdv::BluetoothLEAdvertisementWatcher();
|
||||
State->Watcher.ScanningMode(WinAdv::BluetoothLEScanningMode::Active);
|
||||
ScannerHandle = State;
|
||||
|
||||
// Capture filter and manager ref
|
||||
FString FilterCopy = Filter;
|
||||
UPS_BLE_Manager* MgrRef = Ref;
|
||||
|
||||
State->ReceivedToken = State->Watcher.Received(
|
||||
[MgrRef, FilterCopy](BluetoothLEAdvertisementWatcher const&, BluetoothLEAdvertisementReceivedEventArgs const& Args)
|
||||
[MgrRef, FilterCopy](WinAdv::BluetoothLEAdvertisementWatcher const&,
|
||||
WinAdv::BluetoothLEAdvertisementReceivedEventArgs const& Args)
|
||||
{
|
||||
FString Name = Args.Advertisement().LocalName().c_str();
|
||||
|
||||
// Apply name filter (comma-separated substrings)
|
||||
if (!FilterCopy.IsEmpty())
|
||||
{
|
||||
TArray<FString> Parts;
|
||||
@ -157,11 +179,7 @@ bool UPS_BLE_Module::StartDiscoveryLive(UPS_BLE_Manager* Ref, int32 DurationMs,
|
||||
bool bMatch = false;
|
||||
for (const FString& Part : Parts)
|
||||
{
|
||||
if (Name.Contains(Part.TrimStartAndEnd()))
|
||||
{
|
||||
bMatch = true;
|
||||
break;
|
||||
}
|
||||
if (Name.Contains(Part.TrimStartAndEnd())) { bMatch = true; break; }
|
||||
}
|
||||
if (!bMatch) return;
|
||||
}
|
||||
@ -170,12 +188,12 @@ bool UPS_BLE_Module::StartDiscoveryLive(UPS_BLE_Manager* Ref, int32 DurationMs,
|
||||
Rec.ID = Args.BluetoothAddress();
|
||||
Rec.RSSI = Args.RawSignalStrengthInDBm();
|
||||
Rec.Name = Name;
|
||||
|
||||
DispatchDeviceDiscovered(MgrRef, Rec);
|
||||
});
|
||||
|
||||
State->StoppedToken = State->Watcher.Stopped(
|
||||
[MgrRef](BluetoothLEAdvertisementWatcher const&, BluetoothLEAdvertisementWatcherStoppedEventArgs const&)
|
||||
[MgrRef](WinAdv::BluetoothLEAdvertisementWatcher const&,
|
||||
WinAdv::BluetoothLEAdvertisementWatcherStoppedEventArgs const&)
|
||||
{
|
||||
TArray<FPS_DeviceRecord> Empty;
|
||||
DispatchDiscoveryEnd(MgrRef, Empty);
|
||||
@ -183,7 +201,6 @@ bool UPS_BLE_Module::StartDiscoveryLive(UPS_BLE_Manager* Ref, int32 DurationMs,
|
||||
|
||||
State->Watcher.Start();
|
||||
|
||||
// Auto-stop after DurationMs
|
||||
if (DurationMs > 0)
|
||||
{
|
||||
int32 Ms = DurationMs;
|
||||
@ -203,28 +220,28 @@ bool UPS_BLE_Module::StartDiscoveryLive(UPS_BLE_Manager* Ref, int32 DurationMs,
|
||||
bool UPS_BLE_Module::StartDiscoveryInBackground(UPS_BLE_Manager* Ref, int32 DurationMs, const FString& Filter)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
PS_BLE_WINRT_NS
|
||||
if (!Ref) return false;
|
||||
ScannerCleanup();
|
||||
|
||||
// Collect all devices during scan, fire DiscoveryEnd at the end
|
||||
TSharedPtr<TArray<FPS_DeviceRecord>, ESPMode::ThreadSafe> Collected =
|
||||
MakeShared<TArray<FPS_DeviceRecord>, ESPMode::ThreadSafe>();
|
||||
|
||||
FPS_ScannerState* State = new FPS_ScannerState();
|
||||
State->Watcher = BluetoothLEAdvertisementWatcher();
|
||||
State->Watcher.ScanningMode(BluetoothLEScanningMode::Active);
|
||||
State->Watcher = WinAdv::BluetoothLEAdvertisementWatcher();
|
||||
State->Watcher.ScanningMode(WinAdv::BluetoothLEScanningMode::Active);
|
||||
ScannerHandle = State;
|
||||
|
||||
FString FilterCopy = Filter;
|
||||
UPS_BLE_Manager* MgrRef = Ref;
|
||||
|
||||
State->ReceivedToken = State->Watcher.Received(
|
||||
[MgrRef, FilterCopy, Collected](BluetoothLEAdvertisementWatcher const&, BluetoothLEAdvertisementReceivedEventArgs const& Args)
|
||||
[MgrRef, FilterCopy, Collected](WinAdv::BluetoothLEAdvertisementWatcher const&,
|
||||
WinAdv::BluetoothLEAdvertisementReceivedEventArgs const& Args)
|
||||
{
|
||||
FString Name = Args.Advertisement().LocalName().c_str();
|
||||
|
||||
// Dedup by address
|
||||
uint64 Addr = Args.BluetoothAddress();
|
||||
|
||||
for (const FPS_DeviceRecord& R : *Collected)
|
||||
{
|
||||
if (R.ID == Addr) return;
|
||||
@ -250,7 +267,8 @@ bool UPS_BLE_Module::StartDiscoveryInBackground(UPS_BLE_Manager* Ref, int32 Dura
|
||||
});
|
||||
|
||||
State->StoppedToken = State->Watcher.Stopped(
|
||||
[MgrRef, Collected](BluetoothLEAdvertisementWatcher const&, BluetoothLEAdvertisementWatcherStoppedEventArgs const&)
|
||||
[MgrRef, Collected](WinAdv::BluetoothLEAdvertisementWatcher const&,
|
||||
WinAdv::BluetoothLEAdvertisementWatcherStoppedEventArgs const&)
|
||||
{
|
||||
DispatchDiscoveryEnd(MgrRef, *Collected);
|
||||
});
|
||||
@ -276,12 +294,14 @@ bool UPS_BLE_Module::StartDiscoveryInBackground(UPS_BLE_Manager* Ref, int32 Dura
|
||||
bool UPS_BLE_Module::StopDiscovery()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
PS_BLE_WINRT_NS
|
||||
if (ScannerHandle)
|
||||
{
|
||||
FPS_ScannerState* State = static_cast<FPS_ScannerState*>(ScannerHandle);
|
||||
try
|
||||
{
|
||||
if (State->Watcher && State->Watcher.Status() == BluetoothLEAdvertisementWatcherStatus::Started)
|
||||
if (State->Watcher &&
|
||||
State->Watcher.Status() == WinAdv::BluetoothLEAdvertisementWatcherStatus::Started)
|
||||
{
|
||||
State->Watcher.Stop();
|
||||
}
|
||||
@ -303,39 +323,33 @@ bool UPS_BLE_Module::ConnectDevice(UPS_BLE_Device* Device)
|
||||
if (!Device) return false;
|
||||
|
||||
uint64 Addr = Device->DeviceID;
|
||||
UPS_BLE_Module* ModRef = this;
|
||||
|
||||
// WinRT async connect + service discovery on background thread
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [ModRef, Device, Addr]()
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [Device, Addr]()
|
||||
{
|
||||
PS_BLE_WINRT_NS
|
||||
try
|
||||
{
|
||||
// FromBluetoothAddressAsync is the standard WinRT connect path
|
||||
auto Op = BluetoothLEDevice::FromBluetoothAddressAsync(Addr);
|
||||
BluetoothLEDevice BLEDev = Op.get();
|
||||
|
||||
auto BLEDev = WinBT::BluetoothLEDevice::FromBluetoothAddressAsync(Addr).get();
|
||||
if (!BLEDev)
|
||||
{
|
||||
DispatchDeviceDisconnected(Device);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store native handle (AddRef via IUnknown kept alive by winrt wrapper)
|
||||
Device->NativeDeviceHandle = new winrt::Windows::Devices::Bluetooth::BluetoothLEDevice(BLEDev);
|
||||
|
||||
// Subscribe to connection-status change
|
||||
BLEDev.ConnectionStatusChanged(
|
||||
[Device](BluetoothLEDevice const& Dev, IInspectable const&)
|
||||
FPS_BLEDeviceHandle* Handle = new FPS_BLEDeviceHandle();
|
||||
Handle->Device = BLEDev;
|
||||
Handle->ConnectionStatusToken = BLEDev.ConnectionStatusChanged(
|
||||
[Device](WinBT::BluetoothLEDevice const& Dev, winrt::Windows::Foundation::IInspectable const&)
|
||||
{
|
||||
if (Dev.ConnectionStatus() == BluetoothConnectionStatus::Disconnected)
|
||||
if (Dev.ConnectionStatus() == WinBT::BluetoothConnectionStatus::Disconnected)
|
||||
{
|
||||
DispatchDeviceDisconnected(Device);
|
||||
}
|
||||
});
|
||||
Device->NativeDeviceHandle = Handle;
|
||||
|
||||
// Discover GATT services
|
||||
auto SvcResult = BLEDev.GetGattServicesAsync(BluetoothCacheMode::Uncached).get();
|
||||
if (SvcResult.Status() != GattCommunicationStatus::Success)
|
||||
auto SvcResult = BLEDev.GetGattServicesAsync(WinBT::BluetoothCacheMode::Uncached).get();
|
||||
if (SvcResult.Status() != WinGAP::GattCommunicationStatus::Success)
|
||||
{
|
||||
DispatchDeviceDisconnected(Device);
|
||||
return;
|
||||
@ -348,16 +362,17 @@ bool UPS_BLE_Module::ConnectDevice(UPS_BLE_Device* Device)
|
||||
for (uint32_t si = 0; si < Services.Size(); si++)
|
||||
{
|
||||
auto Svc = Services.GetAt(si);
|
||||
Device->NativeGattServices.Add(new GattDeviceService(Svc));
|
||||
FPS_GattServiceHandle* SvcHandle = new FPS_GattServiceHandle();
|
||||
SvcHandle->Service = Svc;
|
||||
Device->NativeGattServices.Add(SvcHandle);
|
||||
|
||||
FPS_ServiceItem SvcItem;
|
||||
GUID g = Svc.Uuid();
|
||||
SvcItem.ServiceUUID = UPS_BLE_Device::GUIDToString(&g);
|
||||
SvcItem.ServiceName = SvcItem.ServiceUUID; // WinRT doesn't give a friendly name
|
||||
SvcItem.ServiceName = SvcItem.ServiceUUID;
|
||||
|
||||
// Discover characteristics
|
||||
auto CharResult = Svc.GetCharacteristicsAsync(BluetoothCacheMode::Uncached).get();
|
||||
if (CharResult.Status() == GattCommunicationStatus::Success)
|
||||
auto CharResult = SvcHandle->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Uncached).get();
|
||||
if (CharResult.Status() == WinGAP::GattCommunicationStatus::Success)
|
||||
{
|
||||
auto Chars = CharResult.Characteristics();
|
||||
for (uint32_t ci = 0; ci < Chars.Size(); ci++)
|
||||
@ -368,17 +383,17 @@ bool UPS_BLE_Module::ConnectDevice(UPS_BLE_Device* Device)
|
||||
ChItem.CharacteristicUUID = UPS_BLE_Device::GUIDToString(&cg);
|
||||
ChItem.CharacteristicName = ChItem.CharacteristicUUID;
|
||||
|
||||
// Map WinRT properties to our descriptor bits
|
||||
auto Props = Ch.CharacteristicProperties();
|
||||
uint8 Desc = 0;
|
||||
if ((Props & GattCharacteristicProperties::Broadcast) != GattCharacteristicProperties::None) Desc |= 0x01;
|
||||
if ((Props & GattCharacteristicProperties::ExtendedProperties) != GattCharacteristicProperties::None) Desc |= 0x02;
|
||||
if ((Props & GattCharacteristicProperties::Notify) != GattCharacteristicProperties::None) Desc |= 0x04;
|
||||
if ((Props & GattCharacteristicProperties::Indicate) != GattCharacteristicProperties::None) Desc |= 0x08;
|
||||
if ((Props & GattCharacteristicProperties::Read) != GattCharacteristicProperties::None) Desc |= 0x10;
|
||||
if ((Props & GattCharacteristicProperties::Write) != GattCharacteristicProperties::None) Desc |= 0x20;
|
||||
if ((Props & GattCharacteristicProperties::WriteWithoutResponse)!= GattCharacteristicProperties::None) Desc |= 0x40;
|
||||
if ((Props & GattCharacteristicProperties::AuthenticatedSignedWrites)!= GattCharacteristicProperties::None) Desc |= 0x80;
|
||||
using GP = WinGAP::GattCharacteristicProperties;
|
||||
if ((Props & GP::Broadcast) != GP::None) Desc |= 0x01;
|
||||
if ((Props & GP::ExtendedProperties) != GP::None) Desc |= 0x02;
|
||||
if ((Props & GP::Notify) != GP::None) Desc |= 0x04;
|
||||
if ((Props & GP::Indicate) != GP::None) Desc |= 0x08;
|
||||
if ((Props & GP::Read) != GP::None) Desc |= 0x10;
|
||||
if ((Props & GP::Write) != GP::None) Desc |= 0x20;
|
||||
if ((Props & GP::WriteWithoutResponse) != GP::None) Desc |= 0x40;
|
||||
if ((Props & GP::AuthenticatedSignedWrites) != GP::None) Desc |= 0x80;
|
||||
ChItem.Descriptor = Desc;
|
||||
|
||||
SvcItem.Characteristics.Add(ChItem);
|
||||
@ -405,22 +420,18 @@ bool UPS_BLE_Module::ConnectDevice(UPS_BLE_Device* Device)
|
||||
bool UPS_BLE_Module::DisconnectDevice(UPS_BLE_Device* Device)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
PS_BLE_WINRT_NS
|
||||
if (!Device) return false;
|
||||
|
||||
// Close native GATT service handles
|
||||
for (void* SvcPtr : Device->NativeGattServices)
|
||||
{
|
||||
if (SvcPtr)
|
||||
{
|
||||
delete static_cast<GattDeviceService*>(SvcPtr);
|
||||
}
|
||||
if (SvcPtr) delete static_cast<FPS_GattServiceHandle*>(SvcPtr);
|
||||
}
|
||||
Device->NativeGattServices.Empty();
|
||||
|
||||
// Close device handle
|
||||
if (Device->NativeDeviceHandle)
|
||||
{
|
||||
delete static_cast<BluetoothLEDevice*>(Device->NativeDeviceHandle);
|
||||
delete static_cast<FPS_BLEDeviceHandle*>(Device->NativeDeviceHandle);
|
||||
Device->NativeDeviceHandle = nullptr;
|
||||
}
|
||||
return true;
|
||||
@ -432,11 +443,12 @@ bool UPS_BLE_Module::DisconnectDevice(UPS_BLE_Device* Device)
|
||||
bool UPS_BLE_Module::IsDeviceConnected(UPS_BLE_Device* Device)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
PS_BLE_WINRT_NS
|
||||
if (!Device || !Device->NativeDeviceHandle) return false;
|
||||
try
|
||||
{
|
||||
BluetoothLEDevice* BLEDev = static_cast<BluetoothLEDevice*>(Device->NativeDeviceHandle);
|
||||
return BLEDev->ConnectionStatus() == BluetoothConnectionStatus::Connected;
|
||||
auto* Handle = static_cast<FPS_BLEDeviceHandle*>(Device->NativeDeviceHandle);
|
||||
return Handle->Device.ConnectionStatus() == WinBT::BluetoothConnectionStatus::Connected;
|
||||
}
|
||||
catch (...) {}
|
||||
#endif
|
||||
@ -450,26 +462,27 @@ bool UPS_BLE_Module::IsDeviceConnected(UPS_BLE_Device* Device)
|
||||
bool UPS_BLE_Module::ReadCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8 CI)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Device || SI >= Device->NativeGattServices.Num()) return false;
|
||||
if (!Device || SI >= (uint8)Device->NativeGattServices.Num()) return false;
|
||||
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [Device, SI, CI]()
|
||||
{
|
||||
PS_BLE_WINRT_NS
|
||||
try
|
||||
{
|
||||
GattDeviceService* Svc = static_cast<GattDeviceService*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = Svc->GetCharacteristicsAsync(BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != GattCommunicationStatus::Success) return;
|
||||
auto* SvcH = static_cast<FPS_GattServiceHandle*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return;
|
||||
|
||||
auto Ch = Chars.Characteristics().GetAt(CI);
|
||||
auto Result = Ch.ReadValueAsync(BluetoothCacheMode::Uncached).get();
|
||||
auto Result = Ch.ReadValueAsync(WinBT::BluetoothCacheMode::Uncached).get();
|
||||
|
||||
EPS_GATTStatus Status = (Result.Status() == GattCommunicationStatus::Success)
|
||||
EPS_GATTStatus Status = (Result.Status() == WinGAP::GattCommunicationStatus::Success)
|
||||
? EPS_GATTStatus::Success : EPS_GATTStatus::Failure;
|
||||
|
||||
TArray<uint8> Data;
|
||||
if (Result.Status() == GattCommunicationStatus::Success)
|
||||
if (Result.Status() == WinGAP::GattCommunicationStatus::Success)
|
||||
{
|
||||
auto Reader = DataReader::FromBuffer(Result.Value());
|
||||
auto Reader = WinStr::DataReader::FromBuffer(Result.Value());
|
||||
Data.SetNumUninitialized(Reader.UnconsumedBufferLength());
|
||||
for (uint8& B : Data) B = Reader.ReadByte();
|
||||
}
|
||||
@ -487,26 +500,26 @@ bool UPS_BLE_Module::ReadCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8
|
||||
bool UPS_BLE_Module::WriteCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8 CI, const TArray<uint8>& Data)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Device || SI >= Device->NativeGattServices.Num()) return false;
|
||||
if (!Device || SI >= (uint8)Device->NativeGattServices.Num()) return false;
|
||||
|
||||
TArray<uint8> DataCopy = Data;
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [Device, SI, CI, DataCopy]()
|
||||
{
|
||||
PS_BLE_WINRT_NS
|
||||
try
|
||||
{
|
||||
GattDeviceService* Svc = static_cast<GattDeviceService*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = Svc->GetCharacteristicsAsync(BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != GattCommunicationStatus::Success) return;
|
||||
auto* SvcH = static_cast<FPS_GattServiceHandle*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return;
|
||||
|
||||
auto Ch = Chars.Characteristics().GetAt(CI);
|
||||
|
||||
auto Writer = DataWriter();
|
||||
auto Writer = WinStr::DataWriter();
|
||||
for (uint8 B : DataCopy) Writer.WriteByte(B);
|
||||
|
||||
auto Status = Ch.WriteValueAsync(Writer.DetachBuffer(),
|
||||
GattWriteOption::WriteWithResponse).get();
|
||||
auto WriteStatus = Ch.WriteValueAsync(Writer.DetachBuffer(),
|
||||
WinGAP::GattWriteOption::WriteWithResponse).get();
|
||||
|
||||
EPS_GATTStatus GattStatus = (Status == GattCommunicationStatus::Success)
|
||||
EPS_GATTStatus GattStatus = (WriteStatus == WinGAP::GattCommunicationStatus::Success)
|
||||
? EPS_GATTStatus::Success : EPS_GATTStatus::Failure;
|
||||
|
||||
DispatchWrite(Device, SI, CI, GattStatus);
|
||||
@ -522,39 +535,39 @@ bool UPS_BLE_Module::WriteCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8
|
||||
bool UPS_BLE_Module::SubscribeCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8 CI)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Device || SI >= Device->NativeGattServices.Num()) return false;
|
||||
if (!Device || SI >= (uint8)Device->NativeGattServices.Num()) return false;
|
||||
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [Device, SI, CI]()
|
||||
{
|
||||
PS_BLE_WINRT_NS
|
||||
try
|
||||
{
|
||||
GattDeviceService* Svc = static_cast<GattDeviceService*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = Svc->GetCharacteristicsAsync(BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != GattCommunicationStatus::Success) return;
|
||||
auto* SvcH = static_cast<FPS_GattServiceHandle*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return;
|
||||
|
||||
auto Ch = Chars.Characteristics().GetAt(CI);
|
||||
|
||||
// Write CCCD to enable notifications
|
||||
auto WriteStatus = Ch.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
GattClientCharacteristicConfigurationDescriptorValue::Notify).get();
|
||||
WinGAP::GattClientCharacteristicConfigurationDescriptorValue::Notify).get();
|
||||
|
||||
EPS_GATTStatus GattStatus = (WriteStatus == GattCommunicationStatus::Success)
|
||||
EPS_GATTStatus GattStatus = (WriteStatus == WinGAP::GattCommunicationStatus::Success)
|
||||
? EPS_GATTStatus::Success : EPS_GATTStatus::Failure;
|
||||
|
||||
if (WriteStatus == GattCommunicationStatus::Success)
|
||||
if (WriteStatus == WinGAP::GattCommunicationStatus::Success)
|
||||
{
|
||||
// Register value-changed callback
|
||||
auto Token = Ch.ValueChanged(
|
||||
[Device, SI, CI](GattCharacteristic const&, GattValueChangedEventArgs const& Args)
|
||||
[Device, SI, CI](WinGAP::GattCharacteristic const&,
|
||||
WinGAP::GattValueChangedEventArgs const& Args)
|
||||
{
|
||||
auto Reader = DataReader::FromBuffer(Args.CharacteristicValue());
|
||||
PS_BLE_WINRT_NS
|
||||
auto Reader = WinStr::DataReader::FromBuffer(Args.CharacteristicValue());
|
||||
TArray<uint8> Data;
|
||||
Data.SetNumUninitialized(Reader.UnconsumedBufferLength());
|
||||
for (uint8& B : Data) B = Reader.ReadByte();
|
||||
DispatchNotify(Device, SI, CI, EPS_GATTStatus::Success, MoveTemp(Data));
|
||||
});
|
||||
|
||||
// Store token (key = packed SI<<8|CI)
|
||||
uint64 Key = ((uint64)SI << 8) | CI;
|
||||
Device->NotifyTokens.Add(Key, new winrt::event_token(Token));
|
||||
}
|
||||
@ -572,19 +585,19 @@ bool UPS_BLE_Module::SubscribeCharacteristic(UPS_BLE_Device* Device, uint8 SI, u
|
||||
bool UPS_BLE_Module::UnsubscribeCharacteristic(UPS_BLE_Device* Device, uint8 SI, uint8 CI)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Device || SI >= Device->NativeGattServices.Num()) return false;
|
||||
if (!Device || SI >= (uint8)Device->NativeGattServices.Num()) return false;
|
||||
|
||||
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [Device, SI, CI]()
|
||||
{
|
||||
PS_BLE_WINRT_NS
|
||||
try
|
||||
{
|
||||
GattDeviceService* Svc = static_cast<GattDeviceService*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = Svc->GetCharacteristicsAsync(BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != GattCommunicationStatus::Success) return;
|
||||
auto* SvcH = static_cast<FPS_GattServiceHandle*>(Device->NativeGattServices[SI]);
|
||||
auto Chars = SvcH->Service.GetCharacteristicsAsync(WinBT::BluetoothCacheMode::Cached).get();
|
||||
if (Chars.Status() != WinGAP::GattCommunicationStatus::Success) return;
|
||||
|
||||
auto Ch = Chars.Characteristics().GetAt(CI);
|
||||
|
||||
// Remove event token
|
||||
uint64 Key = ((uint64)SI << 8) | CI;
|
||||
if (winrt::event_token** TokenPtr = reinterpret_cast<winrt::event_token**>(Device->NotifyTokens.Find(Key)))
|
||||
{
|
||||
@ -593,11 +606,10 @@ bool UPS_BLE_Module::UnsubscribeCharacteristic(UPS_BLE_Device* Device, uint8 SI,
|
||||
Device->NotifyTokens.Remove(Key);
|
||||
}
|
||||
|
||||
// Write CCCD to disable notifications
|
||||
auto WriteStatus = Ch.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
GattClientCharacteristicConfigurationDescriptorValue::None).get();
|
||||
WinGAP::GattClientCharacteristicConfigurationDescriptorValue::None).get();
|
||||
|
||||
EPS_GATTStatus GattStatus = (WriteStatus == GattCommunicationStatus::Success)
|
||||
EPS_GATTStatus GattStatus = (WriteStatus == WinGAP::GattCommunicationStatus::Success)
|
||||
? EPS_GATTStatus::Success : EPS_GATTStatus::Failure;
|
||||
|
||||
DispatchUnsubscribe(Device, SI, CI, GattStatus);
|
||||
@ -633,22 +645,55 @@ void UPS_BLE_Module::DispatchDiscoveryEnd(UPS_BLE_Manager* Mgr, const TArray<FPS
|
||||
void UPS_BLE_Module::DispatchDeviceConnected(UPS_BLE_Device* Dev)
|
||||
{
|
||||
if (!Dev) return;
|
||||
if (IsInGameThread()) { Dev->RefToManager->JustConnectedDevice(Dev); Dev->OnConnect.Broadcast(Dev, Dev->ActiveServices); }
|
||||
else { AsyncTask(ENamedThreads::GameThread, [Dev]() { Dev->RefToManager->JustConnectedDevice(Dev); Dev->OnConnect.Broadcast(Dev, Dev->ActiveServices); }); }
|
||||
if (IsInGameThread())
|
||||
{
|
||||
Dev->RefToManager->JustConnectedDevice(Dev);
|
||||
Dev->OnConnect.Broadcast(Dev, Dev->ActiveServices);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [Dev]()
|
||||
{
|
||||
Dev->RefToManager->JustConnectedDevice(Dev);
|
||||
Dev->OnConnect.Broadcast(Dev, Dev->ActiveServices);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_BLE_Module::DispatchDeviceDisconnected(UPS_BLE_Device* Dev)
|
||||
{
|
||||
if (!Dev) return;
|
||||
if (IsInGameThread()) { Dev->RefToManager->JustDisconnectedDevice(Dev); Dev->OnDisconnect.Broadcast(Dev); }
|
||||
else { AsyncTask(ENamedThreads::GameThread, [Dev]() { Dev->RefToManager->JustDisconnectedDevice(Dev); Dev->OnDisconnect.Broadcast(Dev); }); }
|
||||
if (IsInGameThread())
|
||||
{
|
||||
Dev->RefToManager->JustDisconnectedDevice(Dev);
|
||||
Dev->OnDisconnect.Broadcast(Dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [Dev]()
|
||||
{
|
||||
Dev->RefToManager->JustDisconnectedDevice(Dev);
|
||||
Dev->OnDisconnect.Broadcast(Dev);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_BLE_Module::DispatchServicesDiscovered(UPS_BLE_Device* Dev)
|
||||
{
|
||||
if (!Dev) return;
|
||||
if (IsInGameThread()) { Dev->RefToManager->JustDiscoveredServices(Dev); Dev->OnServicesDiscovered.Broadcast(Dev, Dev->ActiveServices); }
|
||||
else { AsyncTask(ENamedThreads::GameThread, [Dev]() { Dev->RefToManager->JustDiscoveredServices(Dev); Dev->OnServicesDiscovered.Broadcast(Dev, Dev->ActiveServices); }); }
|
||||
if (IsInGameThread())
|
||||
{
|
||||
Dev->RefToManager->JustDiscoveredServices(Dev);
|
||||
Dev->OnServicesDiscovered.Broadcast(Dev, Dev->ActiveServices);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [Dev]()
|
||||
{
|
||||
Dev->RefToManager->JustDiscoveredServices(Dev);
|
||||
Dev->OnServicesDiscovered.Broadcast(Dev, Dev->ActiveServices);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_BLE_Module::DispatchRead(UPS_BLE_Device* Dev, uint8 SI, uint8 CI, EPS_GATTStatus Status, TArray<uint8> Data)
|
||||
@ -657,14 +702,16 @@ void UPS_BLE_Module::DispatchRead(UPS_BLE_Device* Dev, uint8 SI, uint8 CI, EPS_G
|
||||
if (IsInGameThread())
|
||||
{
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
Dev->OnRead.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
Dev->OnRead.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [Dev, SI, CI, Status, Data]()
|
||||
{
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
Dev->OnRead.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
Dev->OnRead.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -675,14 +722,16 @@ void UPS_BLE_Module::DispatchNotify(UPS_BLE_Device* Dev, uint8 SI, uint8 CI, EPS
|
||||
if (IsInGameThread())
|
||||
{
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
Dev->OnNotify.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
Dev->OnNotify.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [Dev, SI, CI, Status, Data]()
|
||||
{
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
Dev->OnNotify.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
Dev->OnNotify.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID, Data);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -693,7 +742,8 @@ void UPS_BLE_Module::DispatchWrite(UPS_BLE_Device* Dev, uint8 SI, uint8 CI, EPS_
|
||||
auto Fire = [Dev, SI, CI, Status]()
|
||||
{
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
Dev->OnWrite.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID);
|
||||
Dev->OnWrite.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID);
|
||||
};
|
||||
if (IsInGameThread()) Fire();
|
||||
else AsyncTask(ENamedThreads::GameThread, Fire);
|
||||
@ -707,7 +757,8 @@ void UPS_BLE_Module::DispatchSubscribe(UPS_BLE_Device* Dev, uint8 SI, uint8 CI,
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
{
|
||||
Dev->ActiveServices[SI].Characteristics[CI].subscribed = true;
|
||||
Dev->OnSubscribe.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID);
|
||||
Dev->OnSubscribe.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID);
|
||||
}
|
||||
};
|
||||
if (IsInGameThread()) Fire();
|
||||
@ -722,7 +773,8 @@ void UPS_BLE_Module::DispatchUnsubscribe(UPS_BLE_Device* Dev, uint8 SI, uint8 CI
|
||||
if (SI < Dev->ActiveServices.Num() && CI < Dev->ActiveServices[SI].Characteristics.Num())
|
||||
{
|
||||
Dev->ActiveServices[SI].Characteristics[CI].subscribed = false;
|
||||
Dev->OnUnsubscribe.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID, Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID);
|
||||
Dev->OnUnsubscribe.Broadcast(Status, Dev, Dev->ActiveServices[SI].ServiceUUID,
|
||||
Dev->ActiveServices[SI].Characteristics[CI].CharacteristicUUID);
|
||||
}
|
||||
};
|
||||
if (IsInGameThread()) Fire();
|
||||
|
||||
@ -9,15 +9,6 @@
|
||||
class UPS_BLE_Device;
|
||||
class UPS_BLE_Manager;
|
||||
|
||||
// ─── Internal device record (equivalent to TUE_dev_rec from old plugin) ──────
|
||||
struct FPS_DeviceRecord
|
||||
{
|
||||
uint64 ID = 0;
|
||||
int64 RSSI = 0;
|
||||
FString Name;
|
||||
void* NativeDeviceRef = nullptr; // WinRT IBluetoothLEDevice* (opaque ptr for forward compat)
|
||||
};
|
||||
|
||||
// ─── Module ───────────────────────────────────────────────────────────────────
|
||||
|
||||
class UPS_BLE_Module : public IModuleInterface
|
||||
@ -48,9 +39,9 @@ public:
|
||||
bool UnsubscribeCharacteristic(UPS_BLE_Device* Device, uint8 ServiceIndex, uint8 CharIndex);
|
||||
|
||||
UPS_BLE_Manager* LocalBLEManager = nullptr;
|
||||
bool bInitialized = false;
|
||||
|
||||
private:
|
||||
bool bInitialized = false;
|
||||
|
||||
// WinRT scanner (opaque handle — implementation in .cpp using WinRT types)
|
||||
void* ScannerHandle = nullptr;
|
||||
|
||||
@ -97,6 +97,18 @@ public:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "PS BLE") TArray<FPS_CharacteristicItem> Characteristics;
|
||||
};
|
||||
|
||||
// ─── INTERNAL DEVICE RECORD (forward-declared here so Manager can use it) ────
|
||||
// This is a plain C++ struct (no UCLASS/USTRUCT) used internally between
|
||||
// the module and the manager — not exposed to Blueprint.
|
||||
|
||||
struct FPS_DeviceRecord
|
||||
{
|
||||
uint64 ID = 0;
|
||||
int64 RSSI = 0;
|
||||
FString Name;
|
||||
void* NativeDeviceRef = nullptr;
|
||||
};
|
||||
|
||||
// ─── FORWARD DECLARATIONS ────────────────────────────────────────────────────
|
||||
|
||||
class UPS_BLE_Device;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user