use std::collections::BTreeMap;
use crate::result::CompilerResult;
use crate::test_binary::{SensorAction, SensorInstr};
use crate::test_descriptor::ast::{
SensorBound, SensorBoundNumeric, SensorConstraint, SensorStatement,
};
use crate::test_descriptor::concrete_test::ConcreteTest;
#[derive(PartialEq, Debug)]
pub struct SensorsTimeline {
pub sensor_timelines: BTreeMap<String, SensorTimeline>,
}
#[derive(PartialEq, Debug)]
pub struct SensorTimeline {
pub endpoints: Vec<u32>,
pub range_constraints: Vec<Vec<SensorConstraint>>,
pub endpoint_constraints: Vec<Vec<SensorConstraint>>,
}
impl SensorsTimeline {
pub fn new() -> Self {
SensorsTimeline {
sensor_timelines: BTreeMap::new(),
}
}
pub fn add_sensor_statement(&mut self, statement: &SensorStatement) -> CompilerResult<()> {
let statement_start_time = statement.time.get_start() as u32;
let statement_end_time = statement.time.get_end() as u32;
if statement_start_time == statement_end_time {
self.add_endpoint_constraints(statement_start_time, statement.constraints.clone())
} else {
self.add_range_constraints(
statement_start_time,
statement_end_time,
statement.constraints.clone(),
)
}
}
fn add_range_constraints(
&mut self,
statement_start_time: u32,
statement_end_time: u32,
constraints: Vec<SensorConstraint>,
) -> CompilerResult<()> {
let mut res =
CompilerResult::status_only("Add range sensor constraints to sensor timeline");
for sensor_constraint in &constraints {
if !self.sensor_timelines.contains_key(&sensor_constraint.id) {
self.sensor_timelines
.insert(sensor_constraint.id.clone(), SensorTimeline::new());
}
let sensor = check!(
res,
self.sensor_timelines.get_mut(&sensor_constraint.id),
"Sensor entry in sensor_timelines not found"
);
sensor.add_sensor_range(statement_start_time, statement_end_time, sensor_constraint);
}
res
}
fn add_endpoint_constraints(
&mut self,
statement_time: u32,
constraints: Vec<SensorConstraint>,
) -> CompilerResult<()> {
let mut res =
CompilerResult::status_only("Add endpoint sensor constraints to sensor timeline");
for sensor_constraint in &constraints {
if !self.sensor_timelines.contains_key(&sensor_constraint.id) {
self.sensor_timelines
.insert(sensor_constraint.id.clone(), SensorTimeline::new());
}
let sensor = check!(
res,
self.sensor_timelines.get_mut(&sensor_constraint.id),
"Sensor entry in sensor_timelines not found"
);
sensor.add_sensor_instant(statement_time, sensor_constraint);
}
res
}
pub fn to_instrs(
&mut self,
concrete: &ConcreteTest,
) -> CompilerResult<Vec<(u32, SensorInstr)>> {
let mut res = CompilerResult::new("Convert sensors timeline into a vector of SensorInstr");
let mut sensor_instructions: Vec<(u32, SensorInstr)> = Vec::new();
let mut times: Vec<u32> = Vec::new();
for (_, timeline) in self.sensor_timelines.iter_mut() {
for (idx, constraints_vec) in timeline.endpoint_constraints.iter().enumerate() {
let time = timeline.endpoints[idx];
for constraint in constraints_vec {
let time_inst_tuple = (
time,
check!(
res,
SensorInstr::try_from(concrete, &constraint, SensorAction::is_now_in)
),
);
match times.binary_search(&time) {
Ok(time_idx) => {
times.insert(time_idx, time);
sensor_instructions.insert(time_idx, time_inst_tuple);
}
Err(time_idx) => {
match time_idx >= times.len() {
true => {
times.push(time);
sensor_instructions.push(time_inst_tuple);
}
false => {
times.insert(time_idx, time);
sensor_instructions.insert(time_idx, time_inst_tuple);
}
}
}
};
}
}
for (idx, constraints_vec) in timeline.range_constraints.iter().enumerate() {
for constraint in constraints_vec {
let start_time = timeline.endpoints[idx];
let stop_time = timeline.endpoints[idx + 1];
let start_inst_tuple = (
start_time,
check!(
res,
SensorInstr::try_from(
concrete,
constraint,
SensorAction::start_check_in
)
),
);
let stop_inst_tuple = (
stop_time,
check!(
res,
SensorInstr::try_from(concrete, constraint, SensorAction::stop_check)
),
);
match times.binary_search(&start_time) {
Ok(start_idx) => {
times.insert(start_idx, start_time);
sensor_instructions.insert(start_idx, start_inst_tuple);
}
Err(start_idx) => {
match start_idx >= times.len() {
true => {
times.push(start_time);
sensor_instructions.push(start_inst_tuple);
}
false => {
times.insert(start_idx, start_time);
sensor_instructions.insert(start_idx, start_inst_tuple);
}
}
}
};
match times.binary_search(&stop_time) {
Ok(stop_idx) => {
times.insert(stop_idx, stop_time);
sensor_instructions.insert(stop_idx, stop_inst_tuple);
}
Err(stop_idx) => {
match stop_idx >= times.len() {
true => {
times.push(stop_time);
sensor_instructions.push(stop_inst_tuple);
}
false => {
times.insert(stop_idx, stop_time);
sensor_instructions.insert(stop_idx, stop_inst_tuple);
}
}
}
};
}
}
}
res.with_value(sensor_instructions)
}
}
impl SensorTimeline {
pub fn new() -> Self {
SensorTimeline {
endpoints: vec![0, std::u32::MAX],
range_constraints: vec![Vec::new()],
endpoint_constraints: vec![Vec::new(), Vec::new()],
}
}
pub fn add_sensor_range(
&mut self,
statement_start_time: u32,
statement_end_time: u32,
constraint: &SensorConstraint,
) -> CompilerResult<()> {
let res = CompilerResult::status_only("Add range sensor constraint to sensor timeline");
let range_start_idx = match self.endpoints.binary_search(&statement_start_time) {
Ok(exact_idx) => exact_idx,
Err(one_above_idx) => {
self.split_range(one_above_idx, statement_start_time);
one_above_idx
}
};
let range_end_idx = match self.endpoints.binary_search(&statement_end_time) {
Ok(exact_idx) => exact_idx,
Err(one_above_idx) => {
self.split_range(one_above_idx, statement_end_time);
one_above_idx
}
};
for idx in range_start_idx..range_end_idx {
self.range_constraints[idx].push(constraint.clone());
}
res
}
fn add_sensor_instant(
&mut self,
statement_time: u32,
constraint: &SensorConstraint,
) -> CompilerResult<()> {
let res = CompilerResult::status_only("Add instant sensor constraints to sensor timeline");
let range_start_idx = match self.endpoints.binary_search(&statement_time) {
Ok(exact_idx) => exact_idx,
Err(one_above_idx) => {
self.split_range(one_above_idx, statement_time);
one_above_idx
}
};
self.endpoint_constraints[range_start_idx].push(constraint.clone());
res
}
fn split_range(&mut self, idx_to_split: usize, time_of_split: u32) {
self.endpoints.insert(idx_to_split, time_of_split);
self.range_constraints.insert(
idx_to_split,
self.range_constraints[idx_to_split - 1].clone(),
);
self.endpoint_constraints.insert(idx_to_split, Vec::new())
}
pub fn reduce(&mut self) -> CompilerResult<()> {
let mut res = CompilerResult::status_only("Reduce sensor constraints throughout timeline");
let mut new_range_constraints: Vec<Vec<SensorConstraint>> = Vec::new();
let mut new_endpoint_constraints: Vec<Vec<SensorConstraint>> = Vec::new();
for timeline_idx in 0..(self.endpoints.len()) {
let mut reduced_constraint = self.endpoint_constraints[timeline_idx].get(0).cloned();
for constraint_idx in 1..(self.endpoint_constraints[timeline_idx].len()) {
let cur_constraint = self.endpoint_constraints[timeline_idx]
.get(constraint_idx)
.cloned();
reduced_constraint = Some(check!(
res,
self.reduce_two_constraints(reduced_constraint, cur_constraint)
));
}
if let Some(mut new_endpoint_constraint) = reduced_constraint {
if let Some(range_constraints) = self.range_constraints.get(timeline_idx) {
for constraint_idx in 0..range_constraints.len() {
let cur_constraint = range_constraints.get(constraint_idx).cloned();
new_endpoint_constraint = check!(
res,
self.reduce_two_constraints(
Some(new_endpoint_constraint),
cur_constraint
)
);
}
}
new_endpoint_constraints.push(vec![new_endpoint_constraint]);
}
else {
new_endpoint_constraints.push(vec![]);
}
if timeline_idx != self.endpoints.len() - 1 {
let mut reduced_constraint = self.range_constraints[timeline_idx].get(0).cloned();
for constraint_idx in 1..(self.range_constraints[timeline_idx].len()) {
let cur_constraint = self.range_constraints[timeline_idx]
.get(constraint_idx)
.cloned();
reduced_constraint = Some(check!(
res,
self.reduce_two_constraints(reduced_constraint, cur_constraint)
));
}
if let Some(new_range_constraint) = reduced_constraint {
new_range_constraints.push(vec![new_range_constraint]);
} else {
new_range_constraints.push(vec![]);
}
}
}
self.range_constraints = new_range_constraints;
self.endpoint_constraints = new_endpoint_constraints;
res
}
fn reduce_two_constraints(
&self,
constraint_opt1: Option<SensorConstraint>,
constraint_opt2: Option<SensorConstraint>,
) -> CompilerResult<SensorConstraint> {
let mut res = CompilerResult::new("Reducing two sensor constraints");
match (constraint_opt1, constraint_opt2) {
(None, None) => {
res.error("Tried to reduce an empty SensorConstraint value(s).");
res
}
(Some(constraint1), None) => {
res.set_value(constraint1);
res
}
(None, Some(constraint2)) => {
res.set_value(constraint2);
res
}
(Some(constraint1), Some(constraint2)) => {
if !constraint1.abort.eq(&constraint2.abort) {
res.error((constraint1.metadata, "See next message "));
res.error((
constraint2.metadata,
"Tried to reduce two sensor constraints with differing aborts",
));
}
let numeric_bound1: &SensorBoundNumeric;
let numeric_bound2: &SensorBoundNumeric;
match (&constraint1.sensor_bound, &constraint2.sensor_bound) {
(SensorBound::Numeric(bound1), SensorBound::Numeric(bound2)) => {
numeric_bound1 = bound1;
numeric_bound2 = bound2;
}
_ => {
res.error((constraint1.metadata, "See next message"));
res.error((
constraint2.metadata,
"Tried to reduce two non-numeric constraints",
));
return res;
}
}
let mut new_constraint = (constraint1).clone();
let mut new_left: f64;
let mut new_right: f64;
new_left = numeric_bound1.left;
if numeric_bound2.left > numeric_bound1.left {
new_left = numeric_bound2.left;
}
new_right = numeric_bound1.right;
if numeric_bound2.right < numeric_bound1.right {
new_right = numeric_bound2.right;
}
if new_right <= new_left {
res.error((constraint1.metadata, "See next message"));
res.error((
constraint2.metadata,
"No overlap between two sensor constraints",
));
return res;
}
let mut new_bound = numeric_bound1.clone();
new_bound.left = new_left;
new_bound.right = new_right;
new_constraint.sensor_bound = SensorBound::Numeric(new_bound);
res.set_value(new_constraint);
res
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::result::diagnostic::Location;
use crate::result::Status;
use crate::test_descriptor::ast::SensorTime::{Instant, Interval};
use crate::test_descriptor::ast::{SensorBound, SensorBoundNumeric, SensorConstraint};
#[test]
fn construct_sensor_timeline() {
let empty_location = Location::from_raw(0, 0, 0);
let constraint1 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 20.0,
right: 30.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint2 = SensorConstraint {
id: "2".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 30.0,
right: 40.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let endpoint_constraints: Vec<Vec<SensorConstraint>> = vec![vec![], vec![], vec![]];
let range_constraints_one: Vec<Vec<SensorConstraint>> =
vec![vec![constraint1.clone()], vec![]];
let range_constraints_two: Vec<Vec<SensorConstraint>> =
vec![vec![constraint2.clone()], vec![]];
let statement1 = SensorStatement {
time: Interval { start: 0, end: 1 },
constraints: vec![constraint1.clone()],
abort: "abort1".to_string(),
metadata: empty_location,
};
let statement2 = SensorStatement {
time: Interval { start: 0, end: 2 },
constraints: vec![constraint2.clone()],
abort: "abort1".to_string(),
metadata: empty_location,
};
let endpoints_one: Vec<u32> = vec![0, 1, u32::MAX];
let endpoints_two: Vec<u32> = vec![0, 2, u32::MAX];
let simple_timeline_one = SensorTimeline {
endpoints: endpoints_one,
endpoint_constraints: endpoint_constraints.clone(),
range_constraints: range_constraints_one,
};
let simple_timeline_two = SensorTimeline {
endpoints: endpoints_two,
endpoint_constraints,
range_constraints: range_constraints_two,
};
let mut timeline_map = BTreeMap::new();
timeline_map.insert("1".to_string(), simple_timeline_one);
timeline_map.insert("2".to_string(), simple_timeline_two);
let simple_timeline_wrap = SensorsTimeline {
sensor_timelines: timeline_map,
};
let mut timeline_sensor = SensorsTimeline::new();
timeline_sensor.add_sensor_statement(&statement1);
timeline_sensor.add_sensor_statement(&statement2);
assert_eq!(timeline_sensor, simple_timeline_wrap);
}
#[test]
fn reduce_sensor_timeline() {
let empty_location = Location::from_raw(0, 0, 0);
let constraint1 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 10.0,
right: 30.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint2 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 20.0,
right: 40.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint3 = SensorConstraint {
id: "2".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 5.0,
right: 10.0,
unit: "c".to_string(),
}),
abort: "abort2".to_string(),
metadata: empty_location,
};
let constraint4 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 50.0,
right: 60.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint_reduced = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 20.0,
right: 30.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let endpoint_constraints_reduced: Vec<Vec<SensorConstraint>> =
vec![vec![], vec![], vec![], vec![]];
let endpoint_constraints_two: Vec<Vec<SensorConstraint>> = vec![vec![], vec![], vec![]];
let range_constraints_reduced: Vec<Vec<SensorConstraint>> = vec![
vec![constraint_reduced.clone()],
vec![constraint2.clone()],
vec![],
];
let range_constraints_two: Vec<Vec<SensorConstraint>> =
vec![vec![constraint3.clone()], vec![]];
let statement1 = SensorStatement {
time: Interval { start: 0, end: 1 },
constraints: vec![constraint1.clone()],
abort: "abort1".to_string(),
metadata: empty_location,
};
let statement2 = SensorStatement {
time: Interval { start: 0, end: 2 },
constraints: vec![constraint2.clone()],
abort: "abort1".to_string(),
metadata: empty_location,
};
let statement3 = SensorStatement {
time: Interval { start: 0, end: 3 },
constraints: vec![constraint3.clone()],
abort: "abort2".to_string(),
metadata: empty_location,
};
let statement4 = SensorStatement {
time: Instant { time: 1 },
constraints: vec![constraint4.clone()],
abort: "abort1".to_string(),
metadata: empty_location,
};
let endpoints_one: Vec<u32> = vec![0, 1, 2, u32::MAX];
let endpoints_two: Vec<u32> = vec![0, 3, u32::MAX];
let simple_timeline_reduced = SensorTimeline {
endpoints: endpoints_one,
endpoint_constraints: endpoint_constraints_reduced,
range_constraints: range_constraints_reduced,
};
let simple_timeline_two = SensorTimeline {
endpoints: endpoints_two,
endpoint_constraints: endpoint_constraints_two,
range_constraints: range_constraints_two,
};
let mut simple_timeline_map = BTreeMap::new();
simple_timeline_map.insert("1".to_string(), simple_timeline_reduced);
simple_timeline_map.insert("2".to_string(), simple_timeline_two);
let simple_timeline_wrap = SensorsTimeline {
sensor_timelines: simple_timeline_map,
};
let mut timeline_sensor = SensorsTimeline::new();
timeline_sensor.add_sensor_statement(&statement1);
timeline_sensor.add_sensor_statement(&statement2);
timeline_sensor.add_sensor_statement(&statement3);
for (_, timeline) in timeline_sensor.sensor_timelines.iter_mut() {
timeline.reduce();
}
assert_eq!(simple_timeline_wrap, timeline_sensor);
timeline_sensor.add_sensor_statement(&statement4);
let res_fail = timeline_sensor
.sensor_timelines
.get_mut("1")
.unwrap()
.reduce();
assert_eq!(res_fail.get_status(), Status::Failed);
}
#[test]
fn reduce_constraints() {
let empty_location = Location::from_raw(0, 0, 0);
let constraint1 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 10.0,
right: 30.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint2 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 20.0,
right: 40.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint_no_overlap = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 50.0,
right: 51.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let constraint3 = SensorConstraint {
id: "1".to_string(),
sensor_bound: SensorBound::Numeric(SensorBoundNumeric {
left: 20.0,
right: 30.0,
unit: "c".to_string(),
}),
abort: "abort1".to_string(),
metadata: empty_location,
};
let timeline: SensorTimeline = SensorTimeline {
endpoints: vec![],
range_constraints: vec![],
endpoint_constraints: vec![],
};
let res = timeline.reduce_two_constraints(Some(constraint1.clone()), Some(constraint2));
let res_fail =
timeline.reduce_two_constraints(Some(constraint1), Some(constraint_no_overlap));
assert_eq!(constraint3, res.to_option().unwrap());
assert_eq!(res_fail.get_status(), Status::Failed);
}
}