2026-05-24 16:12:29 -04:00
|
|
|
#include <algorithm>
|
2025-01-05 14:19:53 -04:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <sstream>
|
2025-01-08 06:23:34 -04:00
|
|
|
#include <memory>
|
2025-01-05 14:19:53 -04:00
|
|
|
#include <opts.h>
|
2025-09-27 23:16:46 -04:00
|
|
|
#include <componentThread.h>
|
2025-01-05 14:19:53 -04:00
|
|
|
#include <deviceManager/deviceManager.h>
|
2025-09-29 01:07:32 -04:00
|
|
|
#include <deviceManager/deviceReattacher.h>
|
2025-10-01 18:47:42 -04:00
|
|
|
#include <stimBuffApis/stimBuffApiManager.h>
|
2026-06-10 21:14:55 -04:00
|
|
|
#include <loadableLib/loadableLibraryManager.h>
|
2026-02-22 17:46:27 -04:00
|
|
|
#include <marionette/marionette.h>
|
2026-05-24 16:12:29 -04:00
|
|
|
#include <marionette/marionetteThread.h>
|
2025-09-27 23:16:46 -04:00
|
|
|
#include <mind.h>
|
2026-05-31 07:13:53 -04:00
|
|
|
#include <spinscale/co/postTarget.h>
|
2026-05-24 16:12:29 -04:00
|
|
|
#include <spinscale/co/group.h>
|
2026-06-07 19:37:50 -04:00
|
|
|
#include <spinscale/asynchronousLoop.h>
|
2025-01-05 14:19:53 -04:00
|
|
|
|
2025-07-22 06:48:04 -04:00
|
|
|
namespace smo {
|
2025-01-13 21:57:11 -04:00
|
|
|
namespace device {
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
void assertMarionetteThread()
|
|
|
|
|
{
|
|
|
|
|
auto self = sscl::ComponentThread::getSelf();
|
|
|
|
|
if (self->id != SmoThreadId::MRNTT)
|
|
|
|
|
{
|
|
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__)
|
|
|
|
|
+ ": Must be executed on Marionette thread");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 07:13:53 -04:00
|
|
|
std::shared_ptr<sscl::ComponentThread> threadForDeviceOp(
|
|
|
|
|
const DeviceAttachmentSpec& spec)
|
|
|
|
|
{
|
|
|
|
|
if (spec.sensorType == 'e') {
|
|
|
|
|
return mind::globalMind->world.thread;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mind::globalMind->body.thread;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
void throwIfStimBuffAttachFailed(
|
|
|
|
|
const stim_buff::StimBuffDeviceOpResult &result,
|
|
|
|
|
const char *callerFn)
|
|
|
|
|
{
|
|
|
|
|
if (result.success) { return; }
|
|
|
|
|
|
|
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(callerFn) + ": attach failed for "
|
|
|
|
|
+ result.deviceSpec->stringify());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void throwIfStimBuffDetachFailed(
|
|
|
|
|
const stim_buff::StimBuffDeviceOpResult &result,
|
|
|
|
|
const char *callerFn)
|
|
|
|
|
{
|
|
|
|
|
if (result.success) { return; }
|
|
|
|
|
|
|
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(callerFn) + ": detach failed for "
|
|
|
|
|
+ result.deviceSpec->stringify());
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
} // namespace
|
2025-08-29 17:42:13 -04:00
|
|
|
|
2025-09-29 01:07:32 -04:00
|
|
|
DeviceManager::~DeviceManager()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-15 14:33:42 -04:00
|
|
|
const std::string DeviceManager::stringifyDeviceSpecs(void)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
for (const auto& spec : getInstance().s.rsrc.deviceAttachmentSpecs) {
|
2026-06-11 11:17:06 -04:00
|
|
|
oss << "Device Attachment Spec: " << spec->stringify() << "\n";
|
2025-09-15 14:33:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return oss.str();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<void>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::attachStimBuffDeviceCReq(
|
|
|
|
|
const std::shared_ptr<DeviceAttachmentSpec>& spec)
|
2025-09-15 14:20:08 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
2025-09-15 14:20:08 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
auto &sbam = stim_buff::StimBuffApiManager::getInstance();
|
2026-06-07 19:37:50 -04:00
|
|
|
auto &lib = sbam.getStimBuffApiLibByApiName(spec->stimBuffApi);
|
2025-09-30 22:01:34 -04:00
|
|
|
|
2026-06-10 21:14:55 -04:00
|
|
|
if (lib.loadedSharedLibrary->isBeingDestroyed.load())
|
2026-05-24 16:12:29 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__) + ": Library is being destroyed"
|
|
|
|
|
+ " for API '" + spec->stimBuffApi + "'");
|
2025-09-15 14:20:08 -04:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 08:21:46 -04:00
|
|
|
if (!lib.stimBuffApiDesc.sal_mgmt_libOps.attachDeviceCReq)
|
2025-09-15 14:20:08 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__) + ": attachDeviceCReq() is NULL "
|
2026-06-10 21:14:55 -04:00
|
|
|
"for library '" + lib.loadedSharedLibrary->libraryPath + "'");
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-15 14:20:08 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
/* FIXME Locking here makes no sense. */
|
2026-05-24 16:12:29 -04:00
|
|
|
sscl::co::CoQutex::ReleaseHandle sbamGuard =
|
2026-06-10 21:14:55 -04:00
|
|
|
co_await sbam.s.lock
|
|
|
|
|
.getAcquireInvocationAndSuspensionPolicy();
|
2026-05-24 16:12:29 -04:00
|
|
|
sscl::co::CoQutex::ReleaseHandle libGuard =
|
|
|
|
|
co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
|
|
|
|
sbamGuard.release();
|
|
|
|
|
|
2026-05-31 07:13:53 -04:00
|
|
|
/** EXPLANATION:
|
|
|
|
|
* We pass in either the body or world thread here, depending on whether
|
|
|
|
|
* the device is an introspector (idev) or extrospector (edev).
|
|
|
|
|
*
|
|
|
|
|
* Introspectors are attached to the body thread; extrospectors are
|
|
|
|
|
* attached to the world thread.
|
|
|
|
|
*/
|
|
|
|
|
std::shared_ptr<sscl::ComponentThread> targetThread =
|
|
|
|
|
threadForDeviceOp(*spec);
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
if (spec->sensorType == 'e')
|
|
|
|
|
{
|
|
|
|
|
std::cout << __func__ << ": Attaching edev "
|
|
|
|
|
<< spec->deviceIdentifier << " to world thread" << "\n";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::cout << __func__ << ": Attaching non-edev "
|
|
|
|
|
<< spec->deviceIdentifier << " to body thread" << "\n";
|
2025-09-12 16:09:26 -04:00
|
|
|
}
|
2025-01-07 14:14:24 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
stim_buff::StimBuffDeviceOpResult result =
|
|
|
|
|
co_await lib.stimBuffApiDesc.sal_mgmt_libOps.attachDeviceCReq(
|
|
|
|
|
sscl::co::ExplicitPostTarget{targetThread->getIoContext()},
|
|
|
|
|
spec, targetThread);
|
|
|
|
|
throwIfStimBuffAttachFailed(result, __func__);
|
|
|
|
|
co_return;
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<void>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::detachStimBuffDeviceCReq(
|
|
|
|
|
const std::shared_ptr<DeviceAttachmentSpec>& spec)
|
2025-09-28 11:41:20 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
2025-09-30 22:10:39 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
auto &sbam = stim_buff::StimBuffApiManager::getInstance();
|
2026-06-07 19:37:50 -04:00
|
|
|
auto &lib = sbam.getStimBuffApiLibByApiName(spec->stimBuffApi);
|
2025-09-30 22:10:39 -04:00
|
|
|
|
2026-06-10 21:14:55 -04:00
|
|
|
if (lib.loadedSharedLibrary->isBeingDestroyed.load())
|
2026-05-24 16:12:29 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__) + ": Library is being destroyed"
|
|
|
|
|
+ " for API '" + spec->stimBuffApi + "'");
|
2025-09-28 11:41:20 -04:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 08:21:46 -04:00
|
|
|
if (!lib.stimBuffApiDesc.sal_mgmt_libOps.detachDeviceCReq)
|
2025-09-28 11:41:20 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__) + ": detachDeviceCReq() is NULL "
|
2026-06-10 21:14:55 -04:00
|
|
|
"for library '" + lib.loadedSharedLibrary->libraryPath + "'");
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-28 11:41:20 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
/* FIXME Locking here makes no sense. */
|
2026-05-24 16:12:29 -04:00
|
|
|
sscl::co::CoQutex::ReleaseHandle sbamGuard =
|
2026-06-10 21:14:55 -04:00
|
|
|
co_await sbam.s.lock
|
|
|
|
|
.getAcquireInvocationAndSuspensionPolicy();
|
2026-05-24 16:12:29 -04:00
|
|
|
sscl::co::CoQutex::ReleaseHandle libGuard =
|
|
|
|
|
co_await lib.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
|
|
|
|
sbamGuard.release();
|
2025-09-28 11:41:20 -04:00
|
|
|
|
2026-05-31 07:13:53 -04:00
|
|
|
std::shared_ptr<sscl::ComponentThread> targetThread =
|
|
|
|
|
threadForDeviceOp(*spec);
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
stim_buff::StimBuffDeviceOpResult result =
|
|
|
|
|
co_await lib.stimBuffApiDesc.sal_mgmt_libOps.detachDeviceCReq(
|
|
|
|
|
sscl::co::ExplicitPostTarget{targetThread->getIoContext()},
|
|
|
|
|
spec);
|
|
|
|
|
throwIfStimBuffDetachFailed(result, __func__);
|
|
|
|
|
co_return;
|
2025-08-29 17:42:13 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<std::shared_ptr<DeviceRole>>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::newDeviceAttachmentSpecIndCReq(
|
|
|
|
|
const DeviceAttachmentSpec &spec)
|
2025-09-28 11:41:20 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
2025-09-28 11:41:20 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager &dm = getInstance();
|
|
|
|
|
sscl::co::CoQutex::ReleaseHandle dmGuard =
|
|
|
|
|
co_await dm.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
|
|
|
|
|
|
|
|
|
// First, add the spec to deviceAttachmentSpecs if it's not already there
|
|
|
|
|
std::shared_ptr<DeviceAttachmentSpec> specPtr;
|
|
|
|
|
bool specExists = false;
|
|
|
|
|
for (const auto& existingSpec : dm.s.rsrc.deviceAttachmentSpecs)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
if (*existingSpec == spec)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
specExists = true;
|
|
|
|
|
specPtr = existingSpec;
|
|
|
|
|
break;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
if (!specExists)
|
|
|
|
|
{
|
|
|
|
|
specPtr = std::make_shared<DeviceAttachmentSpec>(spec);
|
|
|
|
|
dm.s.rsrc.deviceAttachmentSpecs.push_back(specPtr);
|
|
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
std::shared_ptr<Device> device;
|
|
|
|
|
bool deviceExists = false;
|
|
|
|
|
for (const auto& existingDevice : dm.s.rsrc.devices)
|
|
|
|
|
{
|
|
|
|
|
if (existingDevice->deviceIdentifier != spec.deviceIdentifier)
|
|
|
|
|
{ continue; }
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
device = existingDevice;
|
|
|
|
|
deviceExists = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
// If device doesn't exist, create a new one and add it
|
|
|
|
|
if (!device)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
device = std::make_shared<Device>(spec.deviceIdentifier);
|
|
|
|
|
dm.s.rsrc.devices.push_back(device);
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
// Check if a DeviceRole w/ this spec already exists in attachedDeviceRoles
|
|
|
|
|
std::shared_ptr<DeviceRole> existingDeviceRole;
|
|
|
|
|
bool deviceRoleExists = false;
|
|
|
|
|
for (const auto& role : dm.s.rsrc.attachedDeviceRoles)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
if (*role->deviceAttachmentSpec == spec)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
deviceRoleExists = true;
|
|
|
|
|
existingDeviceRole = role;
|
|
|
|
|
break;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-30 20:48:45 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
// If DeviceRole exists, both spec and device must also exist
|
|
|
|
|
if (deviceRoleExists)
|
|
|
|
|
{
|
|
|
|
|
if (!specExists || !deviceExists)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
throw std::runtime_error(
|
|
|
|
|
"Program error: DeviceRole exists but spec or device doesn't "
|
|
|
|
|
"pre-exist. specExists=" + std::to_string(specExists) +
|
|
|
|
|
", deviceExists=" + std::to_string(deviceExists));
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2025-09-30 20:29:39 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
co_return existingDeviceRole;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
dmGuard.release();
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
/* FIXME:
|
|
|
|
|
* We should add an unlocked flag to at/detachStimBuffDeviceCReq()
|
|
|
|
|
* so we can call it with the devmgr lock held.
|
|
|
|
|
*/
|
|
|
|
|
co_await attachStimBuffDeviceCReq(specPtr);
|
2025-09-30 18:12:20 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
sscl::co::CoQutex::ReleaseHandle dmGuardAfterAttach =
|
|
|
|
|
co_await dm.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
|
|
|
|
(void)dmGuardAfterAttach;
|
2025-09-30 18:12:20 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
auto deviceRole = std::make_shared<DeviceRole>(*device, specPtr);
|
|
|
|
|
device->deviceRoles.push_back(deviceRole);
|
|
|
|
|
dm.s.rsrc.attachedDeviceRoles.push_back(deviceRole);
|
|
|
|
|
|
|
|
|
|
co_return deviceRole;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<void>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::removeDeviceAttachmentSpecCReq(
|
|
|
|
|
const DeviceAttachmentSpec &spec)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
2025-09-30 18:12:20 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager &dm = getInstance();
|
|
|
|
|
sscl::co::CoQutex::ReleaseHandle dmGuard =
|
|
|
|
|
co_await dm.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
2025-09-30 18:12:20 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
// Find the shared_ptr to the spec in the collection
|
|
|
|
|
std::shared_ptr<DeviceAttachmentSpec> specPtr;
|
|
|
|
|
for (const auto& existingSpec : dm.s.rsrc.deviceAttachmentSpecs)
|
2025-09-30 18:12:20 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
if (*existingSpec == spec)
|
|
|
|
|
{
|
|
|
|
|
specPtr = existingSpec;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-09-30 18:12:20 -04:00
|
|
|
}
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
if (!specPtr)
|
|
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__) + ": Device attachment spec not found: "
|
|
|
|
|
+ spec.stringify());
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
dmGuard.release();
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
/* FIXME:
|
|
|
|
|
* We should add an unlocked flag to at/detachStimBuffDeviceCReq()
|
|
|
|
|
* so we can call it with the devmgr lock held.
|
|
|
|
|
*/
|
|
|
|
|
co_await detachStimBuffDeviceCReq(specPtr);
|
2026-05-24 16:12:29 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
sscl::co::CoQutex::ReleaseHandle dmGuardAfterDetach =
|
|
|
|
|
co_await dm.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
|
|
|
|
(void)dmGuardAfterDetach;
|
|
|
|
|
|
|
|
|
|
// Find the DeviceRole in attachedDeviceRoles
|
|
|
|
|
auto deviceRoleIt = std::find_if(
|
|
|
|
|
dm.s.rsrc.attachedDeviceRoles.begin(),
|
|
|
|
|
dm.s.rsrc.attachedDeviceRoles.end(),
|
|
|
|
|
[&specPtr](const std::shared_ptr<DeviceRole> &role) {
|
|
|
|
|
return *role->deviceAttachmentSpec == *specPtr;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2026-06-07 19:37:50 -04:00
|
|
|
);
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
if (deviceRoleIt == dm.s.rsrc.attachedDeviceRoles.end())
|
|
|
|
|
{
|
|
|
|
|
throw std::runtime_error(
|
|
|
|
|
std::string(__func__) + ": DeviceRole not found for spec (race "
|
|
|
|
|
"condition)?: "
|
|
|
|
|
+ specPtr->stringify() + ", deviceRoles="
|
|
|
|
|
+ std::to_string(dm.s.rsrc.attachedDeviceRoles.size()));
|
|
|
|
|
}
|
2026-05-24 16:12:29 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
auto deviceRole = *deviceRoleIt;
|
|
|
|
|
auto& device = deviceRole->parentDevice;
|
2026-05-24 16:12:29 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
// Remove DeviceRole from DeviceManager's collection
|
|
|
|
|
dm.s.rsrc.attachedDeviceRoles.erase(deviceRoleIt);
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
// Remove DeviceRole from Device's collection
|
|
|
|
|
auto deviceRoleIt2 = std::find(
|
|
|
|
|
device.deviceRoles.begin(),
|
|
|
|
|
device.deviceRoles.end(),
|
|
|
|
|
deviceRole);
|
|
|
|
|
if (deviceRoleIt2 != device.deviceRoles.end())
|
|
|
|
|
{
|
|
|
|
|
device.deviceRoles.erase(deviceRoleIt2);
|
|
|
|
|
}
|
2026-05-24 16:12:29 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
// Remove DeviceAttachmentSpec from deviceAttachmentSpecs collection
|
|
|
|
|
auto specIt = std::find_if(
|
|
|
|
|
dm.s.rsrc.deviceAttachmentSpecs.begin(),
|
|
|
|
|
dm.s.rsrc.deviceAttachmentSpecs.end(),
|
|
|
|
|
[&specPtr](
|
|
|
|
|
const std::shared_ptr<DeviceAttachmentSpec> &existingSpec)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
return *existingSpec == *specPtr;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2026-06-07 19:37:50 -04:00
|
|
|
);
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
if (specIt != dm.s.rsrc.deviceAttachmentSpecs.end())
|
|
|
|
|
{
|
|
|
|
|
dm.s.rsrc.deviceAttachmentSpecs.erase(specIt);
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2026-06-07 19:37:50 -04:00
|
|
|
|
|
|
|
|
co_return;
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<sscl::MultiOperationResultSetWithException>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::attachAllUnattachedDevicesFromCReq(
|
|
|
|
|
const std::shared_ptr<std::vector<DeviceAttachmentSpec>> &specs)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
const unsigned int nTotal = static_cast<unsigned int>(specs->size());
|
|
|
|
|
if (nTotal == 0) {
|
|
|
|
|
co_return sscl::MultiOperationResultSetWithException{};
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sscl::co::Group group;
|
|
|
|
|
std::vector<
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<std::shared_ptr<DeviceRole>>>
|
2026-05-24 16:12:29 -04:00
|
|
|
invokers;
|
2026-06-07 19:37:50 -04:00
|
|
|
invokers.reserve(nTotal);
|
2026-05-24 16:12:29 -04:00
|
|
|
|
|
|
|
|
for (const auto &spec : *specs)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
invokers.emplace_back(newDeviceAttachmentSpecIndCReq(spec));
|
|
|
|
|
group.add(invokers.back());
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
const std::vector<sscl::co::Group::SettlementDescriptor> &settlements =
|
|
|
|
|
co_await group.getAwaitAllSettlementsInvoker();
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
unsigned int nSucceeded = 0;
|
|
|
|
|
unsigned int nFailed = 0;
|
2026-06-07 19:37:50 -04:00
|
|
|
using SettlementType = sscl::co::Group::SettlementDescriptor::TypeE;
|
|
|
|
|
for (const auto &desc : settlements)
|
2026-05-24 16:12:29 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
if (desc.type == SettlementType::EXCEPTION_THROWN) {
|
2026-05-24 16:12:29 -04:00
|
|
|
nFailed++;
|
2026-06-07 19:37:50 -04:00
|
|
|
} else {
|
|
|
|
|
nSucceeded++;
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
std::exception_ptr memberFailureException = nullptr;
|
|
|
|
|
if (nFailed > 0) {
|
|
|
|
|
memberFailureException = group.captureAggregatedGroupExceptions();
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
co_return sscl::MultiOperationResultSetWithException(
|
|
|
|
|
sscl::MultiOperationResultSet(nTotal, nSucceeded, nFailed),
|
|
|
|
|
memberFailureException);
|
2025-09-28 12:52:59 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<void>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::attachAllUnattachedDevicesFromKnownListCReq()
|
2025-09-28 23:15:22 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
|
|
|
|
|
|
|
|
|
DeviceManager &dm = getInstance();
|
|
|
|
|
sscl::co::CoQutex::ReleaseHandle dmGuard =
|
|
|
|
|
co_await dm.s.lock.getAcquireInvocationAndSuspensionPolicy();
|
|
|
|
|
|
|
|
|
|
auto unattachedSpecs = std::make_shared<
|
|
|
|
|
std::vector<DeviceAttachmentSpec>>();
|
|
|
|
|
|
|
|
|
|
for (const auto& spec : dm.s.rsrc.deviceAttachmentSpecs)
|
2025-09-28 23:15:22 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
bool isAttached = false;
|
2025-09-28 23:15:22 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
for (const auto& role : dm.s.rsrc.attachedDeviceRoles)
|
2025-09-28 23:15:22 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
if (*role->deviceAttachmentSpec == *spec)
|
2025-09-28 23:15:22 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
isAttached = true;
|
|
|
|
|
break;
|
2025-09-30 21:24:51 -04:00
|
|
|
}
|
2025-09-28 23:15:22 -04:00
|
|
|
}
|
2025-09-30 21:24:51 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
if (!isAttached) {
|
|
|
|
|
unattachedSpecs->push_back(*spec);
|
|
|
|
|
}
|
2025-09-30 21:24:51 -04:00
|
|
|
}
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
dmGuard.release();
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
const sscl::MultiOperationResultSetWithException batchResult =
|
|
|
|
|
co_await attachAllUnattachedDevicesFromCReq(unattachedSpecs);
|
|
|
|
|
|
|
|
|
|
if (batchResult.results.nSucceeded > 0)
|
|
|
|
|
{
|
|
|
|
|
std::cout << "DeviceReattacher: Successfully reattached "
|
|
|
|
|
<< batchResult.results.nSucceeded << " of "
|
|
|
|
|
<< batchResult.results.nTotal
|
|
|
|
|
<< " devices" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (batchResult.hasMemberFailure())
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
std::rethrow_exception(batchResult.memberFailureException);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
std::cerr << __func__ << ": " << e.what() << std::endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
co_return;
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-30 21:24:51 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<void>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::attachAllUnattachedDevicesFromCmdlineCReq()
|
2025-09-30 21:24:51 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
auto specs = std::make_shared<std::vector<DeviceAttachmentSpec>>(
|
|
|
|
|
getInstance().s.rsrc.commandLineDASpecs);
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
const sscl::MultiOperationResultSetWithException batchResult =
|
|
|
|
|
co_await attachAllUnattachedDevicesFromCReq(specs);
|
|
|
|
|
|
|
|
|
|
std::cout << "Mrntt: attached "
|
|
|
|
|
<< batchResult.results.nSucceeded << " of "
|
|
|
|
|
<< batchResult.results.nTotal
|
|
|
|
|
<< " sense devices." << "\n";
|
|
|
|
|
|
|
|
|
|
if (batchResult.results.nTotal > 0
|
|
|
|
|
&& batchResult.results.nSucceeded == 0)
|
|
|
|
|
{
|
|
|
|
|
std::string message =
|
|
|
|
|
std::string(__func__)
|
|
|
|
|
+ ": Startup policy requires at least one cmdline sense device "
|
|
|
|
|
"to attach successfully; 0 of "
|
|
|
|
|
+ std::to_string(batchResult.results.nTotal)
|
|
|
|
|
+ " requested sense devices attached — aborting startup.";
|
|
|
|
|
|
|
|
|
|
if (batchResult.hasMemberFailure())
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
std::rethrow_exception(batchResult.memberFailureException);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
message += "\n";
|
|
|
|
|
message += e.what();
|
|
|
|
|
} catch (...) {
|
|
|
|
|
message += "\n<unknown exception type>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw std::runtime_error(message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
co_return;
|
2025-09-28 23:15:22 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
mrntt::MrnttViralPostingInvoker<void>
|
2026-05-24 16:12:29 -04:00
|
|
|
DeviceManager::detachAllAttachedDeviceRolesCReq()
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-05-24 16:12:29 -04:00
|
|
|
assertMarionetteThread();
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
std::vector<std::shared_ptr<DeviceAttachmentSpec>> specsToDetach;
|
|
|
|
|
specsToDetach.reserve(getInstance().s.rsrc.attachedDeviceRoles.size());
|
2026-06-07 19:37:50 -04:00
|
|
|
for (const auto& deviceRole : getInstance().s.rsrc.attachedDeviceRoles) {
|
2026-05-24 16:12:29 -04:00
|
|
|
specsToDetach.push_back(deviceRole->deviceAttachmentSpec);
|
|
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
sscl::AsynchronousLoop loop(
|
|
|
|
|
static_cast<unsigned int>(specsToDetach.size()));
|
|
|
|
|
if (loop.nTotalIsZero()) {
|
|
|
|
|
co_return;
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
sscl::co::Group group;
|
2026-06-07 19:37:50 -04:00
|
|
|
std::vector<mrntt::MrnttViralPostingInvoker<void>> invokers;
|
|
|
|
|
invokers.reserve(loop.nTotal);
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
for (const auto &spec : specsToDetach)
|
|
|
|
|
{
|
|
|
|
|
invokers.emplace_back(detachStimBuffDeviceCReq(spec));
|
|
|
|
|
group.add(invokers.back());
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
2025-09-28 12:39:45 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
const std::vector<sscl::co::Group::SettlementDescriptor> &settlements =
|
|
|
|
|
co_await group.getAwaitAllSettlementsInvoker();
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
using SettlementType = sscl::co::Group::SettlementDescriptor::TypeE;
|
|
|
|
|
for (const auto &desc : settlements)
|
2025-09-27 23:16:46 -04:00
|
|
|
{
|
2026-06-07 19:37:50 -04:00
|
|
|
loop.incrementSuccessOrFailureDueTo(
|
|
|
|
|
desc.type != SettlementType::EXCEPTION_THROWN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loop.nFailed.load() > 0)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
group.checkForAndReThrowGroupExceptions();
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
std::cerr << __func__ << ": " << e.what() << std::endl;
|
2026-05-24 16:12:29 -04:00
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
const unsigned int nSucceeded = loop.nSucceeded.load();
|
|
|
|
|
const unsigned int nFailed = loop.nFailed.load();
|
|
|
|
|
|
|
|
|
|
if (nFailed > 0)
|
|
|
|
|
{
|
|
|
|
|
std::cerr << "Mrntt: Failed to detach "
|
|
|
|
|
<< nFailed << " of " << loop.nTotal << " sense devices." << "\n";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::cout << "Mrntt: Successfully detached "
|
|
|
|
|
<< nSucceeded << " of " << loop.nTotal << " sense devices." << "\n";
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 16:12:29 -04:00
|
|
|
if (OptionParser::getOptions().verbose)
|
|
|
|
|
{
|
|
|
|
|
std::cout << __func__ << ": " << nSucceeded
|
|
|
|
|
<< " devices detached, "
|
|
|
|
|
<< nFailed << " devices failed\n";
|
|
|
|
|
}
|
2025-09-27 23:16:46 -04:00
|
|
|
|
2026-06-07 19:37:50 -04:00
|
|
|
co_return;
|
2025-09-27 23:16:46 -04:00
|
|
|
}
|
|
|
|
|
|
2025-09-29 01:07:32 -04:00
|
|
|
void DeviceManager::initializeDeviceReattacher()
|
|
|
|
|
{
|
|
|
|
|
deviceReattacher = std::make_unique<DeviceReattacher>(
|
2026-02-22 17:46:27 -04:00
|
|
|
*this, mrntt::mrntt.thread);
|
2025-09-29 01:07:32 -04:00
|
|
|
|
|
|
|
|
deviceReattacher->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeviceManager::finalizeDeviceReattacher()
|
|
|
|
|
{
|
2025-10-01 10:19:08 -04:00
|
|
|
if (!deviceReattacher) { return; }
|
|
|
|
|
|
|
|
|
|
deviceReattacher->stop();
|
|
|
|
|
deviceReattacher.reset();
|
2025-09-29 01:07:32 -04:00
|
|
|
}
|
|
|
|
|
|
2025-01-13 21:57:11 -04:00
|
|
|
} // namespace device
|
2025-07-22 06:48:04 -04:00
|
|
|
} // namespace smo
|