LivoxGen1: Port to coros
No longer uses CPS. We also found and documented a potential bug in the way we deal with disablePcloudData during detachDeviceReq.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# Adapter Awaiters
|
||||
|
||||
This directory contains coroutine/awaitable adapters that wrap callback-driven
|
||||
or event-driven APIs.
|
||||
|
||||
## Placement rules
|
||||
|
||||
- Put wrappers for external APIs in provider-specific subdirectories.
|
||||
- Examples: `boostAsio/`, `opencl/`, `liburing/`.
|
||||
- Put wrappers for SMO/internal APIs in `smo/`.
|
||||
- Do not place adapter awaiters in feature folders like
|
||||
`stimBuffApis/*` or `smocore/*` unless they are strictly private to one
|
||||
translation unit.
|
||||
|
||||
## Tree layout
|
||||
|
||||
```text
|
||||
include/adapters/
|
||||
README.md
|
||||
boostAsio/
|
||||
<boost asio adapter awaiters>
|
||||
opencl/
|
||||
<OpenCL adapter awaiters>
|
||||
smo/
|
||||
cpsCallbackAReq.h
|
||||
livoxProto1CpsAwaiters.h
|
||||
<other SMO/internal callback adapters>
|
||||
```
|
||||
|
||||
## Design guidelines
|
||||
|
||||
- Name adapter awaiter wrapper functions `get<fnName>AReqAwaiter()`, where
|
||||
`<fnName>` is the wrapped CPS/API request symbol with its library prefix
|
||||
removed and each `_`-delimited segment Pascal-cased (e.g.
|
||||
`livoxProto1_getOrCreateDeviceReq` → `getGetOrCreateDeviceReqAReqAwaiter()`).
|
||||
- Keep adapters small and single-purpose; but unify where possible to reduce
|
||||
code duplication.
|
||||
- Make result types explicit for multi-argument callbacks.
|
||||
- Resume coroutines on a caller-specified executor/io_service.
|
||||
- Avoid embedding business logic in adapters.
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef ADAPTERS_BOOST_ASIO_DEADLINE_TIMER_AREQ_H
|
||||
#define ADAPTERS_BOOST_ASIO_DEADLINE_TIMER_AREQ_H
|
||||
|
||||
#include <boostAsioLinkageFix.h>
|
||||
#include <functional>
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <adapters/smo/cpsCallbackAReq.h>
|
||||
|
||||
namespace adapters::boostAsio {
|
||||
|
||||
using TimerWaitCbFn = std::function<void(bool success)>;
|
||||
|
||||
inline auto deadlineTimerWaitAReq(
|
||||
boost::asio::io_service &ioService,
|
||||
const boost::posix_time::milliseconds delay)
|
||||
{
|
||||
return smo::cpsBoundary::CpsCallbackAReq<bool, TimerWaitCbFn, std::function<void(sscl::cps::Callback<TimerWaitCbFn>)>>(
|
||||
ioService,
|
||||
[&ioService, delay](sscl::cps::Callback<TimerWaitCbFn> cb)
|
||||
{
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(ioService);
|
||||
timer->expires_from_now(delay);
|
||||
timer->async_wait(
|
||||
[timer, cb](const boost::system::error_code &error) mutable
|
||||
{
|
||||
cb.callbackFn(!error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace adapters::boostAsio
|
||||
|
||||
#endif // ADAPTERS_BOOST_ASIO_DEADLINE_TIMER_AREQ_H
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef ADAPTERS_SMO_CPS_CALLBACK_AREQ_H
|
||||
#define ADAPTERS_SMO_CPS_CALLBACK_AREQ_H
|
||||
|
||||
#include <boostAsioLinkageFix.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <coroutine>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <spinscale/cps/callback.h>
|
||||
|
||||
namespace smo {
|
||||
namespace cpsBoundary {
|
||||
|
||||
/** Eager-start CPS callback -> coroutine adapter (mirrors
|
||||
* PuppetThread::ViralThreadLifetimeMgmtInvoker).
|
||||
*/
|
||||
template <typename Result, typename CallbackFn, typename StartFn>
|
||||
class CpsCallbackAReq
|
||||
{
|
||||
public:
|
||||
struct AsyncState
|
||||
{
|
||||
std::atomic<bool> settled{false};
|
||||
Result result{};
|
||||
std::coroutine_handle<> callerSchedHandle;
|
||||
};
|
||||
|
||||
CpsCallbackAReq(
|
||||
boost::asio::io_service &resumeIoService,
|
||||
StartFn startFn)
|
||||
: asyncState(std::make_shared<AsyncState>()),
|
||||
resumeIoService(resumeIoService)
|
||||
{
|
||||
startFn(sscl::cps::Callback<CallbackFn>{
|
||||
nullptr,
|
||||
[this](auto &&...args)
|
||||
{
|
||||
storeResult(std::forward<decltype(args)>(args)...);
|
||||
signalSettledAndResumeCaller();
|
||||
}});
|
||||
}
|
||||
|
||||
bool await_ready() const noexcept
|
||||
{
|
||||
return asyncState->settled.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool await_suspend(std::coroutine_handle<> callerSchedHandle) noexcept
|
||||
{
|
||||
if (asyncState->settled.load(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
asyncState->callerSchedHandle = callerSchedHandle;
|
||||
return true;
|
||||
}
|
||||
|
||||
Result await_resume() noexcept
|
||||
{
|
||||
return asyncState->result;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... Args>
|
||||
void storeResult(Args &&...args)
|
||||
{
|
||||
asyncState->result = Result{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
void signalSettledAndResumeCaller() noexcept
|
||||
{
|
||||
asyncState->settled.store(true, std::memory_order_release);
|
||||
|
||||
std::coroutine_handle<> handle = asyncState->callerSchedHandle;
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
boost::asio::post(
|
||||
resumeIoService,
|
||||
[handle]() { handle.resume(); });
|
||||
}
|
||||
|
||||
std::shared_ptr<AsyncState> asyncState;
|
||||
boost::asio::io_service &resumeIoService;
|
||||
};
|
||||
|
||||
} // namespace cpsBoundary
|
||||
} // namespace smo
|
||||
|
||||
#endif // ADAPTERS_SMO_CPS_CALLBACK_AREQ_H
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifndef ADAPTERS_SMO_LIVOX_PROTO1_CPS_AWAITERS_H
|
||||
#define ADAPTERS_SMO_LIVOX_PROTO1_CPS_AWAITERS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <adapters/smo/cpsCallbackAReq.h>
|
||||
#include <livoxProto1/livoxProto1.h>
|
||||
#include <spinscale/componentThread.h>
|
||||
|
||||
namespace adapters::smo {
|
||||
|
||||
struct GetOrCreateDeviceResult
|
||||
{
|
||||
bool success = false;
|
||||
std::shared_ptr<livoxProto1::Device> device;
|
||||
};
|
||||
|
||||
struct GetReturnModeResult
|
||||
{
|
||||
bool success = false;
|
||||
uint8_t returnMode = 0;
|
||||
};
|
||||
|
||||
inline auto getGetOrCreateDeviceReqAReqAwaiter(
|
||||
boost::asio::io_service &resumeIoService,
|
||||
livoxProto1_getOrCreateDeviceReqFn *fn,
|
||||
const std::string &deviceIdentifier,
|
||||
const std::shared_ptr<sscl::ComponentThread> &componentThread,
|
||||
int commandTimeoutMs,
|
||||
int retryDelayMs,
|
||||
const std::string &smoIp,
|
||||
uint8_t smoSubnetNbits,
|
||||
uint16_t dataPort,
|
||||
uint16_t cmdPort,
|
||||
uint16_t imuPort)
|
||||
{
|
||||
return ::smo::cpsBoundary::CpsCallbackAReq<
|
||||
GetOrCreateDeviceResult,
|
||||
livoxProto1_getOrCreateDeviceReqCbFn,
|
||||
std::function<void(sscl::cps::Callback<livoxProto1_getOrCreateDeviceReqCbFn>)>>(
|
||||
resumeIoService,
|
||||
[=](sscl::cps::Callback<livoxProto1_getOrCreateDeviceReqCbFn> cb)
|
||||
{
|
||||
(*fn)(
|
||||
deviceIdentifier,
|
||||
componentThread,
|
||||
commandTimeoutMs, retryDelayMs,
|
||||
smoIp, smoSubnetNbits,
|
||||
dataPort, cmdPort, imuPort,
|
||||
std::move(cb));
|
||||
});
|
||||
}
|
||||
|
||||
inline auto getDeviceGetReturnModeReqAReqAwaiter(
|
||||
boost::asio::io_service &resumeIoService,
|
||||
livoxProto1_device_getReturnModeReqFn *fn,
|
||||
std::shared_ptr<livoxProto1::Device> device)
|
||||
{
|
||||
return ::smo::cpsBoundary::CpsCallbackAReq<
|
||||
GetReturnModeResult,
|
||||
livoxProto1_device_getReturnModeReqCbFn,
|
||||
std::function<void(sscl::cps::Callback<livoxProto1_device_getReturnModeReqCbFn>)>>(
|
||||
resumeIoService,
|
||||
[=](sscl::cps::Callback<livoxProto1_device_getReturnModeReqCbFn> cb)
|
||||
{
|
||||
(*fn)(device, std::move(cb));
|
||||
});
|
||||
}
|
||||
|
||||
inline auto getDeviceEnablePcloudDataReqAReqAwaiter(
|
||||
boost::asio::io_service &resumeIoService,
|
||||
livoxProto1_device_enablePcloudDataReqFn *fn,
|
||||
std::shared_ptr<livoxProto1::Device> device)
|
||||
{
|
||||
return ::smo::cpsBoundary::CpsCallbackAReq<
|
||||
bool,
|
||||
livoxProto1_device_enablePcloudDataReqCbFn,
|
||||
std::function<void(sscl::cps::Callback<livoxProto1_device_enablePcloudDataReqCbFn>)>>(
|
||||
resumeIoService,
|
||||
[=](sscl::cps::Callback<livoxProto1_device_enablePcloudDataReqCbFn> cb)
|
||||
{
|
||||
(*fn)(device, std::move(cb));
|
||||
});
|
||||
}
|
||||
|
||||
inline auto getDeviceDisablePcloudDataReqAReqAwaiter(
|
||||
boost::asio::io_service &resumeIoService,
|
||||
livoxProto1_device_disablePcloudDataReqFn *fn,
|
||||
std::shared_ptr<livoxProto1::Device> device)
|
||||
{
|
||||
return ::smo::cpsBoundary::CpsCallbackAReq<
|
||||
bool,
|
||||
livoxProto1_device_disablePcloudDataReqCbFn,
|
||||
std::function<void(sscl::cps::Callback<livoxProto1_device_disablePcloudDataReqCbFn>)>>(
|
||||
resumeIoService,
|
||||
[=](sscl::cps::Callback<livoxProto1_device_disablePcloudDataReqCbFn> cb)
|
||||
{
|
||||
(*fn)(device, std::move(cb));
|
||||
});
|
||||
}
|
||||
|
||||
inline auto getDestroyDeviceReqAReqAwaiter(
|
||||
boost::asio::io_service &resumeIoService,
|
||||
livoxProto1_destroyDeviceReqFn *fn,
|
||||
std::shared_ptr<livoxProto1::Device> device)
|
||||
{
|
||||
return ::smo::cpsBoundary::CpsCallbackAReq<
|
||||
bool,
|
||||
livoxProto1_destroyDeviceReqCbFn,
|
||||
std::function<void(sscl::cps::Callback<livoxProto1_destroyDeviceReqCbFn>)>>(
|
||||
resumeIoService,
|
||||
[=](sscl::cps::Callback<livoxProto1_destroyDeviceReqCbFn> cb)
|
||||
{
|
||||
(*fn)(device, std::move(cb));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace adapters::smo
|
||||
|
||||
#endif // ADAPTERS_SMO_LIVOX_PROTO1_CPS_AWAITERS_H
|
||||
Reference in New Issue
Block a user