use uuid::Uuid;
use crate::base::{
block::{Block, BlockDesc, BlockProps, BlockState},
input::{InputProps, input_reader::InputReader},
output::Output,
};
use crate::blocks::utils::input_as_number;
use libhaystack::val::{Bool, Value, kind::HaystackKind};
use crate::{blocks::InputImpl, blocks::OutputImpl};
#[block]
#[derive(BlockProps, Debug)]
#[category = "control"]
pub struct LeadLag {
#[input(kind = "Bool")]
pub enable: InputImpl,
#[input(kind = "Bool")]
pub rotate: InputImpl,
#[input(kind = "Number")]
pub demand: InputImpl,
#[output(name = "a", kind = "Bool")]
pub out_a: OutputImpl,
#[output(name = "b", kind = "Bool")]
pub out_b: OutputImpl,
lead_index: u8,
prev_rotate: Option<bool>,
}
impl Block for LeadLag {
async fn execute(&mut self) {
self.read_inputs_until_ready().await;
let enable = matches!(self.enable.get_value(), Some(Value::Bool(b)) if b.value);
let rotate = matches!(self.rotate.get_value(), Some(Value::Bool(b)) if b.value);
let demand = input_as_number(&self.demand)
.map(|n| n.value as i32)
.unwrap_or(0)
.max(0);
let rising_rotate = matches!(self.prev_rotate, Some(false)) && rotate;
self.prev_rotate = Some(rotate);
if rising_rotate {
self.lead_index ^= 1;
}
let (a_on, b_on) = if !enable {
(false, false)
} else {
let lead_on = demand >= 1;
let lag_on = demand >= 2;
if self.lead_index == 0 {
(lead_on, lag_on)
} else {
(lag_on, lead_on)
}
};
self.out_a.set(Bool { value: a_on }.into());
self.out_b.set(Bool { value: b_on }.into());
}
}
#[cfg(test)]
mod test {
use libhaystack::val::Value;
use crate::{
base::block::Block, base::block::test_utils::write_block_inputs, blocks::control::LeadLag,
};
#[tokio::test]
async fn test_leadlag_disable() {
let mut block = LeadLag::new();
write_block_inputs([
(&mut block.enable, Value::make_false()),
(&mut block.rotate, Value::make_false()),
(&mut block.demand, 2.into()),
])
.await;
block.execute().await;
assert_eq!(block.out_a.value, false.into());
assert_eq!(block.out_b.value, false.into());
}
#[tokio::test]
async fn test_leadlag_demand_one_lead_only() {
let mut block = LeadLag::new();
write_block_inputs([
(&mut block.enable, Value::make_true()),
(&mut block.rotate, Value::make_false()),
(&mut block.demand, 1.into()),
])
.await;
block.execute().await;
assert_eq!(block.out_a.value, true.into());
assert_eq!(block.out_b.value, false.into());
}
#[tokio::test]
async fn test_leadlag_demand_two_both_on() {
let mut block = LeadLag::new();
write_block_inputs([
(&mut block.enable, Value::make_true()),
(&mut block.rotate, Value::make_false()),
(&mut block.demand, 2.into()),
])
.await;
block.execute().await;
assert_eq!(block.out_a.value, true.into());
assert_eq!(block.out_b.value, true.into());
}
#[tokio::test]
async fn test_leadlag_rotate_swaps_lead() {
let mut block = LeadLag::new();
write_block_inputs([
(&mut block.enable, Value::make_true()),
(&mut block.rotate, Value::make_false()),
(&mut block.demand, 1.into()),
])
.await;
block.execute().await;
assert_eq!(block.out_a.value, true.into());
assert_eq!(block.out_b.value, false.into());
write_block_inputs([(&mut block.rotate, true)]).await;
block.execute().await;
assert_eq!(block.out_a.value, false.into());
assert_eq!(block.out_b.value, true.into());
}
}