/** * Author: Sven Gothel * Copyright (c) 2022 Gothel Software e.K. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef DBT_CLIENT00_HPP_ #define DBT_CLIENT00_HPP_ #include "dbt_constants.hpp" #include "dbt_client_test.hpp" class DBTClient00; typedef std::shared_ptr DBTClient00Ref; using namespace jau; using namespace jau::fractions_i64_literals; /** * This central BTRole::Master participant works with DBTServer00. */ class DBTClient00 : public DBTClientTest { private: bool KEEP_CONNECTED = false; bool REMOVE_DEVICE = false; DiscoveryPolicy discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY; // default value static const bool GATT_VERBOSE = false; static const bool SHOW_UPDATE_EVENTS = false; jau::sc_atomic_int deviceReadyCount = 0; jau::sc_atomic_int disconnectCount = 0; jau::sc_atomic_int notificationsReceived = 0; jau::sc_atomic_int indicationsReceived = 0; jau::sc_atomic_int completedGATTCommands = 0; jau::sc_atomic_int completedMeasurementsTotal = 0; jau::sc_atomic_int completedMeasurementsSuccess = 0; jau::sc_atomic_int measurementsLeft = 0; const uint64_t timestamp_t0 = getCurrentMilliseconds(); // const fraction_i64 timestamp_t0 = jau::getMonotonicMicroseconds(); const uint8_t cmd_arg = 0x44; class MyAdapterStatusListener : public AdapterStatusListener { public: DBTClient00& parent; MyAdapterStatusListener(DBTClient00& p) : parent(p) {} void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, const AdapterSetting changedmask, const uint64_t timestamp) override { const bool initialSetting = AdapterSetting::NONE == oldmask; if( initialSetting ) { fprintf_td(stderr, "****** Client SETTINGS_INITIAL: %s -> %s, changed %s\n", to_string(oldmask).c_str(), to_string(newmask).c_str(), to_string(changedmask).c_str()); } else { fprintf_td(stderr, "****** Client SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(), to_string(newmask).c_str(), to_string(changedmask).c_str()); } fprintf_td(stderr, "Client Status BTAdapter:\n"); fprintf_td(stderr, "%s\n", a.toString().c_str()); (void)timestamp; } void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override { fprintf_td(stderr, "****** Client DISCOVERING: meta %s, changed[%s, enabled %d, policy %s]: %s\n", to_string(currentMeta).c_str(), to_string(changedType).c_str(), changedEnabled, to_string(policy).c_str(), a.toString().c_str()); (void)timestamp; } bool deviceFound(BTDeviceRef device, const uint64_t timestamp) override { (void)timestamp; if( !BTDeviceRegistry::isDeviceProcessing( device->getAddressAndType() ) && ( !BTDeviceRegistry::isWaitingForAnyDevice() || ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName()) && ( 0 < parent.measurementsLeft || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) ) ) ) { fprintf_td(stderr, "****** Client FOUND__-0: Connecting %s\n", device->toString(true).c_str()); { const uint64_t td = jau::getCurrentMilliseconds() - parent.timestamp_t0; // adapter-init -> now fprintf_td(stderr, "PERF: adapter-init -> FOUND__-0 %" PRIu64 " ms\n", td); } std::thread dc(&DBTClient00::connectDiscoveredDevice, &parent, device); // @suppress("Invalid arguments") dc.detach(); return true; } else { fprintf_td(stderr, "****** Client FOUND__-1: NOP %s\n", device->toString(true).c_str()); return false; } } void deviceUpdated(BTDeviceRef device, const EIRDataType updateMask, const uint64_t timestamp) override { (void)device; (void)updateMask; (void)timestamp; } void deviceConnected(BTDeviceRef device, const bool discovered, const uint64_t timestamp) override { fprintf_td(stderr, "****** Client CONNECTED (discovered %d): %s\n", discovered, device->toString(true).c_str()); (void)discovered; (void)timestamp; } void devicePairingState(BTDeviceRef device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override { fprintf_td(stderr, "****** Client PAIRING STATE: state %s, mode %s, %s\n", to_string(state).c_str(), to_string(mode).c_str(), device->toString().c_str()); (void)timestamp; switch( state ) { case SMPPairingState::NONE: // next: deviceReady(..) break; case SMPPairingState::FAILED: { const bool res = SMPKeyBin::remove(DBTConstants::CLIENT_KEY_PATH, *device); fprintf_td(stderr, "****** PAIRING_STATE: state %s; Remove key file %s, res %d\n", to_string(state).c_str(), SMPKeyBin::getFilename(DBTConstants::CLIENT_KEY_PATH, *device).c_str(), res); // next: deviceReady() or deviceDisconnected(..) } break; case SMPPairingState::REQUESTED_BY_RESPONDER: // next: FEATURE_EXCHANGE_STARTED break; case SMPPairingState::FEATURE_EXCHANGE_STARTED: // next: FEATURE_EXCHANGE_COMPLETED break; case SMPPairingState::FEATURE_EXCHANGE_COMPLETED: // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION break; case SMPPairingState::PASSKEY_EXPECTED: { const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName()); if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) { std::thread dc(&BTDevice::setPairingPasskey, device, static_cast( sec->getPairingPasskey() )); dc.detach(); } else { std::thread dc(&BTDevice::setPairingPasskey, device, 0); // 3s disconnect: std::thread dc(&BTDevice::setPairingPasskeyNegative, device); dc.detach(); } // next: KEY_DISTRIBUTION or FAILED } break; case SMPPairingState::NUMERIC_COMPARE_EXPECTED: { const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName()); if( nullptr != sec ) { std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison()); dc.detach(); } else { std::thread dc(&BTDevice::setPairingNumericComparison, device, false); dc.detach(); } // next: KEY_DISTRIBUTION or FAILED } break; case SMPPairingState::OOB_EXPECTED: // FIXME: ABORT break; case SMPPairingState::KEY_DISTRIBUTION: // next: COMPLETED or FAILED break; case SMPPairingState::COMPLETED: // next: deviceReady(..) break; default: // nop break; } } void deviceReady(BTDeviceRef device, const uint64_t timestamp) override { (void)timestamp; if( !BTDeviceRegistry::isDeviceProcessing( device->getAddressAndType() ) && ( !BTDeviceRegistry::isWaitingForAnyDevice() || ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName()) && ( 0 < parent.measurementsLeft || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) ) ) ) { parent.deviceReadyCount++; fprintf_td(stderr, "****** Client READY-0: Processing[%d] %s\n", parent.deviceReadyCount.load(), device->toString(true).c_str()); BTDeviceRegistry::addToProcessingDevices(device->getAddressAndType(), device->getName()); // Be nice to Test* case, allowing to reach its own listener.deviceReady() added later std::thread dc(&DBTClient00::processReadyDevice, &parent, device); dc.detach(); // processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution! } else { fprintf_td(stderr, "****** Client READY-1: NOP %s\n", device->toString(true).c_str()); } } void deviceDisconnected(BTDeviceRef device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override { fprintf_td(stderr, "****** Client DISCONNECTED: Reason 0x%X (%s), old handle %s: %s\n", static_cast(reason), to_string(reason).c_str(), to_hexstring(handle).c_str(), device->toString(true).c_str()); (void)timestamp; parent.disconnectCount++; std::thread dc(&DBTClient00::removeDevice, &parent, device); // @suppress("Invalid arguments") dc.detach(); } std::string toString() const override { return "Client MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } }; class MyGATTEventListener : public BTGattChar::Listener { private: DBTClient00& parent; int i, j; public: MyGATTEventListener(DBTClient00& p, int i_, int j_) : parent(p), i(i_), j(j_) {} void notificationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp) override { if( GATT_VERBOSE ) { const uint64_t tR = jau::getCurrentMilliseconds(); fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic-Notify: UUID %s, td %" PRIu64 " ******\n", i, j, charDecl->value_type->toUUID128String().c_str(), (tR-timestamp)); fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic: %s ******\n", i, j, charDecl->toString().c_str()); fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } parent.notificationsReceived++; } void indicationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp, const bool confirmationSent) override { if( GATT_VERBOSE ) { const uint64_t tR = jau::getCurrentMilliseconds(); fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic-Indication: UUID %s, td %" PRIu64 ", confirmed %d ******\n", i, j, charDecl->value_type->toUUID128String().c_str(), (tR-timestamp), confirmationSent); fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic: %s ******\n", i, j, charDecl->toString().c_str()); fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } parent.indicationsReceived++; } }; const std::string adapterShortName = "TDev2Clt"; std::string adapterName = "TestDev2_Clt"; EUI48 useAdapter = EUI48::ALL_DEVICE; BTMode btMode = BTMode::DUAL; BTAdapterRef clientAdapter = nullptr; std::shared_ptr myAdapterStatusListener = std::make_shared(*this); public: DBTClient00(const std::string& adapterName_, const EUI48 useAdapter_, const BTMode btMode_) { this->adapterName = adapterName_; this->useAdapter = useAdapter_; this->btMode = btMode_; } std::string getName() override { return adapterName; } void setAdapter(BTAdapterRef clientAdapter_) override { this->clientAdapter = clientAdapter_; } BTAdapterRef getAdapter() override { return clientAdapter; } void setProtocolSessionsLeft(const int v) override { measurementsLeft = v; } int getProtocolSessionsLeft() override { return measurementsLeft; } int getProtocolSessionsDoneTotal() override { return completedMeasurementsTotal; } int getProtocolSessionsDoneSuccess() override { return completedMeasurementsSuccess; } int getDisconnectCount() override { return disconnectCount; } void setDiscoveryPolicy(const DiscoveryPolicy v) override { discoveryPolicy = v; } void setKeepConnected(const bool v) override { KEEP_CONNECTED = v; } void setRemoveDevice(const bool v) override { REMOVE_DEVICE = v; } private: void resetLastProcessingStats() { completedGATTCommands = 0; notificationsReceived = 0; indicationsReceived = 0; } void connectDiscoveredDevice(BTDeviceRef device) { fprintf_td(stderr, "****** Client Connecting Device: Start %s\n", device->toString().c_str()); resetLastProcessingStats(); const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName()); if( nullptr != sec ) { fprintf_td(stderr, "****** Client Connecting Device: Found SecurityDetail %s for %s\n", sec->toString().c_str(), device->toString().c_str()); } else { fprintf_td(stderr, "****** Client Connecting Device: No SecurityDetail for %s\n", device->toString().c_str()); } const BTSecurityLevel req_sec_level = nullptr != sec ? sec->getSecLevel() : BTSecurityLevel::UNSET; HCIStatusCode res = device->uploadKeys(DBTConstants::CLIENT_KEY_PATH, req_sec_level, true /* verbose_ */); fprintf_td(stderr, "****** Client Connecting Device: BTDevice::uploadKeys(...) result %s\n", to_string(res).c_str()); if( HCIStatusCode::SUCCESS != res ) { if( nullptr != sec ) { if( sec->isSecurityAutoEnabled() ) { bool r = device->setConnSecurityAuto( sec->getSecurityAutoIOCap() ); fprintf_td(stderr, "****** Client Connecting Device: Using SecurityDetail.SEC AUTO %s, set OK %d\n", sec->toString().c_str(), r); } else if( sec->isSecLevelOrIOCapSet() ) { bool r = device->setConnSecurity( sec->getSecLevel(), sec->getIOCap() ); fprintf_td(stderr, "****** Client Connecting Device: Using SecurityDetail.Level+IOCap %s, set OK %d\n", sec->toString().c_str(), r); } else { bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY ); fprintf_td(stderr, "****** Client Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY (%s) -> set OK %d\n", sec->toString().c_str(), r); } } else { bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY ); fprintf_td(stderr, "****** Client Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK %d\n", r); } } std::shared_ptr eir = device->getEIR(); fprintf_td(stderr, "Client EIR-1 %s\n", device->getEIRInd()->toString().c_str()); fprintf_td(stderr, "Client EIR-2 %s\n", device->getEIRScanRsp()->toString().c_str()); fprintf_td(stderr, "Client EIR-+ %s\n", eir->toString().c_str()); uint16_t conn_interval_min = (uint16_t)8; // 10ms uint16_t conn_interval_max = (uint16_t)12; // 15ms const uint16_t conn_latency = (uint16_t)0; if( eir->isSet(EIRDataType::CONN_IVAL) ) { eir->getConnInterval(conn_interval_min, conn_interval_max); } const uint16_t supervision_timeout = (uint16_t) getHCIConnSupervisorTimeout(conn_latency, (int) ( conn_interval_max * 1.25 ) /* ms */); res = device->connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout); fprintf_td(stderr, "****** Client Connecting Device: End result %s of %s\n", to_string(res).c_str(), device->toString().c_str()); } void processReadyDevice(BTDeviceRef device) { fprintf_td(stderr, "****** Client Processing Ready Device: Start %s\n", device->toString().c_str()); const uint64_t t1 = jau::getCurrentMilliseconds(); SMPKeyBin::createAndWrite(*device, DBTConstants::CLIENT_KEY_PATH, true /* verbose */); const uint64_t t2 = jau::getCurrentMilliseconds(); bool success = false; { LE_PHYs resTx, resRx; HCIStatusCode res = device->getConnectedLE_PHY(resTx, resRx); fprintf_td(stderr, "****** Client Got Connected LE PHY: status %s: Tx %s, Rx %s\n", to_string(res).c_str(), to_string(resTx).c_str(), to_string(resRx).c_str()); } const uint64_t t3 = jau::getCurrentMilliseconds(); // // GATT Service Processing // try { jau::darray primServices = device->getGattServices(); if( 0 == primServices.size() ) { fprintf_td(stderr, "****** Clinet Processing Ready Device: getServices() failed %s\n", device->toString().c_str()); goto exit; } const uint64_t t5 = jau::getCurrentMilliseconds(); { const uint64_t td00 = device->getLastDiscoveryTimestamp() - timestamp_t0; // adapter-init to discovered const uint64_t td01 = t1 - timestamp_t0; // adapter-init to processing-start const uint64_t td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete const uint64_t tdc1 = t1 - device->getLastDiscoveryTimestamp(); // discovered to processing-start const uint64_t tdc5 = t5 - device->getLastDiscoveryTimestamp(); // discovered to gatt-complete const uint64_t td12 = t2 - t1; // SMPKeyBin const uint64_t td23 = t3 - t2; // LE_PHY const uint64_t td13 = t3 - t1; // SMPKeyBin + LE_PHY const uint64_t td35 = t5 - t3; // get-gatt-services fprintf_td(stderr, "\n\n\n"); fprintf_td(stderr, "PERF: GATT primary-services completed\n" "PERF: adapter-init to discovered %" PRIu64 " ms,\n" "PERF: adapter-init to processing-start %" PRIu64 " ms,\n" "PERF: adapter-init to gatt-complete %" PRIu64 " ms\n" "PERF: discovered to processing-start %" PRIu64 " ms,\n" "PERF: discovered to gatt-complete %" PRIu64 " ms,\n" "PERF: SMPKeyBin + LE_PHY %" PRIu64 " ms (SMPKeyBin %" PRIu64 " ms, LE_PHY %" PRIu64 " ms),\n" "PERF: get-gatt-services %" PRIu64 " ms,\n\n", td00, td01, td05, tdc1, tdc5, td13, td12, td23, td35); } { BTGattCmd cmd = BTGattCmd(*device, "TestCmd", DBTConstants::CommandUUID, DBTConstants::ResponseUUID, 256); cmd.setVerbose(true); const bool cmd_resolved = cmd.isResolved(); fprintf_td(stderr, "Command test: %s, resolved %d\n", cmd.toString().c_str(), cmd_resolved); POctets cmd_data(1, endian::little); cmd_data.put_uint8_nc(0, cmd_arg); const HCIStatusCode cmd_res = cmd.send(true /* prefNoAck */, cmd_data, 3_s); if( HCIStatusCode::SUCCESS == cmd_res ) { const jau::TROOctets& resp = cmd.getResponse(); if( 1 == resp.size() && resp.get_uint8_nc(0) == cmd_arg ) { fprintf_td(stderr, "Client Success: %s -> %s (echo response)\n", cmd.toString().c_str(), resp.toString().c_str()); completedGATTCommands++; } else { fprintf_td(stderr, "Client Failure: %s -> %s (different response)\n", cmd.toString().c_str(), resp.toString().c_str()); } } else { fprintf_td(stderr, "Client Failure: %s -> %s\n", cmd.toString().c_str(), to_string(cmd_res).c_str()); } // cmd.close(); // done via dtor } for(size_t i=0; itoUUID128String().c_str(), primService.type->getTypeSizeString().c_str()); fprintf_td(stderr, " [%2.2d] %s\n", i, primService.toString().c_str()); } jau::darray & serviceCharacteristics = primService.characteristicList; for(size_t j=0; jvalue_type->toUUID128String().c_str(), serviceChar->value_type->getTypeSizeString().c_str()); fprintf_td(stderr, " [%2.2d.%2.2d] %s\n", i, j, serviceChar->toString().c_str()); } if( serviceChar->hasProperties(BTGattChar::PropertyBitVal::Read) ) { POctets value(BTGattHandler::number(BTGattHandler::Defaults::MAX_ATT_MTU), 0, jau::endian::little); if( serviceChar->readValue(value) ) { std::string sval = dfa_utf8_decode(value.get_ptr(), value.size()); if( GATT_VERBOSE ) { fprintf_td(stderr, " [%2.2d.%2.2d] value: %s ('%s')\n", (int)i, (int)j, value.toString().c_str(), sval.c_str()); } } } jau::darray & charDescList = serviceChar->descriptorList; for(size_t k=0; ktoUUID128String().c_str(), charDesc.type->getTypeSizeString().c_str()); fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] %s\n", i, j, k, charDesc.toString().c_str()); } } bool cccdEnableResult[2]; if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { // ClientCharConfigDescriptor (CCD) is available std::shared_ptr cl = std::make_shared(*this, i, j); bool clAdded = serviceChar->addCharListener( cl ); if( GATT_VERBOSE ) { fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded); fprintf_td(stderr, "\n"); } } } if( GATT_VERBOSE ) { fprintf_td(stderr, "\n"); } } { const fraction_timespec t0 = getMonotonicTime(); bool timeout = false; do { success = completedGATTCommands >= 1 && ( notificationsReceived >= 2 || indicationsReceived >= 2 ); if( !success ) { const fraction_i64 td = ( getMonotonicTime() - t0 ).to_fraction_i64(); timeout = 3_s < td; if( !timeout ) { jau::sleep_for( 17_ms ); } } } while( !success && !timeout ); } { // Tell server we have successfully completed the test. BTGattCmd cmd = BTGattCmd(*device, "FinalHandshake", DBTConstants::CommandUUID, DBTConstants::ResponseUUID, 256); cmd.setVerbose(true); const bool cmd_resolved = cmd.isResolved(); fprintf_td(stderr, "FinalCommand test: %s, resolved %d\n", cmd.toString().c_str(), cmd_resolved); const int data_sz = DBTConstants::FailHandshakeCommandData.size(); POctets cmd_data(data_sz, endian::little); if( success ) { cmd_data.put_bytes_nc(0, DBTConstants::SuccessHandshakeCommandData.data(), data_sz); } else { cmd_data.put_bytes_nc(0, DBTConstants::FailHandshakeCommandData.data(), data_sz); } const HCIStatusCode cmd_res = cmd.send(true /* prefNoAck */, cmd_data, 3_s); if( HCIStatusCode::SUCCESS == cmd_res ) { const jau::TROOctets& resp = cmd.getResponse(); if( cmd_data.size() == resp.size() && 0 == ::memcmp(cmd_data.get_ptr(), resp.get_ptr(), resp.size()) ) { fprintf_td(stderr, "Client Success: %s -> %s (echo response)\n", cmd.toString().c_str(), resp.toString().c_str()); } else { fprintf_td(stderr, "Client Failure: %s -> %s (different response)\n", cmd.toString().c_str(), resp.toString().c_str()); } } else { fprintf_td(stderr, "Client Failure: %s -> %s\n", cmd.toString().c_str(), to_string(cmd_res).c_str()); } // cmd.close(); // done via dtor } } catch ( std::exception & e ) { fprintf_td(stderr, "****** Client Processing Ready Device: Exception.2 caught for %s: %s\n", device->toString().c_str(), e.what()); } exit: fprintf_td(stderr, "****** Client Processing Ready Device: End-1: Success %d on %s; devInProc %zu\n", success, device->toString().c_str(), BTDeviceRegistry::getProcessingDeviceCount()); BTDeviceRegistry::removeFromProcessingDevices(device->getAddressAndType()); if( DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED == discoveryPolicy ) { device->getAdapter().removeDevicePausingDiscovery(*device); } fprintf_td(stderr, "****** Client Processing Ready Device: End-2: Success %d on %s; devInProc %zu\n", success, device->toString().c_str(), BTDeviceRegistry::getProcessingDeviceCount()); if( success ) { BTDeviceRegistry::addToProcessedDevices(device->getAddressAndType(), device->getName()); } device->removeAllCharListener(); if( !KEEP_CONNECTED ) { if( REMOVE_DEVICE ) { device->remove(); } else { device->disconnect(); } } completedMeasurementsTotal++; if( success ) { completedMeasurementsSuccess++; } if( 0 < measurementsLeft ) { measurementsLeft--; } fprintf_td(stderr, "****** Client Processing Ready Device: Success %d; Measurements completed %d" ", left %d; Received notitifications %d, indications %d" "; Completed GATT commands %d: %s\n", success, completedMeasurementsSuccess.load(), measurementsLeft.load(), notificationsReceived.load(), indicationsReceived.load(), completedGATTCommands.load(), device->getAddressAndType().toString().c_str()); } void removeDevice(BTDeviceRef device) { fprintf_td(stderr, "****** Client Remove Device: removing: %s\n", device->getAddressAndType().toString().c_str()); BTDeviceRegistry::removeFromProcessingDevices(device->getAddressAndType()); if( REMOVE_DEVICE ) { device->remove(); } } static const bool le_scan_active = true; // default value static const uint16_t le_scan_interval = 24; // default value static const uint16_t le_scan_window = 24; // default value static const uint8_t filter_policy = 0; // default value static const bool filter_dup = true; // default value public: HCIStatusCode startDiscovery(const std::string& msg) override { HCIStatusCode status = clientAdapter->startDiscovery( discoveryPolicy, le_scan_active, le_scan_interval, le_scan_window, filter_policy, filter_dup ); fprintf_td(stderr, "****** Client Start discovery (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), clientAdapter->toString().c_str()); return status; } HCIStatusCode stopDiscovery(const std::string& msg) override { HCIStatusCode status = clientAdapter->stopDiscovery(); fprintf_td(stderr, "****** Client Stop discovery (%s) result: %s\n", msg.c_str(), to_string(status).c_str()); return status; } void close(const std::string& msg) override { fprintf_td(stderr, "****** Client Close: %s\n", msg.c_str()); clientAdapter->stopDiscovery(); clientAdapter->removeStatusListener( myAdapterStatusListener ); } bool initAdapter(BTAdapterRef adapter) override { if( useAdapter != EUI48::ALL_DEVICE && useAdapter != adapter->getAddressAndType().address ) { fprintf_td(stderr, "initClientAdapter: Adapter not selected: %s\n", adapter->toString().c_str()); return false; } adapterName = adapterName + "-" + adapter->getAddressAndType().address.toString(); { auto it = std::remove( adapterName.begin(), adapterName.end(), ':'); adapterName.erase(it, adapterName.end()); } // Initialize with defaults and power-on if( !adapter->isInitialized() ) { HCIStatusCode status = adapter->initialize( btMode ); if( HCIStatusCode::SUCCESS != status ) { fprintf_td(stderr, "initClientAdapter: Adapter initialization failed: %s: %s\n", to_string(status).c_str(), adapter->toString().c_str()); return false; } } else if( !adapter->setPowered( true ) ) { fprintf_td(stderr, "initClientAdapter: Already initialized adapter power-on failed:: %s\n", adapter->toString().c_str()); return false; } // adapter is powered-on fprintf_td(stderr, "initClientAdapter.1: %s\n", adapter->toString().c_str()); { const LE_Features le_feats = adapter->getLEFeatures(); fprintf_td(stderr, "initClientAdapter: LE_Features %s\n", to_string(le_feats).c_str()); } if( adapter->setPowered(false) ) { HCIStatusCode status = adapter->setName(adapterName, adapterShortName); if( HCIStatusCode::SUCCESS == status ) { fprintf_td(stderr, "initClientAdapter: setLocalName OK: %s\n", adapter->toString().c_str()); } else { fprintf_td(stderr, "initClientAdapter: setLocalName failed: %s\n", adapter->toString().c_str()); return false; } if( !adapter->setPowered( true ) ) { fprintf_td(stderr, "initClientAdapter: setPower.2 on failed: %s\n", adapter->toString().c_str()); return false; } } else { fprintf_td(stderr, "initClientAdapter: setPowered.2 off failed: %s\n", adapter->toString().c_str()); return false; } // adapter is powered-on fprintf_td(stderr, "initClientAdapter.2: %s\n", adapter->toString().c_str()); { const LE_Features le_feats = adapter->getLEFeatures(); fprintf_td(stderr, "initClientAdapter: LE_Features %s\n", to_string(le_feats).c_str()); } if( adapter->getBTMajorVersion() > 4 ) { LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M }; HCIStatusCode res = adapter->setDefaultLE_PHY(Tx, Rx); fprintf_td(stderr, "initClientAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n", to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str()); } adapter->addStatusListener( myAdapterStatusListener ); return true; } }; #endif /* DBT_CLIENT00_HPP_ */