1use crate::action::{Action, ActionSignal, Props, StatefulAction, DEFAULT, INFINITE, VISUAL};
2use crate::comm::{QWriter, Signal, SignalId};
3use crate::resource::{IoManager, ResourceAddr, ResourceManager};
4use crate::server::{AsyncSignal, Config, State, SyncSignal};
5use crate::util::approx_eq;
6use eframe::egui;
7use eyre::{eyre, Result};
8use itertools::Itertools;
9use serde::{Deserialize, Serialize};
10use serde_cbor::Value;
11use std::collections::BTreeSet;
12
13#[derive(Debug, Deserialize, Serialize)]
14pub struct Switch {
15 #[serde(default)]
16 default: bool,
17 #[serde(alias = "if")]
18 if_true: Box<dyn Action>,
19 #[serde(alias = "else")]
20 if_false: Box<dyn Action>,
21 in_control: SignalId,
22}
23
24enum Decision {
25 Temporary(bool),
26 Final(bool),
27}
28
29stateful!(Switch {
30 if_true: Box<dyn StatefulAction>,
31 if_false: Box<dyn StatefulAction>,
32 in_control: SignalId,
33 decision: Decision,
34});
35
36impl Action for Switch {
37 #[inline]
38 fn in_signals(&self) -> BTreeSet<SignalId> {
39 let mut signals = BTreeSet::from([self.in_control]);
40 signals.extend(self.if_true.in_signals());
41 signals.extend(self.if_false.in_signals());
42 signals
43 }
44
45 #[inline]
46 fn out_signals(&self) -> BTreeSet<SignalId> {
47 let mut signals = BTreeSet::new();
48 signals.extend(self.if_true.out_signals());
49 signals.extend(self.if_false.out_signals());
50 signals
51 }
52
53 #[inline]
54 fn resources(&self, config: &Config) -> Vec<ResourceAddr> {
55 [&self.if_true, &self.if_false]
56 .iter()
57 .flat_map(|c| c.resources(config))
58 .unique()
59 .collect()
60 }
61
62 fn stateful(
63 &self,
64 io: &IoManager,
65 res: &ResourceManager,
66 config: &Config,
67 sync_writer: &QWriter<SyncSignal>,
68 async_writer: &QWriter<AsyncSignal>,
69 ) -> Result<Box<dyn StatefulAction>> {
70 Ok(Box::new(StatefulSwitch {
71 done: false,
72 if_true: self
73 .if_true
74 .stateful(io, res, config, sync_writer, async_writer)?,
75 if_false: self
76 .if_false
77 .stateful(io, res, config, sync_writer, async_writer)?,
78 in_control: self.in_control,
79 decision: Decision::Temporary(self.default),
80 }))
81 }
82}
83
84impl StatefulAction for StatefulSwitch {
85 impl_stateful!();
86
87 #[inline]
88 fn props(&self) -> Props {
89 if let Decision::Final(true) = self.decision {
90 self.if_true.props()
91 } else if let Decision::Final(false) = self.decision {
92 self.if_false.props()
93 } else {
94 [&self.if_true, &self.if_false]
95 .iter()
96 .fold(DEFAULT, |mut state, c| {
97 let c = c.props();
98 if c.visual() {
99 state |= VISUAL;
100 }
101 if c.infinite() {
102 state |= INFINITE;
103 }
104 state
105 })
106 .into()
107 }
108 }
109
110 #[inline]
111 fn start(
112 &mut self,
113 sync_writer: &mut QWriter<SyncSignal>,
114 async_writer: &mut QWriter<AsyncSignal>,
115 state: &State,
116 ) -> Result<Signal> {
117 if matches!(self.decision, Decision::Final(_)) {
118 return Err(eyre!("Tried to restart a switch."));
119 }
120
121 let decision = match state.get(&self.in_control) {
122 Some(Value::Bool(c)) => *c,
123 Some(Value::Integer(1)) => true,
124 Some(Value::Integer(0)) => false,
125 Some(Value::Float(x)) if approx_eq(*x, 1.0) => true,
126 Some(Value::Float(x)) if approx_eq(*x, 0.0) => false,
127 Some(v) => return Err(eyre!("Failed to interpret value ({v:?}) as boolean.")),
128 None => return Err(eyre!("Control state is missing.")),
129 };
130
131 self.decision = Decision::Final(decision);
132 if decision {
133 self.if_true.start(sync_writer, async_writer, state)
134 } else {
135 self.if_false.start(sync_writer, async_writer, state)
136 }
137 }
138
139 #[inline]
140 fn update(
141 &mut self,
142 signal: &ActionSignal,
143 sync_writer: &mut QWriter<SyncSignal>,
144 async_writer: &mut QWriter<AsyncSignal>,
145 state: &State,
146 ) -> Result<Signal> {
147 match self.decision {
148 Decision::Final(true) => {
149 let news = self
150 .if_true
151 .update(signal, sync_writer, async_writer, state)?;
152 if self.if_true.is_over()? {
153 self.done = true;
154 }
155 Ok(news)
156 }
157 Decision::Final(false) => {
158 let news = self
159 .if_false
160 .update(signal, sync_writer, async_writer, state)?;
161 if self.if_false.is_over()? {
162 self.done = true;
163 }
164 Ok(news)
165 }
166 _ => Ok(Signal::none()),
167 }
168 }
169
170 fn show(
171 &mut self,
172 ui: &mut egui::Ui,
173 sync_writer: &mut QWriter<SyncSignal>,
174 async_writer: &mut QWriter<AsyncSignal>,
175 state: &State,
176 ) -> Result<()> {
177 match self.decision {
178 Decision::Final(true) => self.if_true.show(ui, sync_writer, async_writer, state),
179 Decision::Final(false) => self.if_false.show(ui, sync_writer, async_writer, state),
180 Decision::Temporary(_) => Ok(()),
181 }
182 }
183
184 #[inline]
185 fn stop(
186 &mut self,
187 sync_writer: &mut QWriter<SyncSignal>,
188 async_writer: &mut QWriter<AsyncSignal>,
189 state: &State,
190 ) -> Result<Signal> {
191 match self.decision {
192 Decision::Final(true) => self.if_true.stop(sync_writer, async_writer, state),
193 Decision::Final(false) => self.if_false.stop(sync_writer, async_writer, state),
194 Decision::Temporary(_) => Ok(Signal::none()),
195 }
196 }
197}