use crate::base::block::{Block, BlockDesc, BlockProps, BlockStaticDesc};
use crate::base::input::InputProps;
use crate::base::engine::Engine;
use crate::blocks::control::Pid;
use crate::blocks::logic::{
And, Equal, GreaterThan, GreaterThanEq, LessThan, LessThanEq, Not, NotEqual, Or, Xor,
};
use crate::blocks::math::{Abs, Add, ArcTan, Cos, Sub};
use crate::blocks::math::{
ArcCos, ArcSin, Average, Div, Even, Exp, Log10, Logn, Max, Median, Min, Mod, Mul, Neg, Odd,
Pow, Sin, Sqrt,
};
use crate::blocks::misc::{Random, SineWave};
use crate::blocks::string::StrLen;
use anyhow::{anyhow, Result};
use lazy_static::lazy_static;
use std::collections::BTreeMap;
use std::sync::Mutex;
use super::misc::ParseNumber;
use super::InputImpl;
pub(crate) type DynBlockProps = dyn BlockProps<
Reader = <InputImpl as InputProps>::Reader,
Writer = <InputImpl as InputProps>::Writer,
>;
type MapType = BTreeMap<String, BlockEntry>;
type BlockRegistry = Mutex<MapType>;
pub struct BlockEntry {
pub desc: BlockDesc,
pub make: Option<fn() -> Box<DynBlockProps>>,
}
#[macro_export]
macro_rules! register_blocks{
( $( $x:ty ),* ) => {
lazy_static! {
pub static ref BLOCKS: BlockRegistry = {
let mut reg = BTreeMap::new();
$(
register_impl::<$x>(&mut reg);
)*
reg.into()
};
}
pub fn schedule_block<E>(name: &str, eng: &mut E) -> Result<uuid::Uuid>
where E : Engine<Reader = <InputImpl as InputProps>::Reader, Writer = <InputImpl as InputProps>::Writer> {
match name {
$(
stringify!($x) => {
let block = <$x>::new();
let uuid = *block.id();
eng.schedule(block);
Ok(uuid)
}
)*
_ => {
return Err(anyhow!("Block not found"));
}
}
}
pub fn schedule_block_with_uuid<E>(name: &str, uuid: uuid::Uuid, eng: &mut E) -> Result<uuid::Uuid>
where E : Engine<Reader = <InputImpl as InputProps>::Reader, Writer = <InputImpl as InputProps>::Writer> {
match name {
$(
stringify!($x) => {
let block = <$x>::new_uuid(uuid);
eng.schedule(block);
Ok(uuid)
}
)*
_ => {
return Err(anyhow!("Block not found"));
}
}
}
};
}
register_blocks!(
And,
Or,
Not,
Equal,
NotEqual,
Xor,
GreaterThan,
GreaterThanEq,
LessThan,
LessThanEq,
Abs,
Add,
ArcCos,
ArcTan,
Average,
Median,
Even,
Odd,
Sub,
Mul,
Div,
Exp,
Cos,
ArcSin,
Sin,
Log10,
Logn,
Sqrt,
Pow,
Mod,
Min,
Max,
Neg,
Pid,
StrLen,
Random,
SineWave,
ParseNumber
);
pub fn make(name: &str) -> Option<Box<DynBlockProps>> {
let reg = BLOCKS.lock().expect("Block registry is locked");
if let Some(data) = reg.get(name) {
data.make.map(|make| make())
} else {
None
}
}
pub fn register<
B: Block<
Reader = <InputImpl as InputProps>::Reader,
Writer = <InputImpl as InputProps>::Writer,
> + Default
+ 'static,
>() {
let mut reg = BLOCKS.lock().expect("Block registry is locked");
register_impl::<B>(&mut reg);
}
fn register_impl<
B: Block<
Reader = <InputImpl as InputProps>::Reader,
Writer = <InputImpl as InputProps>::Writer,
> + Default
+ 'static,
>(
reg: &mut MapType,
) {
reg.insert(<B as BlockStaticDesc>::desc().name.clone(), {
let desc = <B as BlockStaticDesc>::desc();
let make = || -> Box<DynBlockProps> {
let block = B::default();
Box::new(block)
};
BlockEntry {
desc: desc.clone(),
make: Some(make),
}
});
}
#[cfg(test)]
mod test {
use crate::base::block::connect::connect_output;
use super::*;
#[test]
fn test_registry() {
let mut add = make("Add").expect("Add block not found");
let mut random = make("Random").expect("Random block not found");
let sine = make("SineWave").expect("SineWave block not found");
assert_eq!(add.desc().name, "Add");
assert_eq!(random.desc().name, "Random");
assert_eq!(sine.desc().name, "SineWave");
let mut outs = random.outputs_mut();
let mut ins = add.inputs_mut();
let out = outs.first_mut().unwrap();
let input = ins.first_mut().unwrap();
connect_output(*out, *input).unwrap();
let mut eng = crate::single_threaded::SingleThreadedEngine::new();
schedule_block("Add", &mut eng).expect("Block");
assert!(eng.blocks().iter().any(|b| b.desc().name == "Add"));
}
}