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}