use std::marker::PhantomData;
use std::sync::Arc;
use std::time::Duration;
use crate::__private::surface::{
ComponentBoundSurface, ToolSurface, TypedIoSurface, WorldAuthoritySurface,
};
use crate::AssetResolver;
use crate::bus::{
AskQuery, CommandContract, CommandPublisher, ContractBody, DEFAULT_QUERY_TIMEOUT,
DiagnosticContract, DiagnosticPublisher, Latest, MeasurementContract, MeasurementPublisher,
Publish, Querier, RobotInstant, ServeQuery, StateContract, StatePublisher, StepToken,
Subscribe, Subscriber, TimelineId, Topic, WorldClockContract,
};
use crate::model::Robot;
use crate::participant::api::{Participant, QueryRegistration};
use crate::participant::managed::{ManagedTaskPolicy, ManagedTasks};
use phoxal_bus::{Bus, TimelineAuthority, WorldClockPublisher};
pub(crate) type TimelineRetention = Box<dyn Fn(TimelineId) + Send + Sync>;
pub struct SetupContext<R: Participant> {
bus: Bus,
robot: Option<Arc<Robot>>,
assets: Option<AssetResolver>,
component_instance: Option<String>,
managed_tasks: ManagedTasks,
timeline_retentions: Vec<TimelineRetention>,
queries: Vec<QueryRegistration<R>>,
_runtime: PhantomData<fn() -> R>,
}
impl<R: Participant> SetupContext<R> {
pub(crate) fn new(
bus: Bus,
robot: Option<Arc<Robot>>,
assets: Option<AssetResolver>,
component_instance: Option<String>,
) -> Self {
SetupContext {
bus,
robot,
assets,
component_instance,
managed_tasks: ManagedTasks::default(),
timeline_retentions: Vec::new(),
queries: Vec::new(),
_runtime: PhantomData,
}
}
pub fn spawn_managed<F>(&mut self, name: impl Into<String>, future: F)
where
F: std::future::Future<Output = ()> + Send + 'static,
{
self.spawn_managed_with(name, ManagedTaskPolicy::FaultOnExit, future);
}
pub fn spawn_managed_with<F>(
&mut self,
name: impl Into<String>,
policy: ManagedTaskPolicy,
future: F,
) where
F: std::future::Future<Output = ()> + Send + 'static,
{
self.managed_tasks.spawn(name, policy, future);
}
pub(crate) fn take_managed_tasks(&mut self) -> ManagedTasks {
std::mem::take(&mut self.managed_tasks)
}
pub(crate) fn register_timeline_retention(
&mut self,
retention: impl Fn(TimelineId) + Send + Sync + 'static,
) {
self.timeline_retentions.push(Box::new(retention));
}
pub(crate) fn take_timeline_retentions(&mut self) -> Vec<TimelineRetention> {
std::mem::take(&mut self.timeline_retentions)
}
pub(crate) fn register_query(&mut self, registration: QueryRegistration<R>) {
self.queries.push(registration);
}
pub(crate) fn query_registrations(&self) -> &[QueryRegistration<R>] {
&self.queries
}
pub(crate) fn take_query_registrations(&mut self) -> Vec<QueryRegistration<R>> {
std::mem::take(&mut self.queries)
}
pub(crate) fn component_instance(&self) -> Option<&str> {
self.component_instance.as_deref()
}
pub(crate) fn robot_ref(&self) -> Option<&Robot> {
self.robot.as_deref()
}
pub fn robot(&self) -> crate::Result<&Robot> {
self.robot_ref().ok_or_else(|| {
anyhow::anyhow!(
"no robot model is bound (this participant was launched without a bundle root)"
)
})
}
pub fn assets(&self) -> crate::Result<&AssetResolver> {
self.assets.as_ref().ok_or_else(|| {
anyhow::anyhow!("no compiled assets are bound (this participant has no bundle root)")
})
}
}
impl<R: Participant + TypedIoSurface> SetupContext<R> {
pub async fn state_publisher<B: StateContract>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<StatePublisher<B>> {
Ok(StatePublisher::new(self.bus.clone(), &topic)?)
}
pub async fn measurement_publisher<B: MeasurementContract>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<MeasurementPublisher<B>> {
Ok(MeasurementPublisher::new(self.bus.clone(), &topic)?)
}
pub async fn command_publisher<B: CommandContract>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<CommandPublisher<B>> {
Ok(CommandPublisher::new(self.bus.clone(), &topic)?)
}
pub async fn diagnostic_publisher<B: DiagnosticContract>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<DiagnosticPublisher<B>> {
Ok(DiagnosticPublisher::new(self.bus.clone(), &topic)?)
}
pub async fn latest<B: ContractBody>(
&mut self,
topic: Topic<Subscribe<B>>,
) -> crate::Result<Latest<B>> {
let handle = Latest::new(&self.bus, &topic).await?;
let retained = handle.clone();
self.register_timeline_retention(move |timeline| {
retained.__retain_timeline(timeline);
});
Ok(handle)
}
pub async fn subscriber<B: ContractBody>(
&mut self,
topic: Topic<Subscribe<B>>,
depth: usize,
) -> crate::Result<Subscriber<B>> {
let handle = Subscriber::new(&self.bus, &topic, depth).await?;
let retained = handle.clone();
self.register_timeline_retention(move |timeline| {
retained.__retain_timeline(timeline);
});
Ok(handle)
}
pub async fn querier<Req: ContractBody, Resp: ContractBody>(
&self,
topic: Topic<AskQuery<Req, Resp>>,
) -> crate::Result<Querier<Req, Resp>> {
Ok(Querier::new(
self.bus.clone(),
&topic,
DEFAULT_QUERY_TIMEOUT,
)?)
}
pub async fn query<Req, Resp, H>(
&mut self,
topic: Topic<ServeQuery<Req, Resp>>,
handler: H,
) -> crate::Result<()>
where
Req: ContractBody,
Resp: ContractBody,
H: for<'a> std::ops::AsyncFn(
&'a R,
&'a R::Api,
Req,
&'a mut R::State,
) -> crate::bus::QueryResult<Resp>
+ Send
+ Sync
+ 'static,
{
let topic = topic.key().to_string();
if self
.query_registrations()
.iter()
.any(|registration| registration.topic() == topic)
{
anyhow::bail!("duplicate query binding for '{topic}'");
}
self.register_query(QueryRegistration::new(topic, handler));
Ok(())
}
}
impl<R: Participant + ComponentBoundSurface> SetupContext<R> {
pub fn component(&self) -> crate::Result<&crate::model::ComponentInstance> {
let id = self.component_instance().ok_or_else(|| {
anyhow::anyhow!("no component instance is bound for this participant launch")
})?;
Ok(self.robot()?.component_instance(id)?)
}
}
impl<R: Participant + WorldAuthoritySurface> SetupContext<R> {
pub fn timeline_authority(&self, timeline: TimelineId) -> crate::Result<TimelineAuthority> {
Ok(TimelineAuthority::__mint(timeline)?)
}
pub async fn world_clock_publisher<B: WorldClockContract>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<WorldClockPublisher<B>> {
Ok(WorldClockPublisher::__mint(self.bus.clone(), &topic)?)
}
}
impl<R: Participant + ToolSurface> SetupContext<R> {
pub fn bus(&self) -> Bus {
self.bus.clone()
}
pub fn execution(&self) -> crate::bus::ExecutionId {
self.bus.execution()
}
}
#[derive(Clone, Copy, Debug)]
pub struct StepContext {
token: StepToken,
step_index: u64,
dt: Duration,
missed_ticks: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ResetContext {
previous_timeline: TimelineId,
new_timeline: TimelineId,
}
impl ResetContext {
pub(crate) fn new(previous_timeline: TimelineId, new_timeline: TimelineId) -> Self {
Self {
previous_timeline,
new_timeline,
}
}
pub fn previous_timeline(&self) -> TimelineId {
self.previous_timeline
}
pub fn new_timeline(&self) -> TimelineId {
self.new_timeline
}
}
impl StepContext {
pub(crate) fn new(token: StepToken, step_index: u64, dt: Duration, missed_ticks: u32) -> Self {
StepContext {
token,
step_index,
dt,
missed_ticks,
}
}
pub fn token(&self) -> &StepToken {
&self.token
}
pub fn now(&self) -> RobotInstant {
crate::bus::StepStamp::instant(&self.token)
}
pub fn timeline(&self) -> TimelineId {
self.now().timeline()
}
pub fn step_index(&self) -> u64 {
self.step_index
}
pub fn dt(&self) -> Duration {
self.dt
}
pub fn missed_ticks(&self) -> u32 {
self.missed_ticks
}
}