extern crate alloc;
use core::time::Duration;
use crate::{traits::Scalar, IsValid};
use alloc::{vec, vec::Vec};
use crate::byte_data::{parse_byte_data_spec, try_unpack_data, ByteOrderSpec, DataType};
use pictorus_block_data::BlockData as OldBlockData;
use pictorus_traits::{ByteSliceSignal, Pass, PassBy, ProcessBlock};
pub struct BytesUnpackBlock<T: Apply> {
pub data: Vec<OldBlockData>,
buffer: T::Output,
last_valid_time: Option<Duration>,
}
impl<T: Apply> Default for BytesUnpackBlock<T> {
fn default() -> Self {
let buffer = T::Output::default();
let data = T::as_old_block_data(&buffer);
BytesUnpackBlock {
data,
buffer,
last_valid_time: None,
}
}
}
impl<T: Apply> ProcessBlock for BytesUnpackBlock<T> {
type Inputs = ByteSliceSignal;
type Output = T::Output;
type Parameters = T::Parameters;
fn process<'b>(
&'b mut self,
parameters: &Self::Parameters,
context: &dyn pictorus_traits::Context,
inputs: PassBy<'_, Self::Inputs>,
) -> PassBy<'b, Self::Output> {
let update_age = context.time() - self.last_valid_time.unwrap_or_default();
let unpack_success = T::apply(&mut self.buffer, inputs, parameters, update_age);
self.data = T::as_old_block_data(&self.buffer);
if unpack_success {
self.last_valid_time = Some(context.time());
}
self.buffer.as_by()
}
}
impl<T: Apply> IsValid for BytesUnpackBlock<T> {
fn is_valid(&self, _app_time_s: f64) -> OldBlockData {
OldBlockData::scalar_from_bool(T::is_valid(&self.buffer))
}
}
pub struct Parameters<const N: usize> {
pub pack_spec: [(DataType, ByteOrderSpec); N],
pub stale_age: Duration,
}
impl<const N: usize> Parameters<N> {
pub fn new<S: AsRef<str>>(pack_spec_str: &[S], stale_age_ms: f64) -> Self {
let pack_spec = parse_byte_data_spec(pack_spec_str)
.try_into()
.expect("Bytes Data Spec is incorrectly sized for the number of inputs");
Self {
pack_spec,
stale_age: Duration::from_secs_f64(stale_age_ms / 1000.0),
}
}
}
pub trait Unpack: Scalar {
fn unpack(data: &[u8], data_type: DataType, byte_order: ByteOrderSpec)
-> (Option<Self>, &[u8]);
fn as_f64(&self) -> f64;
}
impl Unpack for f64 {
fn unpack(
data: &[u8],
data_type: DataType,
byte_order: ByteOrderSpec,
) -> (Option<Self>, &[u8]) {
let val = match byte_order {
ByteOrderSpec::BigEndian => try_unpack_data::<byteorder::BigEndian>(data, data_type),
ByteOrderSpec::LittleEndian => {
try_unpack_data::<byteorder::LittleEndian>(data, data_type)
}
}
.ok();
let advanced_data = if val.is_some() {
&data[data_type.byte_size()..]
} else {
data
};
(val, advanced_data)
}
fn as_f64(&self) -> f64 {
*self
}
}
pub trait Apply: Default + Pass {
type Output: Pass + Default;
type Parameters;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool;
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData>;
fn is_valid(val: &Self::Output) -> bool;
}
impl<T: Unpack> Apply for T {
type Output = (T, bool);
type Parameters = Parameters<1>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let val1 = T::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1).0;
if let Some(val1) = val1 {
*dest = (val1, true);
true
} else {
if update_age > parameters.stale_age {
dest.1 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![OldBlockData::from_scalar(val.0.as_f64())]
}
fn is_valid(val: &Self::Output) -> bool {
val.1
}
}
impl<T1: Unpack, T2: Unpack> Apply for (T1, T2) {
type Output = (T1, T2, bool);
type Parameters = Parameters<2>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let (val1, data) = T1::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1);
let (val2, _) = T2::unpack(data, parameters.pack_spec[1].0, parameters.pack_spec[1].1);
if let (Some(val1), Some(val2)) = (val1, val2) {
*dest = (val1, val2, true);
true
} else {
if update_age > parameters.stale_age {
dest.2 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![
OldBlockData::from_scalar(val.0.as_f64()),
OldBlockData::from_scalar(val.1.as_f64()),
]
}
fn is_valid(val: &Self::Output) -> bool {
val.2
}
}
impl<T1: Unpack, T2: Unpack, T3: Unpack> Apply for (T1, T2, T3) {
type Output = (T1, T2, T3, bool);
type Parameters = Parameters<3>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let (val1, data) = T1::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1);
let (val2, data) = T2::unpack(data, parameters.pack_spec[1].0, parameters.pack_spec[1].1);
let (val3, _) = T3::unpack(data, parameters.pack_spec[2].0, parameters.pack_spec[2].1);
if let (Some(val1), Some(val2), Some(val3)) = (val1, val2, val3) {
*dest = (val1, val2, val3, true);
true
} else {
if update_age > parameters.stale_age {
dest.3 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![
OldBlockData::from_scalar(val.0.as_f64()),
OldBlockData::from_scalar(val.1.as_f64()),
OldBlockData::from_scalar(val.2.as_f64()),
]
}
fn is_valid(val: &Self::Output) -> bool {
val.3
}
}
impl<T1: Unpack, T2: Unpack, T3: Unpack, T4: Unpack> Apply for (T1, T2, T3, T4) {
type Output = (T1, T2, T3, T4, bool);
type Parameters = Parameters<4>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let (val1, data) = T1::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1);
let (val2, data) = T2::unpack(data, parameters.pack_spec[1].0, parameters.pack_spec[1].1);
let (val3, data) = T3::unpack(data, parameters.pack_spec[2].0, parameters.pack_spec[2].1);
let (val4, _) = T4::unpack(data, parameters.pack_spec[3].0, parameters.pack_spec[3].1);
if let (Some(val1), Some(val2), Some(val3), Some(val4)) = (val1, val2, val3, val4) {
*dest = (val1, val2, val3, val4, true);
true
} else {
if update_age > parameters.stale_age {
dest.4 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![
OldBlockData::from_scalar(val.0.as_f64()),
OldBlockData::from_scalar(val.1.as_f64()),
OldBlockData::from_scalar(val.2.as_f64()),
OldBlockData::from_scalar(val.3.as_f64()),
]
}
fn is_valid(val: &Self::Output) -> bool {
val.4
}
}
impl<T1: Unpack, T2: Unpack, T3: Unpack, T4: Unpack, T5: Unpack> Apply for (T1, T2, T3, T4, T5) {
type Output = (T1, T2, T3, T4, T5, bool);
type Parameters = Parameters<5>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let (val1, data) = T1::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1);
let (val2, data) = T2::unpack(data, parameters.pack_spec[1].0, parameters.pack_spec[1].1);
let (val3, data) = T3::unpack(data, parameters.pack_spec[2].0, parameters.pack_spec[2].1);
let (val4, data) = T4::unpack(data, parameters.pack_spec[3].0, parameters.pack_spec[3].1);
let (val5, _) = T5::unpack(data, parameters.pack_spec[4].0, parameters.pack_spec[4].1);
if let (Some(val1), Some(val2), Some(val3), Some(val4), Some(val5)) =
(val1, val2, val3, val4, val5)
{
*dest = (val1, val2, val3, val4, val5, true);
true
} else {
if update_age > parameters.stale_age {
dest.5 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![
OldBlockData::from_scalar(val.0.as_f64()),
OldBlockData::from_scalar(val.1.as_f64()),
OldBlockData::from_scalar(val.2.as_f64()),
OldBlockData::from_scalar(val.3.as_f64()),
OldBlockData::from_scalar(val.4.as_f64()),
]
}
fn is_valid(val: &Self::Output) -> bool {
val.5
}
}
impl<T1: Unpack, T2: Unpack, T3: Unpack, T4: Unpack, T5: Unpack, T6: Unpack> Apply
for (T1, T2, T3, T4, T5, T6)
{
type Output = (T1, T2, T3, T4, T5, T6, bool);
type Parameters = Parameters<6>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let (val1, data) = T1::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1);
let (val2, data) = T2::unpack(data, parameters.pack_spec[1].0, parameters.pack_spec[1].1);
let (val3, data) = T3::unpack(data, parameters.pack_spec[2].0, parameters.pack_spec[2].1);
let (val4, data) = T4::unpack(data, parameters.pack_spec[3].0, parameters.pack_spec[3].1);
let (val5, data) = T5::unpack(data, parameters.pack_spec[4].0, parameters.pack_spec[4].1);
let (val6, _) = T6::unpack(data, parameters.pack_spec[5].0, parameters.pack_spec[5].1);
if let (Some(val1), Some(val2), Some(val3), Some(val4), Some(val5), Some(val6)) =
(val1, val2, val3, val4, val5, val6)
{
*dest = (val1, val2, val3, val4, val5, val6, true);
true
} else {
if update_age > parameters.stale_age {
dest.6 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![
OldBlockData::from_scalar(val.0.as_f64()),
OldBlockData::from_scalar(val.1.as_f64()),
OldBlockData::from_scalar(val.2.as_f64()),
OldBlockData::from_scalar(val.3.as_f64()),
OldBlockData::from_scalar(val.4.as_f64()),
OldBlockData::from_scalar(val.5.as_f64()),
]
}
fn is_valid(val: &Self::Output) -> bool {
val.6
}
}
impl<T1: Unpack, T2: Unpack, T3: Unpack, T4: Unpack, T5: Unpack, T6: Unpack, T7: Unpack> Apply
for (T1, T2, T3, T4, T5, T6, T7)
{
type Output = (T1, T2, T3, T4, T5, T6, T7, bool);
type Parameters = Parameters<7>;
fn apply(
dest: &mut Self::Output,
data: PassBy<ByteSliceSignal>,
parameters: &Self::Parameters,
update_age: Duration,
) -> bool {
let (val1, data) = T1::unpack(data, parameters.pack_spec[0].0, parameters.pack_spec[0].1);
let (val2, data) = T2::unpack(data, parameters.pack_spec[1].0, parameters.pack_spec[1].1);
let (val3, data) = T3::unpack(data, parameters.pack_spec[2].0, parameters.pack_spec[2].1);
let (val4, data) = T4::unpack(data, parameters.pack_spec[3].0, parameters.pack_spec[3].1);
let (val5, data) = T5::unpack(data, parameters.pack_spec[4].0, parameters.pack_spec[4].1);
let (val6, data) = T6::unpack(data, parameters.pack_spec[5].0, parameters.pack_spec[5].1);
let (val7, _) = T7::unpack(data, parameters.pack_spec[6].0, parameters.pack_spec[6].1);
if let (
Some(val1),
Some(val2),
Some(val3),
Some(val4),
Some(val5),
Some(val6),
Some(val7),
) = (val1, val2, val3, val4, val5, val6, val7)
{
*dest = (val1, val2, val3, val4, val5, val6, val7, true);
true
} else {
if update_age > parameters.stale_age {
dest.7 = false;
}
false
}
}
fn as_old_block_data(val: &Self::Output) -> Vec<OldBlockData> {
vec![
OldBlockData::from_scalar(val.0.as_f64()),
OldBlockData::from_scalar(val.1.as_f64()),
OldBlockData::from_scalar(val.2.as_f64()),
OldBlockData::from_scalar(val.3.as_f64()),
OldBlockData::from_scalar(val.4.as_f64()),
OldBlockData::from_scalar(val.5.as_f64()),
OldBlockData::from_scalar(val.6.as_f64()),
]
}
fn is_valid(val: &Self::Output) -> bool {
val.7
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core_blocks::bytes_pack_block::{BytesPackBlock, Parameters as PackParameters};
use crate::testing::StubContext;
use approx::assert_relative_eq;
#[test]
fn test_bytes_unpack_1_output() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<f64>::default();
let mut block = BytesUnpackBlock::<f64>::default();
let spec_strings = &["I8:BigEndian"];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = 42.0;
let expected = (42.0, true);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_eq!(unpacked, expected);
let unpacked = block.process(¶meters, &context, &[]);
assert_eq!(unpacked, expected);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &[]);
assert_eq!(unpacked, (42.0, false));
}
#[test]
fn test_bytes_unpack_2_outputs() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<(f64, f64)>::default();
let mut block = BytesUnpackBlock::<(f64, f64)>::default();
let spec_strings = &["I8:BigEndian", "U64:LittleEndian"];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = (-23.0, 43.0);
let expected = (-23.0, 43.0, true);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_eq!(unpacked, expected);
let unpacked = block.process(¶meters, &context, &[]);
assert_eq!(unpacked, expected);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &[]);
assert_eq!(unpacked, (-23.0, 43.0, false));
}
#[test]
fn test_bytes_unpack_3_outputs() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<(f64, f64, f64)>::default();
let mut block = BytesUnpackBlock::<(f64, f64, f64)>::default();
let spec_strings = &["I8:BigEndian", "U64:LittleEndian", "F32:BigEndian"];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = (-23.0, 43.0, 1.234);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert!(unpacked.3);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert!(unpacked.3);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert!(!unpacked.3);
}
#[test]
fn test_bytes_unpack_4_outputs() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<(f64, f64, f64, f64)>::default();
let mut block = BytesUnpackBlock::<(f64, f64, f64, f64)>::default();
let spec_strings = &[
"I8:BigEndian",
"U64:LittleEndian",
"F32:BigEndian",
"F64:LittleEndian",
];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = (-23.0, 43.0, 1.234, 3.1);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert!(unpacked.4);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert!(unpacked.4);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1);
assert!(!unpacked.4);
}
#[test]
fn test_bytes_unpack_5_outputs() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<(f64, f64, f64, f64, f64)>::default();
let mut block = BytesUnpackBlock::<(f64, f64, f64, f64, f64)>::default();
let spec_strings = &[
"I8:BigEndian",
"U64:LittleEndian",
"F32:BigEndian",
"F64:LittleEndian",
"I32:BigEndian",
];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = (-23.0, 43.0, 1.234, 3.1, 42.5);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert!(unpacked.5);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert!(unpacked.5);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert!(!unpacked.5);
}
#[test]
fn test_bytes_unpack_6_outputs() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<(f64, f64, f64, f64, f64, f64)>::default();
let mut block = BytesUnpackBlock::<(f64, f64, f64, f64, f64, f64)>::default();
let spec_strings = &[
"I8:BigEndian",
"U64:LittleEndian",
"F32:BigEndian",
"F64:LittleEndian",
"I32:BigEndian",
"U16:LittleEndian",
];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = (-23.0, 43.0, 1.234, 3.1, 42.5, 9999.0);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert_relative_eq!(unpacked.5, 9999.0_f64);
assert!(unpacked.6);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert_relative_eq!(unpacked.5, 9999.0_f64);
assert!(unpacked.6);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &packed[..15]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert_relative_eq!(unpacked.5, 9999.0_f64);
assert!(!unpacked.6);
let test_data = (1337.0, 12.0, 1994.0, -8.3, 71.92, -15.0);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_relative_eq!(unpacked.0, 127.0_f64); assert_relative_eq!(unpacked.1, 12.0_f64);
assert_relative_eq!(unpacked.2, 1994.0_f64);
assert_relative_eq!(unpacked.3, -8.3_f64);
assert_relative_eq!(unpacked.4, 71.0_f64); assert_relative_eq!(unpacked.5, 0.0_f64); assert!(unpacked.6);
}
#[test]
fn test_bytes_unpack_7_outputs() {
let mut context = StubContext::default();
let mut pack_block = BytesPackBlock::<(f64, f64, f64, f64, f64, f64, f64)>::default();
let mut block = BytesUnpackBlock::<(f64, f64, f64, f64, f64, f64, f64)>::default();
let spec_strings = &[
"I8:BigEndian",
"U64:LittleEndian",
"F32:BigEndian",
"F64:LittleEndian",
"I32:BigEndian",
"U16:LittleEndian",
"F32:LittleEndian",
];
let pack_parameters = PackParameters::new(spec_strings);
let parameters = Parameters::new(spec_strings, 1000.0);
let test_data = (-23.0, 43.0, 1.234, 3.1, 42.5, 9999.0, -7.89);
let packed = pack_block.process(&pack_parameters, &context, test_data);
let unpacked = block.process(¶meters, &context, packed);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert_relative_eq!(unpacked.5, 9999.0_f64);
assert_relative_eq!(unpacked.6, -7.89_f64, epsilon = 0.001);
assert!(unpacked.7);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert_relative_eq!(unpacked.5, 9999.0_f64);
assert_relative_eq!(unpacked.6, -7.89_f64, epsilon = 0.001);
assert!(unpacked.7);
context.time += Duration::from_secs_f64(1.1);
let unpacked = block.process(¶meters, &context, &[]);
assert_relative_eq!(unpacked.0, -23.0_f64);
assert_relative_eq!(unpacked.1, 43.0_f64);
assert_relative_eq!(unpacked.2, 1.234_f64, epsilon = 0.001);
assert_relative_eq!(unpacked.3, 3.1_f64);
assert_relative_eq!(unpacked.4, 42.0_f64);
assert_relative_eq!(unpacked.5, 9999.0_f64);
assert_relative_eq!(unpacked.6, -7.89_f64, epsilon = 0.001);
assert!(!unpacked.7);
}
}