#include "DRI/Dri_Nus.h" #include "COM/Com_Protocol.h" #include #include #include #include #include #include #include #include #include #include #include struct Dri_Nus_Struct_Candidate { QBluetoothDeviceInfo DeviceInfo; QString EndpointId; QString DeviceLabel; QString DeviceName; QString AddressText; }; struct Dri_Nus_Struct_Context { QBluetoothDeviceDiscoveryAgent* p_DiscoveryAgent = nullptr; QLowEnergyController* p_Controller = nullptr; QLowEnergyService* p_Service = nullptr; QVector CandidateList; int CurrentCandidateIndex = -1; int LockedCandidateIndex = -1; bool IsDiscoveryFinished = false; bool HasTargetService = false; QLowEnergyCharacteristic WriteCharacteristic; QLowEnergyCharacteristic NotifyCharacteristic; QLowEnergyDescriptor NotifyDescriptor; QQueue PacketQueue; }; namespace { const QString kPreferredBleDeviceName = QStringLiteral("WH Mini Keyboard"); const QBluetoothUuid kServiceUuid( QUuid(QStringLiteral("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"))); const QBluetoothUuid kRxCharacteristicUuid( QUuid(QStringLiteral("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"))); const QBluetoothUuid kTxCharacteristicUuid( QUuid(QStringLiteral("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"))); QString Dri_Nus_NormalizeAddressText(const QBluetoothAddress& Address) { const QString AddressText = Address.toString().trimmed().toUpper(); return (AddressText == QStringLiteral("00:00:00:00:00:00")) ? QString() : AddressText; } bool Dri_Nus_IsPreferredDeviceName(const QString& DeviceName) { return DeviceName.trimmed().compare(kPreferredBleDeviceName, Qt::CaseInsensitive) == 0; } QString Dri_Nus_FormatCandidateSummary(const Dri_Nus_Struct_Candidate& Candidate) { QString Summary = Candidate.DeviceLabel; if (!Candidate.AddressText.isEmpty()) { Summary += QStringLiteral(" [%1]").arg(Candidate.AddressText); } return Summary; } QString Dri_Nus_GetPacketTypeText(Com_Enum_ProtocolType Type) { switch (Type) { case Com_Enum_ProtocolType_HelloReq: return QStringLiteral("HelloReq"); case Com_Enum_ProtocolType_HelloRsp: return QStringLiteral("HelloRsp"); case Com_Enum_ProtocolType_Bitmap: return QStringLiteral("Bitmap"); case Com_Enum_ProtocolType_FunctionKeyEvent: return QStringLiteral("FunctionKeyEvent"); case Com_Enum_ProtocolType_LedState: return QStringLiteral("LedState"); case Com_Enum_ProtocolType_TimeSync: return QStringLiteral("TimeSync"); case Com_Enum_ProtocolType_ThemeRgb: return QStringLiteral("ThemeRgb"); case Com_Enum_ProtocolType_Ack: return QStringLiteral("Ack"); case Com_Enum_ProtocolType_Error: return QStringLiteral("Error"); default: return QStringLiteral("Unknown"); } } const Dri_Nus_Struct_Candidate* Dri_Nus_GetCurrentCandidate( const Dri_Nus_Struct_Context* p_Context) { if ((p_Context == nullptr) || (p_Context->CurrentCandidateIndex < 0) || (p_Context->CurrentCandidateIndex >= p_Context->CandidateList.size())) { return nullptr; } return &p_Context->CandidateList.at(p_Context->CurrentCandidateIndex); } QString Dri_Nus_BuildEndpointId( const QBluetoothDeviceInfo& DeviceInfo, int CandidateOrdinal) { QString DeviceUuidText = DeviceInfo.deviceUuid().toString().trimmed(); DeviceUuidText.remove(QLatin1Char('{')); DeviceUuidText.remove(QLatin1Char('}')); if (!DeviceUuidText.isEmpty()) { return QStringLiteral("uuid:%1").arg(DeviceUuidText.toUpper()); } const QString AddressText = DeviceInfo.address().toString().trimmed().toUpper(); if (!AddressText.isEmpty() && (AddressText != QStringLiteral("00:00:00:00:00:00"))) { return QStringLiteral("addr:%1").arg(AddressText); } const QString DeviceName = DeviceInfo.name().trimmed(); if (!DeviceName.isEmpty()) { return QStringLiteral("name:%1#%2").arg(DeviceName).arg(CandidateOrdinal); } return QStringLiteral("candidate:%1").arg(CandidateOrdinal); } bool Dri_Nus_HasStableConnectIdentity(const QBluetoothDeviceInfo& DeviceInfo) { QString DeviceUuidText = DeviceInfo.deviceUuid().toString().trimmed(); DeviceUuidText.remove(QLatin1Char('{')); DeviceUuidText.remove(QLatin1Char('}')); if (!DeviceUuidText.isEmpty()) { return true; } const QString AddressText = DeviceInfo.address().toString().trimmed().toUpper(); return !AddressText.isEmpty() && (AddressText != QStringLiteral("00:00:00:00:00:00")); } bool Dri_Nus_IsCurrentCandidate( const Dri_Nus_Struct_Context* p_Context, const QString& EndpointId) { const Dri_Nus_Struct_Candidate* p_CurrentCandidate = Dri_Nus_GetCurrentCandidate(p_Context); return (p_CurrentCandidate != nullptr) && (p_CurrentCandidate->EndpointId.compare(EndpointId, Qt::CaseInsensitive) == 0); } void Dri_Nus_ResetServiceState(Dri_Nus_Struct_Port* p_Port) { Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; if (p_Context == nullptr) { return; } p_Context->p_Service = nullptr; p_Context->WriteCharacteristic = QLowEnergyCharacteristic(); p_Context->NotifyCharacteristic = QLowEnergyCharacteristic(); p_Context->NotifyDescriptor = QLowEnergyDescriptor(); p_Context->HasTargetService = false; p_Port->IsConnected = false; p_Port->HasWriteAck = false; } void Dri_Nus_ReleaseConnectionObjects(Dri_Nus_Struct_Context* p_Context) { if (p_Context == nullptr) { return; } p_Context->p_Service = nullptr; p_Context->WriteCharacteristic = QLowEnergyCharacteristic(); p_Context->NotifyCharacteristic = QLowEnergyCharacteristic(); p_Context->NotifyDescriptor = QLowEnergyDescriptor(); p_Context->HasTargetService = false; if (p_Context->p_Controller != nullptr) { p_Context->p_Controller->disconnect(); p_Context->p_Controller->deleteLater(); p_Context->p_Controller = nullptr; } } void Dri_Nus_QueuePacket( Dri_Nus_Struct_Context* p_Context, const QByteArray& PacketBody, const QString& PortName, const QString& EndpointId) { if ((p_Context == nullptr) || PacketBody.isEmpty()) { return; } Com_Struct_RawPacket Packet; Packet.IsValid = Com_Protocol_DecodeMessageType(PacketBody, &Packet.ProtocolType); Packet.Source = Com_Enum_RawPacketSource_BleNus; Packet.PortName = PortName; Packet.EndpointId = EndpointId; Packet.ByteArray = PacketBody; if (Packet.IsValid) { p_Context->PacketQueue.enqueue(Packet); } } void Dri_Nus_DeleteContext(Dri_Nus_Struct_Context* p_Context) { if (p_Context == nullptr) { return; } if (p_Context->p_DiscoveryAgent != nullptr) { p_Context->p_DiscoveryAgent->stop(); } if (p_Context->p_Controller != nullptr) { p_Context->p_Controller->disconnect(); p_Context->p_Controller->disconnectFromDevice(); delete p_Context->p_Controller; p_Context->p_Controller = nullptr; } delete p_Context->p_DiscoveryAgent; delete p_Context; } bool Dri_Nus_StartNextCandidate(Dri_Nus_Struct_Port* p_Port); void Dri_Nus_AdvanceFromCurrentCandidate( Dri_Nus_Struct_Port* p_Port, const QString& TextStatus) { Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; if (p_Context == nullptr) { return; } const bool IsLockedCandidate = (p_Context->LockedCandidateIndex >= 0) && (p_Context->LockedCandidateIndex == p_Context->CurrentCandidateIndex); Dri_Nus_ResetServiceState(p_Port); Dri_Nus_ReleaseConnectionObjects(p_Context); p_Port->TextEndpointSummary = TextStatus; if (IsLockedCandidate) { // 宸茬‘璁ょ洰鏍囪澶囨柇寮€鍚庯紝鏈疆浼氳瘽蹇呴』缁撴潫锛涙槸鍚﹂噸鍚灇涓剧敱涓婂眰浼氳瘽閫昏緫鍐冲畾銆? p_Context->LockedCandidateIndex = -1; p_Port->IsOpened = false; return; } QTimer::singleShot( 0, QCoreApplication::instance(), [p_Port]() { Dri_Nus_StartNextCandidate(p_Port); }); } void Dri_Nus_AttachServiceSignals( Dri_Nus_Struct_Port* p_Port, Dri_Nus_Struct_Context* p_Context, const QString& EndpointId, const QString& DeviceLabel) { QObject::connect( p_Context->p_Service, &QLowEnergyService::stateChanged, [p_Port, p_Context, EndpointId, DeviceLabel](QLowEnergyService::ServiceState State) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } if (State != QLowEnergyService::ServiceDiscovered) { return; } p_Context->WriteCharacteristic = p_Context->p_Service->characteristic(kRxCharacteristicUuid); p_Context->NotifyCharacteristic = p_Context->p_Service->characteristic(kTxCharacteristicUuid); if (!p_Context->WriteCharacteristic.isValid() || !p_Context->NotifyCharacteristic.isValid()) { Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE candidate %1 is missing NUS characteristics. Try the next one.") .arg(DeviceLabel)); return; } p_Context->NotifyDescriptor = p_Context->NotifyCharacteristic.descriptor( QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); if (p_Context->NotifyDescriptor.isValid()) { p_Context->p_Service->writeDescriptor( p_Context->NotifyDescriptor, QByteArray::fromHex("0100")); return; } p_Port->IsConnected = true; p_Port->TextEndpointSummary = QStringLiteral("NUS service is ready on %1").arg(DeviceLabel); }); QObject::connect( p_Context->p_Service, &QLowEnergyService::descriptorWritten, [p_Port, p_Context, EndpointId, DeviceLabel]( const QLowEnergyDescriptor& Descriptor, const QByteArray&) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } if (Descriptor != p_Context->NotifyDescriptor) { return; } p_Port->IsConnected = true; p_Port->TextEndpointSummary = QStringLiteral("NUS notify is ready on %1").arg(DeviceLabel); }); QObject::connect( p_Context->p_Service, &QLowEnergyService::characteristicChanged, [p_Context, EndpointId, DeviceLabel]( const QLowEnergyCharacteristic& Characteristic, const QByteArray& Value) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } if (Characteristic.uuid() != kTxCharacteristicUuid) { return; } Dri_Nus_QueuePacket( p_Context, Value, QStringLiteral("BLE NUS (%1)").arg(DeviceLabel), EndpointId); }); QObject::connect( p_Context->p_Service, &QLowEnergyService::characteristicWritten, [p_Port, p_Context, EndpointId, DeviceLabel]( const QLowEnergyCharacteristic& Characteristic, const QByteArray& Value) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } if (Characteristic.uuid() != kRxCharacteristicUuid) { return; } Com_Enum_ProtocolType PacketType = Com_Enum_ProtocolType_None; const QString PacketTypeText = Com_Protocol_DecodeMessageType(Value, &PacketType) ? Dri_Nus_GetPacketTypeText(PacketType) : QStringLiteral("unknown"); p_Port->TextEndpointSummary = QStringLiteral("BLE write acknowledged by RX characteristic: %1 (%2)") .arg(DeviceLabel, PacketTypeText); p_Port->HasWriteAck = true; }); QObject::connect( p_Context->p_Service, static_cast( &QLowEnergyService::error), [p_Port, p_Context, EndpointId, DeviceLabel](QLowEnergyService::ServiceError) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE candidate %1 has a NUS service error.").arg(DeviceLabel)); }); p_Context->p_Service->discoverDetails(); } bool Dri_Nus_StartController( Dri_Nus_Struct_Port* p_Port, Dri_Nus_Struct_Context* p_Context, int CandidateIndex) { if ((p_Context == nullptr) || (p_Context->p_Controller != nullptr)) { return false; } if ((CandidateIndex < 0) || (CandidateIndex >= p_Context->CandidateList.size())) { return false; } const Dri_Nus_Struct_Candidate& Candidate = p_Context->CandidateList.at(CandidateIndex); p_Context->CurrentCandidateIndex = CandidateIndex; Dri_Nus_ResetServiceState(p_Port); if (!Candidate.DeviceInfo.isValid()) { p_Port->TextEndpointSummary = QStringLiteral("BLE candidate is invalid: %1").arg(Candidate.DeviceLabel); return false; } if (!Dri_Nus_HasStableConnectIdentity(Candidate.DeviceInfo)) { p_Port->TextEndpointSummary = QStringLiteral("BLE candidate %1 has no stable address/uuid. Skip connect.") .arg(Candidate.DeviceLabel); return false; } p_Context->p_Controller = QLowEnergyController::createCentral( Candidate.DeviceInfo, QCoreApplication::instance()); if (p_Context->p_Controller == nullptr) { p_Port->TextEndpointSummary = QStringLiteral("Failed to create BLE controller for %1").arg(Candidate.DeviceLabel); return false; } const QString EndpointId = Candidate.EndpointId; const QString DeviceLabel = Candidate.DeviceLabel; p_Port->TextEndpointSummary = QStringLiteral("BLE connecting to target device: %1") .arg(Dri_Nus_FormatCandidateSummary(Candidate)); QObject::connect( p_Context->p_Controller, &QLowEnergyController::stateChanged, [p_Port, p_Context, EndpointId, DeviceLabel](QLowEnergyController::ControllerState State) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } p_Port->TextEndpointSummary = QStringLiteral("BLE controller state %1 for %2") .arg(static_cast(State)) .arg(DeviceLabel); }); QObject::connect( p_Context->p_Controller, &QLowEnergyController::connected, [p_Port, p_Context, EndpointId, DeviceLabel]() { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId) || (p_Context->p_Controller == nullptr)) { return; } p_Port->TextEndpointSummary = QStringLiteral("BLE link connected. Discovering NUS service on %1") .arg(DeviceLabel); p_Context->p_Controller->discoverServices(); }); QObject::connect( p_Context->p_Controller, &QLowEnergyController::disconnected, [p_Port, p_Context, EndpointId, DeviceLabel]() { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE candidate disconnected: %1").arg(DeviceLabel)); }); QObject::connect( p_Context->p_Controller, static_cast( &QLowEnergyController::error), [p_Port, p_Context, EndpointId, DeviceLabel](QLowEnergyController::Error) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE candidate connect error: %1").arg(DeviceLabel)); }); QObject::connect( p_Context->p_Controller, &QLowEnergyController::serviceDiscovered, [p_Port, p_Context, EndpointId, DeviceLabel](const QBluetoothUuid& ServiceUuid) { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } if (ServiceUuid == kServiceUuid) { p_Context->HasTargetService = true; p_Port->TextEndpointSummary = QStringLiteral("BLE found NUS service on %1").arg(DeviceLabel); } }); QObject::connect( p_Context->p_Controller, &QLowEnergyController::discoveryFinished, [p_Port, p_Context, EndpointId, DeviceLabel]() { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId)) { return; } if (!p_Context->HasTargetService) { Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE candidate %1 does not expose the NUS service.").arg(DeviceLabel)); return; } p_Context->p_Service = p_Context->p_Controller->createServiceObject(kServiceUuid, p_Context->p_Controller); if (p_Context->p_Service == nullptr) { Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE candidate %1 failed to create the NUS service object.") .arg(DeviceLabel)); return; } Dri_Nus_AttachServiceSignals( p_Port, p_Context, EndpointId, DeviceLabel); }); QTimer::singleShot( 0, p_Context->p_Controller, [p_Port, p_Context, EndpointId, DeviceLabel]() { if (!Dri_Nus_IsCurrentCandidate(p_Context, EndpointId) || (p_Context->p_Controller == nullptr)) { return; } p_Port->TextEndpointSummary = QStringLiteral("BLE starting GATT connect on %1").arg(DeviceLabel); p_Context->p_Controller->connectToDevice(); }); return true; } bool Dri_Nus_StartNextCandidate(Dri_Nus_Struct_Port* p_Port) { Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; if ((p_Context == nullptr) || !p_Port->IsOpened || !p_Context->IsDiscoveryFinished || (p_Context->LockedCandidateIndex >= 0) || (p_Context->p_Controller != nullptr)) { return false; } for (int Index = p_Context->CurrentCandidateIndex + 1; Index < p_Context->CandidateList.size(); ++Index) { if (Dri_Nus_StartController(p_Port, p_Context, Index)) { return true; } } p_Port->IsOpened = false; p_Port->TextEndpointSummary = p_Context->CandidateList.isEmpty() ? QStringLiteral("No target BLE keyboard was discovered for NUS handshake.") : QStringLiteral("All target BLE candidates failed NUS handshake confirmation."); return false; } } // namespace void Dri_Nus_Close(Dri_Nus_Struct_Port* p_Port) { Dri_Nus_DeleteContext(p_Port->p_Context); *p_Port = Dri_Nus_Struct_Port(); } bool Dri_Nus_Init( Dri_Nus_Struct_Port* p_Port, const Com_Struct_DeviceConfig& DeviceConfig, QString* p_TextStatus) { Dri_Nus_Close(p_Port); if ((QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods() & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) == 0) { p_Port->TextEndpointSummary = QStringLiteral("The current Qt platform does not support BLE."); if (p_TextStatus != nullptr) { *p_TextStatus = p_Port->TextEndpointSummary; } return false; } auto* p_Context = new Dri_Nus_Struct_Context(); p_Context->p_DiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(); p_Context->p_DiscoveryAgent->setLowEnergyDiscoveryTimeout(3000); QObject::connect( p_Context->p_DiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, [p_Port, p_Context](const QBluetoothDeviceInfo& DeviceInfo) { if ((DeviceInfo.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) == 0) { return; } Dri_Nus_Struct_Candidate Candidate; Candidate.DeviceInfo = DeviceInfo; Candidate.DeviceName = DeviceInfo.name().trimmed(); if (!Dri_Nus_IsPreferredDeviceName(Candidate.DeviceName)) { return; } Candidate.AddressText = Dri_Nus_NormalizeAddressText(DeviceInfo.address()); Candidate.DeviceLabel = !Candidate.DeviceName.isEmpty() ? Candidate.DeviceName : Candidate.AddressText; Candidate.EndpointId = Dri_Nus_BuildEndpointId(DeviceInfo, p_Context->CandidateList.size() + 1); for (const Dri_Nus_Struct_Candidate& ExistingCandidate : p_Context->CandidateList) { if (ExistingCandidate.EndpointId.compare( Candidate.EndpointId, Qt::CaseInsensitive) == 0) { return; } } p_Context->CandidateList.append(Candidate); p_Port->TextEndpointSummary = QStringLiteral("BLE target candidate discovered: %1") .arg(Dri_Nus_FormatCandidateSummary(Candidate)); }); QObject::connect( p_Context->p_DiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, [p_Port, p_Context]() { p_Context->IsDiscoveryFinished = true; p_Port->TextEndpointSummary = p_Context->CandidateList.isEmpty() ? QStringLiteral("BLE scan finished, but the target keyboard name was not found.") : QStringLiteral("BLE scan finished. %1 target candidate(s) found. Starting handshake.") .arg(p_Context->CandidateList.size()); QTimer::singleShot( 0, p_Context->p_DiscoveryAgent, [p_Port]() { Dri_Nus_StartNextCandidate(p_Port); }); }); QObject::connect( p_Context->p_DiscoveryAgent, static_cast( &QBluetoothDeviceDiscoveryAgent::error), [p_Port, p_Context](QBluetoothDeviceDiscoveryAgent::Error) { p_Context->IsDiscoveryFinished = true; QTimer::singleShot( 0, p_Context->p_DiscoveryAgent, [p_Port]() { Dri_Nus_StartNextCandidate(p_Port); }); }); p_Port->p_Context = p_Context; p_Port->IsOpened = true; p_Port->TextEndpointSummary = QStringLiteral("BLE scan started. Looking for the target keyboard name only."); p_Context->p_DiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); if (p_TextStatus != nullptr) { *p_TextStatus = p_Port->TextEndpointSummary; } Q_UNUSED(DeviceConfig); return true; } bool Dri_Nus_Read( Dri_Nus_Struct_Port* p_Port, Com_Struct_RawPacket* p_Packet, QString* p_TextStatus) { *p_Packet = Com_Struct_RawPacket(); p_Packet->Source = Com_Enum_RawPacketSource_BleNus; p_Packet->PortName = QStringLiteral("BLE NUS"); Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; if (!p_Port->IsOpened || (p_Context == nullptr) || p_Context->PacketQueue.isEmpty()) { return false; } *p_Packet = p_Context->PacketQueue.dequeue(); if (p_TextStatus != nullptr) { *p_TextStatus = p_Port->TextEndpointSummary; } return p_Packet->IsValid; } bool Dri_Nus_LockCandidate( Dri_Nus_Struct_Port* p_Port, const QString& EndpointId, QString* p_TextStatus) { Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; const Dri_Nus_Struct_Candidate* p_CurrentCandidate = Dri_Nus_GetCurrentCandidate(p_Context); if (!p_Port->IsOpened || !p_Port->IsConnected || (p_CurrentCandidate == nullptr)) { if (p_TextStatus != nullptr) { *p_TextStatus = QStringLiteral("BLE NUS does not have a candidate ready to lock yet."); } return false; } if (p_CurrentCandidate->EndpointId.compare(EndpointId, Qt::CaseInsensitive) != 0) { if (p_TextStatus != nullptr) { *p_TextStatus = QStringLiteral("The current BLE candidate does not match the lock target."); } return false; } p_Context->LockedCandidateIndex = p_Context->CurrentCandidateIndex; if (p_Context->p_DiscoveryAgent != nullptr) { p_Context->p_DiscoveryAgent->stop(); } p_Port->TextEndpointSummary = QStringLiteral("BLE NUS 宸查攣瀹氱洰鏍囪澶囷細%1").arg(p_CurrentCandidate->DeviceLabel); if (p_TextStatus != nullptr) { *p_TextStatus = p_Port->TextEndpointSummary; } return true; } bool Dri_Nus_DiscardCandidate( Dri_Nus_Struct_Port* p_Port, const QString& EndpointId, QString* p_TextStatus) { Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; const Dri_Nus_Struct_Candidate* p_CurrentCandidate = Dri_Nus_GetCurrentCandidate(p_Context); if (!p_Port->IsOpened || (p_CurrentCandidate == nullptr)) { return false; } if (p_CurrentCandidate->EndpointId.compare(EndpointId, Qt::CaseInsensitive) != 0) { return false; } const QString DeviceLabel = p_CurrentCandidate->DeviceLabel; Dri_Nus_AdvanceFromCurrentCandidate( p_Port, QStringLiteral("BLE 宸蹭涪寮冩彙鎵嬩笉鍖归厤鍊欓€夛細%1").arg(DeviceLabel)); if (p_TextStatus != nullptr) { *p_TextStatus = QStringLiteral("BLE 宸蹭涪寮冩彙鎵嬩笉鍖归厤鍊欓€夛細%1").arg(DeviceLabel); } return true; } bool Dri_Nus_Write( Dri_Nus_Struct_Port* p_Port, const QByteArray& PacketBody, QString* p_TextStatus) { Dri_Nus_Struct_Context* p_Context = p_Port->p_Context; const Dri_Nus_Struct_Candidate* p_CurrentCandidate = Dri_Nus_GetCurrentCandidate(p_Context); if (!p_Port->IsOpened || (p_Context == nullptr) || (p_Context->p_Service == nullptr) || (p_CurrentCandidate == nullptr)) { if (p_TextStatus != nullptr) { *p_TextStatus = QStringLiteral("BLE NUS is not ready yet. Skip send."); } return false; } if (PacketBody.isEmpty() || !p_Context->WriteCharacteristic.isValid()) { if (p_TextStatus != nullptr) { *p_TextStatus = QStringLiteral("BLE NUS write characteristic is not ready."); } return false; } const auto CharacteristicProperties = p_Context->WriteCharacteristic.properties(); const bool SupportsWriteWithResponse = (CharacteristicProperties & QLowEnergyCharacteristic::Write) != 0; const bool SupportsWriteWithoutResponse = (CharacteristicProperties & QLowEnergyCharacteristic::WriteNoResponse) != 0; if (!SupportsWriteWithoutResponse && !SupportsWriteWithResponse) { if (p_TextStatus != nullptr) { *p_TextStatus = QStringLiteral("BLE NUS RX characteristic has no writable property."); } return false; } const QLowEnergyService::WriteMode PrimaryWriteMode = SupportsWriteWithResponse ? QLowEnergyService::WriteWithResponse : QLowEnergyService::WriteWithoutResponse; p_Context->p_Service->writeCharacteristic( p_Context->WriteCharacteristic, PacketBody, PrimaryWriteMode); if (p_TextStatus != nullptr) { const QString WriteModeText = (PrimaryWriteMode == QLowEnergyService::WriteWithResponse) ? QStringLiteral("write-with-response") : QStringLiteral("write-without-response"); *p_TextStatus = QStringLiteral("BLE NUS sent protocol packet via %1: %2") .arg(WriteModeText, p_CurrentCandidate->DeviceLabel); } return true; }