livoxProto1: Implement async getOrCreateDeviceReq+destroyDeviceReq

These are now both fully asynchronous. They also work fully
and both connect and disconnect to/from the Avia just fine,
in all tested scenarios.
This commit is contained in:
2025-09-09 12:07:49 -04:00
parent 48121ec84c
commit 20cdf64afb
9 changed files with 1041 additions and 269 deletions
+156 -24
View File
@@ -1,8 +1,16 @@
#include <algorithm>
#include <iostream>
#include <functional>
#include <optional>
#include <opts.h>
#include <AsynchronousContinuation.h>
#include <user/senseApiDesc.h>
#include "protocol.h"
#include "core.h"
#include "device.h"
#include "broadcastListener.h"
#include "livoxProto1.h"
namespace livoxProto1 {
@@ -10,7 +18,8 @@ static ProtoState protoState =
{
.isInitialized = false,
.componentThread = nullptr,
.deviceManager = nullptr
.deviceManager = nullptr,
.smoCallbacks = {}
};
ProtoState& getProtoState()
@@ -46,13 +55,80 @@ void DeviceManager::deviceGoneAwayInd(const comms::DiscoveredDevice &device)
}
}
std::shared_ptr<Device> DeviceManager::getOrCreateDevice(
std::optional<std::shared_ptr<Device>> DeviceManager::getDevice(
const std::string &deviceIdentifier
)
{
for (auto& device : devices)
{
if (comms::deviceIdentifiersEqual(
device->discoveredDevice.deviceIdentifier, deviceIdentifier))
{
return device;
}
}
return std::nullopt;
}
// GetOrCreateDeviceReq nested class implementation
class DeviceManager::GetOrCreateDeviceReq
: public AsynchronousContinuation<livoxProto1_getOrCreateDeviceReqCbFn>
{
public:
DeviceManager& deviceManager;
// The device we're trying to connect (holds all connection parameters)
std::shared_ptr<Device> pendingDevice;
public:
GetOrCreateDeviceReq(
DeviceManager& mgr,
std::shared_ptr<Device> device,
livoxProto1_getOrCreateDeviceReqCbFn cb)
: AsynchronousContinuation(std::move(cb)),
deviceManager(mgr), pendingDevice(device)
{}
// Public accessor for the original callback
void callOriginalCallback(bool success, std::shared_ptr<Device> device)
{ originalCbFn(success, device); }
void callOriginalCallbackWithFailure()
{ callOriginalCallback(false, nullptr); }
void getOrCreateDeviceReq1(
std::shared_ptr<GetOrCreateDeviceReq> context, bool connectSuccess
)
{
if (!connectSuccess)
{
std::cerr << __func__ << ": Connection failed for device "
<< context->pendingDevice->discoveredDevice.deviceIdentifier
<< std::endl;
context->callOriginalCallbackWithFailure();
return;
}
// Connection successful, add device to collection
context->deviceManager.devices.push_back(context->pendingDevice);
if (OptionParser::getOptions().verbose)
{
std::cout << __func__ << ": Successfully connected and added device "
<< context->pendingDevice->discoveredDevice.deviceIdentifier
<< std::endl;
}
// Return success with the connected device
context->callOriginalCallback(true, context->pendingDevice);
}
};
void DeviceManager::getOrCreateDeviceReq(
const std::string &deviceIdentifier,
const std::shared_ptr<smo::ComponentThread>& componentThread,
int handshakeTimeoutMs, int retryDelayMs,
const std::string& smoIp, uint8_t smoSubnetNbits,
uint16_t dataPort, uint16_t cmdPort, uint16_t imuPort
)
uint16_t dataPort, uint16_t cmdPort, uint16_t imuPort,
livoxProto1_getOrCreateDeviceReqCbFn callback)
{
// Validate smoIp format using Boost.Asio IPv4 validation
if (!smoIp.empty() && !comms::isValidIPv4(smoIp))
@@ -73,43 +149,98 @@ std::shared_ptr<Device> DeviceManager::getOrCreateDevice(
// First try to get existing device
auto existingDevice = getDevice(deviceIdentifier);
if (existingDevice) {
return existingDevice;
if (existingDevice)
{
// Device already exists and is connected, return it
callback(true, existingDevice.value());
return;
}
// Device doesn't exist, create a new one
// Device doesn't exist, create a new one but don't add it to collection yet
auto newDevice = std::make_shared<Device>(
deviceIdentifier, componentThread,
handshakeTimeoutMs, retryDelayMs,
smoIp, smoSubnetNbits,
dataPort, cmdPort, imuPort);
// Add to our collection
devices.push_back(newDevice);
return newDevice;
// Create the continuation request object to hold state and callbacks
auto request = std::make_shared<GetOrCreateDeviceReq>(
*this, newDevice, std::move(callback));
// Start the connection process - only add to collection on success
request->pendingDevice->connectReq(
std::bind(
&DeviceManager::GetOrCreateDeviceReq::getOrCreateDeviceReq1,
request.get(), request, std::placeholders::_1));
}
std::shared_ptr<Device> DeviceManager::getDevice(
const std::string &deviceIdentifier
)
class DeviceManager::DestroyDeviceReq
: public AsynchronousContinuation<livoxProto1_destroyDeviceReqCbFn>
{
for (auto& device : devices)
public:
DeviceManager& deviceManager;
std::shared_ptr<Device> pendingDevice;
public:
DestroyDeviceReq(
DeviceManager& mgr,
std::shared_ptr<Device> device,
livoxProto1_destroyDeviceReqCbFn cb)
: AsynchronousContinuation(std::move(cb)),
deviceManager(mgr), pendingDevice(device)
{}
// Public accessor for the original callback
void callOriginalCallback(bool success)
{ originalCbFn(success); }
void callOriginalCallbackWithFailure()
{ callOriginalCallback(false); }
void destroyDeviceReq1(
std::shared_ptr<DestroyDeviceReq> context, bool success
)
{
if (comms::deviceIdentifiersEqual(
device->discoveredDevice.deviceIdentifier, deviceIdentifier))
{
return device;
}
context->deviceManager.devices.erase(
std::remove(
context->deviceManager.devices.begin(),
context->deviceManager.devices.end(),
context->pendingDevice),
context->deviceManager.devices.end());
context->callOriginalCallback(success);
}
return nullptr;
}
};
bool DeviceManager::isDeviceKnown(const std::string& deviceIdentifier)
void DeviceManager::destroyDeviceReq(
std::shared_ptr<Device> dev,
livoxProto1_destroyDeviceReqCbFn callback
)
{
return broadcastListener.deviceExists(deviceIdentifier);
/** EXPLANATION:
* Check to see if the device is in our collection. If so, call
* disconnectReq and then remove it.
*/
std::shared_ptr<Device> device = getDevice(dev->discoveredDevice).
value_or(nullptr);
if (!device)
{
callback(false);
return;
}
auto request = std::make_shared<DestroyDeviceReq>(
*this, device, std::move(callback));
device->disconnectReq(
std::bind(
&DeviceManager::DestroyDeviceReq::destroyDeviceReq1,
request.get(), request, std::placeholders::_1));
}
void main(const std::shared_ptr<smo::ComponentThread> &componentThread)
void main(const std::shared_ptr<smo::ComponentThread> &componentThread,
const smo::sense_api::SmoCallbacks& smoCallbacks)
{
if (protoState.isInitialized) {
return;
@@ -117,6 +248,7 @@ void main(const std::shared_ptr<smo::ComponentThread> &componentThread)
protoState.isInitialized = true;
protoState.componentThread = componentThread;
protoState.smoCallbacks = smoCallbacks;
protoState.deviceManager = std::make_unique<DeviceManager>();
protoState.deviceManager->broadcastListener.start();
}