1use crate::base::block::{BlockDesc, BlockProps, BlockStaticDesc};
8use crate::base::input::input_reader::InputReader;
9use crate::base::input::InputProps;
10use libhaystack::val::Value;
11
12use crate::base::engine::Engine;
13use crate::blocks::bitwise::{BitwiseAnd, BitwiseNot, BitwiseOr, BitwiseXor};
14use crate::blocks::collections::{Dict, GetElement, Keys, Length, List, Values};
15use crate::blocks::control::{Pid, PriorityArray};
16use crate::blocks::logic::{
17 And, Equal, GreaterThan, GreaterThanEq, Latch, LessThan, LessThanEq, Not, NotEqual, Or, Xor,
18};
19use crate::blocks::math::{Abs, Add, ArcTan, Cos, Sub};
20use crate::blocks::math::{
21 ArcCos, ArcSin, Average, Div, Even, Exp, Log10, Logn, Max, Median, Min, Mod, Mul, Neg, Odd,
22 Pow, Sin, Sqrt,
23};
24use crate::blocks::misc::{HasValue, ParseBool, ParseNumber, Random, SineWave};
25use crate::blocks::string::{Concat, Replace, StrLen};
26use crate::blocks::time::Now;
27
28use anyhow::{anyhow, Result};
29use lazy_static::lazy_static;
30use std::collections::BTreeMap;
31use std::sync::Mutex;
32
33use super::{BlockImpl, InputImpl};
34
35pub(crate) type DynBlockProps = dyn BlockProps<
36 Reader = <InputImpl as InputProps>::Reader,
37 Writer = <InputImpl as InputProps>::Writer,
38>;
39type MapType = BTreeMap<String, BTreeMap<String, BlockEntry>>;
40type BlockRegistry = Mutex<MapType>;
41
42#[derive(Debug, Clone)]
44pub struct BlockEntry {
45 pub desc: BlockDesc,
46 pub make: Option<fn() -> Box<DynBlockProps>>,
47}
48
49#[macro_export]
52macro_rules! register_blocks{
53 ( $( $block_name:ty ),* ) => {
54 lazy_static! {
55 static ref BLOCKS: BlockRegistry = {
59 let mut reg = BTreeMap::new();
60
61 $(
62 register_impl::<$block_name>(&mut reg);
63 )*
64
65 reg.into()
66 };
67 }
68
69 pub fn schedule_block<E>(name: &str, eng: &mut E) -> Result<uuid::Uuid>
80 where E : Engine<Reader = <InputImpl as InputProps>::Reader, Writer = <InputImpl as InputProps>::Writer> {
81
82 match name {
83 $(
84 stringify!($block_name) => {
85 let block = <$block_name>::new();
86 let uuid = *block.id();
87 eng.schedule(block);
88 Ok(uuid)
89 }
90 )*
91 _ => {
92 return Err(anyhow!("Block not found"));
93 }
94 }
95
96 }
97
98 pub fn schedule_block_with_uuid<E>(name: &str, uuid: uuid::Uuid, eng: &mut E) -> Result<uuid::Uuid>
101 where E : Engine<Reader = <InputImpl as InputProps>::Reader, Writer = <InputImpl as InputProps>::Writer> {
102
103 match name {
104 $(
105 stringify!($block_name) => {
106 let block = <$block_name>::new_uuid(uuid);
107 eng.schedule(block);
108 Ok(uuid)
109 }
110 )*
111 _ => {
112 return Err(anyhow!("Block not found"));
113 }
114 }
115
116 }
117
118 pub async fn eval_static_block(name: &str, inputs: Vec<Value>) -> Result<Vec<Value>> {
128 match name {
129 $(
130 stringify!($block_name) => {
131 let mut block = <$block_name>::new();
132 eval_block_impl(&mut block, inputs).await
133 }
134 )*
135 _ => {
136 return Err(anyhow!("Block not found"));
137 }
138 }
139 }
140 };
141}
142
143register_blocks!(
144 And,
146 Or,
147 Not,
148 Equal,
149 NotEqual,
150 Xor,
151 GreaterThan,
152 GreaterThanEq,
153 Latch,
154 LessThan,
155 LessThanEq,
156 Abs,
158 Add,
159 ArcCos,
160 ArcTan,
161 Average,
162 Median,
163 Even,
164 Odd,
165 Sub,
166 Mul,
167 Div,
168 Exp,
169 Cos,
170 ArcSin,
171 Sin,
172 Log10,
173 Logn,
174 Sqrt,
175 Pow,
176 Mod,
177 Min,
178 Max,
179 Neg,
180 BitwiseAnd,
182 BitwiseNot,
183 BitwiseOr,
184 BitwiseXor,
185 Pid,
187 PriorityArray,
188 Concat,
190 Replace,
191 StrLen,
192 GetElement,
194 Length,
195 Keys,
196 Values,
197 List,
198 Dict,
199 Now,
201 Random,
203 SineWave,
204 ParseBool,
205 ParseNumber,
206 HasValue
207);
208
209pub fn make(name: &str, lib: Option<String>) -> Option<Box<DynBlockProps>> {
215 let entry = get_block(name, lib)?;
216 entry.make.map(|make| make())
217}
218
219pub fn get_block(name: &str, lib: Option<String>) -> Option<BlockEntry> {
221 let reg = BLOCKS.lock().expect("Block registry is locked");
222 let lib = lib.unwrap_or_else(|| "core".to_string());
223
224 let reg = reg.get(&lib)?;
225 reg.get(name).cloned()
226}
227
228pub fn get_core_block(name: &str) -> Option<BlockEntry> {
230 get_block(name, Some("core".to_string()))
231}
232
233pub fn list_registered_blocks() -> Vec<BlockDesc> {
235 let reg = BLOCKS.lock().expect("Block registry is locked");
236
237 let mut blocks = Vec::new();
238 for (_, lib) in reg.iter() {
239 for (_, block) in lib.iter() {
240 blocks.push(block.desc.clone());
241 }
242 }
243
244 blocks
245}
246
247pub fn register_block_desc(desc: &BlockDesc) -> Result<()> {
249 let mut reg = BLOCKS.lock().expect("Block registry is locked");
250
251 let lib = desc.library.clone();
252 let reg = reg.entry(lib).or_default();
253
254 let name = desc.name.clone();
255 if reg.contains_key(&name) {
256 return Err(anyhow!("Block already registered"));
257 }
258
259 reg.insert(
260 name.to_string(),
261 BlockEntry {
262 desc: desc.clone(),
263 make: None,
264 },
265 );
266
267 Ok(())
268}
269
270pub fn register<B: BlockImpl>() {
276 let mut reg = BLOCKS.lock().expect("Block registry is locked");
277
278 register_impl::<B>(&mut reg);
279}
280
281pub async fn eval_block_impl<B: BlockImpl>(
289 block: &mut B,
290 inputs: Vec<Value>,
291) -> Result<Vec<Value>> {
292 for (i, input) in inputs.iter().enumerate() {
293 let mut input_pins = block.inputs_mut();
294
295 if i >= input_pins.len() {
296 return Err(anyhow!("Too many inputs"));
297 }
298
299 input_pins[i].increment_conn();
300 if input_pins[i].writer().try_send(input.clone()).is_ok() && i < inputs.len() - 1 {
301 block.read_inputs().await;
302 }
303 }
304
305 block.execute().await;
306 Ok(block.outputs().iter().map(|o| o.value().clone()).collect())
307}
308
309fn register_impl<B: BlockImpl>(reg: &mut MapType) {
310 let desc = <B as BlockStaticDesc>::desc();
311 let lib = desc.library.clone();
312
313 reg.entry(lib).or_default().insert(desc.name.clone(), {
314 let make = || -> Box<DynBlockProps> {
315 let block = B::default();
316 Box::new(block)
317 };
318
319 BlockEntry {
320 desc: desc.clone(),
321 make: Some(make),
322 }
323 });
324}
325
326#[cfg(test)]
327mod test {
328
329 use crate::base::block::connect::connect_output;
330
331 use super::*;
332
333 #[test]
334 fn test_registry() {
335 let add = get_core_block("Add").expect("Add block not found");
336 let random = get_core_block("Random").expect("Random block not found");
337 let sine = get_core_block("SineWave").expect("SineWave block not found");
338
339 assert_eq!(add.desc.name, "Add");
340 assert_eq!(random.desc.name, "Random");
341 assert_eq!(sine.desc.name, "SineWave");
342
343 let mut random = random.make.unwrap()();
344 let mut outs = random.outputs_mut();
345
346 let mut add = add.make.unwrap()();
347 let mut ins = add.inputs_mut();
348
349 let out = outs.first_mut().unwrap();
350 let input = ins.first_mut().unwrap();
351
352 connect_output(*out, *input).unwrap();
353
354 let mut eng = crate::single_threaded::SingleThreadedEngine::new();
355
356 schedule_block("Add", &mut eng).expect("Block");
357
358 assert!(eng.blocks().iter().any(|b| b.desc().name == "Add"));
359 }
360
361 #[tokio::test]
362 async fn test_block_eval() {
363 let result = eval_static_block("Add", vec![Value::from(1), Value::from(2)]).await;
364
365 assert_eq!(result.unwrap(), vec![Value::from(3)]);
366 }
367}