gmt_dos_actors/
lib.rs

1/*!
2# GMT Dynamic Optics Simulation Actors
3
4The GMT DOS [Actor]s are the building blocks of the GMT DOS integrated model.
5Each [Actor] has 3 properties:
6 1. input objects
7 2. output objects
8 3. client
9
10## Input/Outputs
11
12input objects are a collection of inputs and
13output objects are a collection of outputs.
14An actor must have at least either 1 input or 1 output.
15A pair of input/output is linked with a [channel](flume::bounded) where the input is the receiver
16and the output is the sender.
17The same output may be linked to several inputs.
18[channel](flume::bounded)s are used to synchronize the [Actor]s.
19
20Each [Actor] performs the same task, within an infinite loop, consisting of 3 operations:
21 1. receiving the inputs if any
22 2. updating the client state
23 3. sending the outputs if any
24
25The loop exits when one of the following error happens: [ActorError::NoData], [ActorError::DropSend], [ActorError::DropRecv].
26
27### Sampling rates
28
29All the inputs of an [Actor] are collected are the same rate `NI`, and all the outputs are distributed at the same rate `NO`, however both inputs and outputs rates may be different.
30The inputs rate `NI` is inherited from the rate `NO` of outputs that the data is collected from i.e. `(next actor)::NI=(current actor)::NO`.
31
32The rates `NI` or `NO` are defined as the ratio between the simulation sampling frequency `[Hz]` and the actor inputs or outputs sampling frequency `[Hz]`, it must be an integer ≥ 1.
33If `NI>NO`, outputs are upsampled with a simple sample-and-hold for `NI/NO` samples.
34If `NO>NI`, outputs are decimated by a factor `NO/NI`
35
36For a 1000Hz simulation sampling frequency, the following table gives some examples of inputs/outputs sampling frequencies and rate:
37
38| Inputs `[Hz]` | Ouputs `[Hz]` | NI | NO | Upsampling | Decimation |
39|--------------:|--------------:|---:|---:|-----------:|-----------:|
40| 1000          | 1000          |  1 |  1 | -          |  1         |
41| 1000          |  100          |  1 | 10 | -          | 10         |
42|  100          | 1000          | 10 |  1 | 10         | -          |
43|  500          |  100          |  2 | 10 | -          |  5         |
44|  100          |  500          | 10 |  2 | 5          |  -         |
45
46## Client
47
48A client must be assigned to an [Actor]
49and the client must implement some of the following traits:
50 - [Write] if the actor has some outputs,
51 - [Read] if the actor has some inputs,
52 - [Update], this trait must always be implemented (but the default empty implementation is acceptable)
53
54## Model
55
56An integrated model is build as follows:
57 1. select and instanciate the clients
58 2. assign clients to [Actor]s
59 3. add outputs to the [Actor]s and connect them to inputs of other [Actor]s
60 4. build a [Model]
61
62
63 5. Check, run and wait for the [Model] completion
64
65[Actor]: crate::actor::Actor
66[Write]: interface::Write
67[Read]: interface::Read
68[Update]: interface::Update
69[Model]: crate::model::Model
70*/
71
72use interface::Update;
73use std::sync::Arc;
74use tokio::sync::Mutex;
75
76pub use gmt_dos_actors_dsl::actorscript;
77
78pub mod actor;
79pub mod aggregation;
80pub mod client;
81pub mod framework;
82pub mod graph;
83pub mod model;
84// pub mod subsystem;
85
86pub mod system;
87
88#[derive(thiserror::Error, Debug)]
89pub enum ActorError {
90    #[error("{msg} receiver disconnected")]
91    DropRecv {
92        msg: String,
93        source: flume::RecvError,
94    },
95    #[error("{msg} sender disconnected")]
96    DropSend {
97        msg: String,
98        source: flume::SendError<()>,
99    },
100    #[error("no new data produced")]
101    NoData,
102    #[error("no inputs defined")]
103    NoInputs,
104    #[error("no outputs defined")]
105    NoOutputs,
106    #[error("no client defined")]
107    NoClient,
108    #[error("output {0} dropped")]
109    Disconnected(String),
110    #[error("{0} has some inputs but inputs rate is zero")]
111    SomeInputsZeroRate(String),
112    #[error("{0} has no inputs but a positive inputs rate (May be this Actor should instead be an Initiator)")]
113    NoInputsPositiveRate(String),
114    #[error("{0} has some outputs but outputs rate is zero")]
115    SomeOutputsZeroRate(String),
116    #[error("{0} has no outputs but a positive outputs rate (May be this Actor should instead be a Terminator)")]
117    NoOutputsPositiveRate(String),
118    #[error(r#"Orphan output "{0}" in "{1}" actor"#)]
119    OrphanOutput(String, String),
120}
121pub(crate) type Result<R> = std::result::Result<R, ActorError>;
122
123/// Creates a reference counted pointer
124///
125/// Converts an object into an atomic (i.e. thread-safe) reference counted pointer [Arc] with interior mutability [Mutex]
126pub trait ArcMutex {
127    fn into_arcx(self) -> Arc<Mutex<Self>>
128    where
129        Self: Sized,
130    {
131        Arc::new(Mutex::new(self))
132    }
133}
134impl<C: Update> ArcMutex for C {}
135
136/// Actors macros
137mod macros;
138
139pub(crate) fn trim(name: &str) -> String {
140    if let Some((prefix, suffix)) = name.split_once('<') {
141        let generics: Vec<_> = suffix.split(',').map(trim).collect();
142        format!("{}<{}", trim(prefix), generics.join(","))
143    } else if let Some((_, suffix)) = name.rsplit_once("::") {
144        suffix.into()
145    } else {
146        name.into()
147    }
148}
149
150pub mod prelude {
151    pub use super::{
152        actor::{Actor, Initiator, Terminator},
153        framework::{
154            model::{FlowChart, GetName},
155            network::{AddActorOutput, AddOuput, IntoLogs, IntoLogsN, TryIntoInputs},
156        },
157        model,
158        model::{Model, Unknown},
159        ArcMutex,
160    };
161    pub use vec_box::vec_box;
162}