use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
use crate::bus::{
AskQuery, Bus, ContractBody, DEFAULT_QUERY_TIMEOUT, Latest, LogicalTime, OwnerCap, Publish,
Publisher, Querier, Subscribe, Subscriber, Topic,
};
use crate::model::v1::Robot;
use crate::participant::spec::{
DeclaresPublish, DeclaresQuery, DeclaresSubscribe, IsDriver, ParticipantSpec,
};
const DEFAULT_SUBSCRIBER_DEPTH: usize = 32;
pub struct SetupContext<R: ParticipantSpec> {
bus: Bus,
owner_cap: OwnerCap,
robot: Option<Arc<Robot>>,
bundle_root: Option<PathBuf>,
component_instance: Option<String>,
_runtime: PhantomData<fn() -> R>,
}
impl<R: ParticipantSpec> SetupContext<R> {
pub(crate) fn new(
bus: Bus,
owner_cap: OwnerCap,
robot: Option<Arc<Robot>>,
bundle_root: Option<PathBuf>,
component_instance: Option<String>,
) -> Self {
SetupContext {
bus,
owner_cap,
robot,
bundle_root,
component_instance,
_runtime: PhantomData,
}
}
pub fn robot(&self) -> crate::Result<&Robot> {
self.robot.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"no robot model is bound (this participant was launched without a bundle)"
)
})
}
pub fn bundle_root(&self) -> crate::Result<&Path> {
self.bundle_root.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"no bundle root is bound (this participant was launched without a bundle)"
)
})
}
pub async fn publisher<B>(&self, topic: Topic<Publish<B>>) -> crate::Result<Publisher<B>>
where
B: ContractBody<Api = R::Api>,
R: DeclaresPublish<B>,
{
Ok(Publisher::new(self.bus.clone(), &topic)?)
}
pub fn subscribe<B>(&self, topic: Topic<Subscribe<B>>) -> SubscribeBuilder<R, B>
where
B: ContractBody<Api = R::Api>,
R: DeclaresSubscribe<B>,
{
self.subscribe_with(topic, SubscribeOptions::default())
}
pub fn subscribe_with<B>(
&self,
topic: Topic<Subscribe<B>>,
options: SubscribeOptions,
) -> SubscribeBuilder<R, B>
where
B: ContractBody<Api = R::Api>,
R: DeclaresSubscribe<B>,
{
SubscribeBuilder {
bus: self.bus.clone(),
topic,
depth: options.depth,
_runtime: PhantomData,
}
}
pub async fn querier<Req, Resp>(
&self,
topic: Topic<AskQuery<Req, Resp>>,
) -> crate::Result<Querier<Req, Resp>>
where
Req: ContractBody<Api = R::Api>,
Resp: ContractBody<Api = R::Api>,
R: DeclaresQuery<Req, Resp>,
{
Ok(Querier::new(
self.bus.clone(),
&topic,
DEFAULT_QUERY_TIMEOUT,
)?)
}
pub fn owner_capability(&self) -> OwnerCap {
self.owner_cap
}
#[allow(dead_code)]
pub(crate) fn bus(&self) -> &Bus {
&self.bus
}
}
impl<R> SetupContext<R>
where
R: ParticipantSpec + IsDriver,
{
pub fn component(&self) -> crate::Result<&str> {
self.component_instance.as_deref().ok_or_else(|| {
anyhow::anyhow!("no component instance is bound (this driver was launched without one)")
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SubscribeOptions {
depth: usize,
}
impl SubscribeOptions {
pub const fn new() -> Self {
SubscribeOptions {
depth: DEFAULT_SUBSCRIBER_DEPTH,
}
}
pub const fn depth(mut self, depth: usize) -> Self {
self.depth = depth;
self
}
}
impl Default for SubscribeOptions {
fn default() -> Self {
Self::new()
}
}
pub struct SubscribeBuilder<R: ParticipantSpec, B> {
bus: Bus,
topic: Topic<Subscribe<B>>,
depth: usize,
_runtime: PhantomData<fn() -> R>,
}
impl<R, B> SubscribeBuilder<R, B>
where
R: ParticipantSpec,
B: ContractBody<Api = R::Api>,
{
pub fn depth(mut self, depth: usize) -> Self {
self.depth = depth;
self
}
pub async fn latest(self) -> crate::Result<Latest<B>> {
Ok(Latest::new(&self.bus, &self.topic).await?)
}
pub async fn subscriber(self) -> crate::Result<Subscriber<B>> {
Ok(Subscriber::new(&self.bus, &self.topic, self.depth).await?)
}
}
#[derive(Clone, Copy, Debug)]
pub struct StepContext {
epoch: u64,
step_index: u64,
time_ns: u64,
dt_ns: u64,
missed_ticks: u32,
}
impl StepContext {
pub(crate) fn new(
epoch: u64,
step_index: u64,
time_ns: u64,
dt_ns: u64,
missed_ticks: u32,
) -> Self {
StepContext {
epoch,
step_index,
time_ns,
dt_ns,
missed_ticks,
}
}
pub fn time(&self) -> LogicalTime {
LogicalTime::new(self.epoch, self.time_ns)
}
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn step_index(&self) -> u64 {
self.step_index
}
pub fn dt_ns(&self) -> u64 {
self.dt_ns
}
pub fn dt(&self) -> Duration {
Duration::from_nanos(self.dt_ns)
}
pub fn missed_ticks(&self) -> u32 {
self.missed_ticks
}
}
#[derive(Clone, Copy, Debug)]
pub struct ShutdownContext {
grace: Duration,
}
impl ShutdownContext {
pub(crate) fn new(grace: Duration) -> Self {
ShutdownContext { grace }
}
pub fn grace(&self) -> Duration {
self.grace
}
}