Files
salmanoff/smocore/senseApis/senseApiManager.cpp
T

432 lines
12 KiB
C++
Raw Normal View History

#include <iostream>
#include <stdexcept>
#include <optional>
#include <filesystem>
#include <senseApis/senseApiManager.h>
#include <senseApis/senseApiLib.h>
#include <opts.h>
#include <asynchronousBridge.h>
#include <asynchronousLoop.h>
#include <user/senseApiDesc.h>
#include <deviceManager/deviceManager.h>
namespace fs = std::filesystem;
2025-07-22 06:48:04 -04:00
namespace smo {
namespace sense_api {
/**
* @brief Searches for a library in predefined locations
* @param libraryPath The name or path of the library to find
* @return Optional containing the full path if found in search paths, nullopt
* if not
*
* Searches for the library in the following locations in order:
* 1. Custom path specified by --sense-api-lib-path option (if provided)
* 2. Current working directory
* 3. Directory containing the executable
*
* If the library is not found in any of these locations, returns nullopt and
* falls back to system default library search paths (LD_LIBRARY_PATH, etc.)
*/
static std::optional<std::string> searchForLibInSmoSearchPaths(
const std::string& libraryPath)
{
std::vector<std::string> searchPaths = {
fs::current_path().string(),
fs::path("/proc/self/exe").parent_path().string()
};
const auto& options = OptionParser::getOptions();
if (!options.senseApiLibPath.empty())
{
// Insert all sense API library paths at the beginning of search paths
searchPaths.insert(
searchPaths.begin(),
options.senseApiLibPath.begin(),
options.senseApiLibPath.end());
}
for (const auto& path : searchPaths)
{
fs::path fullPath = fs::path(path) / libraryPath;
if (fs::exists(fullPath))
{
return fullPath.string();
}
}
std::cerr << std::string(__func__) + ": library '"
+ libraryPath + "' isn't in search bespoke search paths: ";
for (const auto& path : searchPaths) {
std::cerr << path << " ";
}
std::cerr << std::endl;
std::cerr << "Trying to load " + libraryPath + " from system default "
"search paths\n";
return std::nullopt;
}
/* Local static function to wrap ComponentThread::getSelf for SmoCallbacks */
static std::shared_ptr<ComponentThread> ComponentThread_getSelf()
{
return ComponentThread::getSelf();
}
2025-07-24 06:00:35 -04:00
/* Hooks to be provided to senseApiLibs, enabling them to call into Salmanoff
* code.
*/
static SmoCallbacks smoCallbacks =
2025-07-24 06:00:35 -04:00
{
.searchForLibInSmoSearchPaths = searchForLibInSmoSearchPaths,
.ComponentThread_getSelf = ComponentThread_getSelf
2025-07-24 06:00:35 -04:00
};
/* Static file-scope threading model object for senseApi libraries */
static SmoThreadingModelDesc smoThreadingModelDesc = {
.componentThread = nullptr
};
std::optional<std::string> SenseApiManager::searchForLibInSmoSearchPaths(
const std::string& libraryPath)
{
return ::smo::sense_api::searchForLibInSmoSearchPaths(libraryPath);
}
SenseApiLib& SenseApiManager::loadSenseApiLib(
const std::string& libraryPath,
std::shared_ptr<ComponentThread>& componentThread
)
{
std::optional<std::string> fullPath = searchForLibInSmoSearchPaths(
libraryPath);
std::string resolvedPath = fullPath.value_or(libraryPath);
// Clear any existing error
dlerror();
2025-01-12 09:44:08 -04:00
auto dlopen_handle = std::unique_ptr<void, SenseApiLib::DlCloser>(
dlopen(resolvedPath.c_str(), RTLD_LAZY));
if (!dlopen_handle && fullPath.has_value())
{
// Fallback to using the supplied libraryPath
dlerror();
dlopen_handle.reset(dlopen(libraryPath.c_str(), RTLD_LAZY));
}
if (!dlopen_handle)
{
2025-01-08 15:08:51 -04:00
const char *dlerr = dlerror();
std::string error = (dlerr
? dlerr
: "Unknown error while opening shlib");
throw std::runtime_error(
2025-01-08 15:08:51 -04:00
std::string(__func__) + ": Cannot load library '"
+ libraryPath + "': "
+ error);
}
// Initialize getSenseApiDescriptor
2025-07-22 06:48:04 -04:00
auto func = reinterpret_cast<SMO_GET_SENSE_API_DESC_FN_TYPEDEF *>(
dlsym(dlopen_handle.get(), SMO_GET_SENSE_API_DESC_FN_NAME_STR));
if (!func)
{
throw std::runtime_error(
2025-01-08 15:09:18 -04:00
std::string(__func__) + ": dlsym('"
2025-07-22 06:48:04 -04:00
SMO_GET_SENSE_API_DESC_FN_NAME_STR "') failed for library '"
+ libraryPath + "'");
}
// Check if the static threading model obj is null and initialize if needed
if (!smoThreadingModelDesc.componentThread) {
smoThreadingModelDesc.componentThread = componentThread;
}
const SenseApiDesc &libApiDesc = func(
smoCallbacks, smoThreadingModelDesc);
auto lib = std::make_shared<SenseApiLib>(
libraryPath, dlopen_handle.release(), func);
lib->setSenseApiDesc(libApiDesc);
senseApiLibs.push_back(lib);
return *senseApiLibs.back();
}
std::optional<std::shared_ptr<SenseApiLib>>
SenseApiManager::getSenseApiLib(const std::string& libraryPath)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&libPath = libraryPath](const std::shared_ptr<SenseApiLib>& lib) {
return lib->libraryPath == libPath;
}
);
if (it != senseApiLibs.end()) { return *it; }
return std::nullopt;
}
std::optional<std::shared_ptr<SenseApiLib>>
SenseApiManager::getSenseApiLibByApiName(const std::string& apiName)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&apiName](const std::shared_ptr<SenseApiLib>& lib) {
return lib->senseApiDesc.name == apiName;
}
);
if (it != senseApiLibs.end()) { return *it; }
return std::nullopt;
}
void SenseApiManager::unloadSenseApiLib(const std::string& libraryPath)
{
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
[&lpath = libraryPath](const std::shared_ptr<SenseApiLib>& lib) {
return lib->libraryPath == lpath;
}
);
if (it != senseApiLibs.end())
{
senseApiLibs.erase(it);
return;
}
std::cerr << std::string(__func__) + ": Library not found: "
<< libraryPath << '\n';
}
void SenseApiManager::unloadAllSenseApiLibs(void)
{
senseApiLibs.clear();
}
void SenseApiManager::loadAllSenseApiLibsFromOptions(
std::shared_ptr<ComponentThread>& componentThread
)
{
const auto& options = OptionParser::getOptions();
for (const auto& libPath : options.senseApiLibs) {
loadSenseApiLib(libPath, componentThread);
}
}
std::string SenseApiManager::stringifyLibs() const
{
std::string result;
for (const auto& lib : senseApiLibs) {
result += lib->stringify() + "\n";
}
return result;
}
void SenseApiManager::initializeSenseApiLib(SenseApiLib& lib)
{
if (!lib.senseApiDesc.sal_mgmt_libOps.initializeInd)
{
throw std::runtime_error(
std::string(__func__) + ": initializeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.initializeInd();
}
void SenseApiManager::finalizeSenseApiLib(SenseApiLib& lib)
{
if (!lib.senseApiDesc.sal_mgmt_libOps.finalizeInd)
{
throw std::runtime_error(
std::string(__func__) + ": finalizeInd() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.finalizeInd();
}
void SenseApiManager::initializeAllSenseApiLibs(void)
{
for (auto& lib : senseApiLibs) {
initializeSenseApiLib(*lib);
}
}
void SenseApiManager::finalizeAllSenseApiLibs(void)
{
for (auto& lib : senseApiLibs) {
finalizeSenseApiLib(*lib);
}
}
void SenseApiManager::attachSenseDeviceReq(
const std::shared_ptr<device::DeviceAttachmentSpec>& spec,
attachSenseDeviceReqCbFn cb
2025-08-29 09:50:26 -04:00
)
{
/** FIXME:
* We should acquire a spinlock here to ensure that the device isn't added
* in the interim while the async op executes.
*/
auto libOpt = getSenseApiLibByApiName(spec->api);
if (!libOpt)
{
throw std::runtime_error(
std::string(__func__) + ": No library found for API '"
2025-08-29 09:50:26 -04:00
+ spec->api + "'");
}
auto& lib = *libOpt.value();
if (!lib.senseApiDesc.sal_mgmt_libOps.attachDeviceReq)
{
throw std::runtime_error(
std::string(__func__) + ": attachDeviceReq() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.attachDeviceReq(spec, cb);
}
void SenseApiManager::detachSenseDeviceReq(
const std::shared_ptr<device::DeviceAttachmentSpec>& spec,
detachSenseDeviceReqCbFn cb
2025-08-29 09:50:26 -04:00
)
{
/** FIXME:
* We should acquire a spinlock here to ensure that the device isn't removed
* in the interim while the async op executes.
*/
2025-08-29 09:50:26 -04:00
auto libOpt = getSenseApiLibByApiName(spec->api);
if (!libOpt)
{
throw std::runtime_error(
std::string(__func__) + ": No library found for API '"
2025-08-29 09:50:26 -04:00
+ spec->api + "'");
}
auto& lib = *libOpt.value();
if (!lib.senseApiDesc.sal_mgmt_libOps.detachDeviceReq)
{
throw std::runtime_error(
std::string(__func__) + ": detachDeviceReq() is NULL for library '"
+ lib.libraryPath + "'");
}
lib.senseApiDesc.sal_mgmt_libOps.detachDeviceReq(spec, cb);
}
void SenseApiManager::attachAllSenseDevicesFromSpecs(void)
{
auto self = ComponentThread::getSelf();
AsynchronousBridge bridge(self->getIoService());
AsynchronousLoop loop(device::DeviceManager::deviceAttachmentSpecs.size());
for (const auto& spec : device::DeviceManager::deviceAttachmentSpecs)
{
try {
attachSenseDeviceReq(spec,
[spec, &loop, &bridge](bool success) -> void
{
if (!success)
{
std::cerr << __func__ << ": Failed to attach device: "
<< spec->deviceIdentifier << "\n";
}
if (!loop.incrementSuccessOrFailureAndTestForCompletionDueTo(
success))
{
return;
}
std::cout << __func__ << ": " << loop.nSucceeded.load()
<< " devices attached, "
<< loop.nFailed.load() << " devices failed\n";
bridge.setAsyncOperationComplete();
});
} catch (const std::exception& e) {
std::cerr << __func__ << ": Exception: " << e.what() << "\n";
if (loop.incrementSuccessOrFailureAndTestForCompletionDueTo(false))
{ bridge.setAsyncOperationComplete(); }
}
}
/* Bridge the async op here. */
bridge.waitForAsyncOperationCompleteOrIoServiceStopped();
if (bridge.exitedBecauseIoServiceStopped())
{
/* Return early because the io_service is stopped. */
return;
}
if (!loop.isComplete())
{
throw std::runtime_error(
std::string(__func__) + ": Failed to get through all devices");
}
std::cout << __func__ << ": " << loop.nSucceeded.load() << "/"
<< loop.nTotal.load() << " devices attached, "
<< loop.nFailed.load() << "/" << loop.nTotal.load()
<< " devices failed\n";
}
void SenseApiManager::detachAllSenseDevicesReq(void)
{
auto self = ComponentThread::getSelf();
AsynchronousBridge bridge(self->getIoService());
AsynchronousLoop loop(device::DeviceManager::deviceAttachmentSpecs.size());
for (const auto& spec : device::DeviceManager::deviceAttachmentSpecs)
{
try {
detachSenseDeviceReq(spec,
[spec, &loop, &bridge](bool success) -> void
{
if (!success)
{
std::cerr << __func__ << ": Failed to detach device: "
<< spec->deviceIdentifier << "\n";
}
if (!loop.incrementSuccessOrFailureAndTestForCompletionDueTo(
success))
{
return;
}
std::cout << __func__ << ": " << loop.nSucceeded.load()
<< " devices detached, "
<< loop.nFailed.load() << " devices failed\n";
bridge.setAsyncOperationComplete();
});
} catch (const std::exception& e) {
std::cerr << __func__ << ": Exception: " << e.what() << "\n";
if (loop.incrementSuccessOrFailureAndTestForCompletionDueTo(false))
{ bridge.setAsyncOperationComplete(); }
}
}
/* Bridge the async op here. */
bridge.waitForAsyncOperationCompleteOrIoServiceStopped();
if (bridge.exitedBecauseIoServiceStopped())
{
/* Return early because the io_service is stopped. */
return;
}
if (!loop.isComplete())
{
throw std::runtime_error(
std::string(__func__) + ": Failed to get through all devices");
}
std::cout << __func__ << ": " << loop.nSucceeded.load() << "/"
<< loop.nTotal.load() << " devices detached, "
<< loop.nFailed.load() << "/" << loop.nTotal.load()
<< " devices failed\n";
}
} // namespace sense_api
2025-07-22 06:48:04 -04:00
} // namespace smo