use std::cmp;
use crate::result::CompilerResult;
use crate::test_binary::{RelayInstr, Segment, SensorAction, SensorInstr, Test};
use crate::test_descriptor::ast::Statement;
use crate::test_descriptor::concrete_test::ConcreteTest;
use self::relays::RelaysTimeline;
pub use self::sensors::SensorsTimeline;
mod relays;
pub mod sensors;
#[derive(Debug)]
pub struct Timeline {
relay_instructions: Vec<(u32, RelayInstr)>,
sensor_instructions: Vec<(u32, SensorInstr)>,
pub timeline_sensor: SensorsTimeline,
}
impl Timeline {
fn try_new(concrete: &ConcreteTest, statements: &[Statement]) -> CompilerResult<Timeline> {
let mut res = CompilerResult::new("Construct timeline from statements");
let mut timeline_sensor = SensorsTimeline::new();
let mut timeline_relay = RelaysTimeline::new();
res.require(validate_statement_ordering(statements));
res.require(add_relay_sensor_statements_to_timeline(
&mut timeline_sensor,
&mut timeline_relay,
statements,
));
for (_, timeline) in timeline_sensor.sensor_timelines.iter_mut() {
res.require(timeline.reduce());
}
res.require(timeline_relay.validate(concrete));
let timeline = check!(
res,
combine_relay_sensor_timelines(concrete, timeline_sensor, timeline_relay)
);
res.with_value(timeline)
}
}
fn validate_statement_ordering(statements: &[Statement]) -> CompilerResult<()> {
let mut res = CompilerResult::status_only("Validate ordering of TDF statements");
let mut last_relay_time: u128 = 0;
let mut last_sensor_time: u128 = 0;
for statement in statements {
match statement {
Statement::Section(section_statement) => {
let time = section_statement.time.start;
if time < last_sensor_time || time < last_relay_time {
res.error((
section_statement.metadata,
"Section statement occurs after other statements with later start time",
));
} else {
last_sensor_time = time + section_statement.time.duration;
last_relay_time = time + section_statement.time.duration;
res.require(validate_statement_ordering(§ion_statement.statements));
}
}
Statement::Sensor(sensor_statement) => {
if sensor_statement.time.get_start() < last_sensor_time {
res.error((sensor_statement.metadata, "Sensor statement occurs after other sensor statement with later start time"));
} else {
last_sensor_time = sensor_statement.time.get_start();
}
}
Statement::Relay(relay_statement) => {
if relay_statement.time < last_relay_time {
res.error((
relay_statement.metadata,
"Relay statement occurs after other relay statement with later time",
));
} else {
last_relay_time = relay_statement.time;
}
}
}
}
res
}
fn add_relay_sensor_statements_to_timeline(
timeline_sensor: &mut SensorsTimeline,
timeline_relay: &mut RelaysTimeline,
statements: &[Statement],
) -> CompilerResult<()> {
let mut res =
CompilerResult::status_only("Add vector of relay and sensor statements to their timelines");
for statement in statements {
match statement {
Statement::Section(section_statement) => {
let section_statements = check!(res, section_statement.to_absolute());
res.require(add_relay_sensor_statements_to_timeline(
timeline_sensor,
timeline_relay,
§ion_statements,
));
}
Statement::Sensor(sensor_statement) => {
res.require(timeline_sensor.add_sensor_statement(&sensor_statement));
}
Statement::Relay(relay_statement) => {
res.require(timeline_relay.add_relay_statement(&relay_statement));
}
}
}
res
}
fn combine_relay_sensor_timelines(
concrete: &ConcreteTest,
mut timeline_sensor: SensorsTimeline,
timeline_relay: RelaysTimeline,
) -> CompilerResult<Timeline> {
let mut res =
CompilerResult::new("Combine sensor and relay timelines into universal test timeline");
let relay_instructions: Vec<(u32, RelayInstr)> =
check!(res, timeline_relay.to_instrs(concrete));
let sensor_instructions: Vec<(u32, SensorInstr)> =
check!(res, timeline_sensor.to_instrs(concrete));
let timeline = Timeline {
relay_instructions,
sensor_instructions,
timeline_sensor,
};
res.with_value(timeline)
}
fn segments_from_timeline(
timeline: Timeline,
concrete: &ConcreteTest,
) -> CompilerResult<Vec<Segment>> {
let mut res = CompilerResult::new("Construct segments from given timeline");
let mut segments: Vec<Segment> = Vec::new();
let mut absolute_time: u32 = 0;
let mut sensor_instructions = timeline.sensor_instructions.into_iter().peekable();
let mut relay_instructions = timeline.relay_instructions.into_iter().peekable();
while sensor_instructions.peek().is_some() || relay_instructions.peek().is_some() {
let mut time1 = u32::MAX;
let mut time2 = u32::MAX;
if let Some((sensor_time, _)) = sensor_instructions.peek() {
time1 = *sensor_time;
}
if let Some((relay_time, _)) = relay_instructions.peek() {
time2 = *relay_time;
}
let absolute_time_new = cmp::min(time1, time2);
let relative_time = absolute_time_new - absolute_time;
absolute_time = absolute_time_new;
let mut segment: Segment = check!(res, Segment::try_new(relative_time));
while let Some((_, next_sensor)) =
sensor_instructions.next_if(|(next_time, _)| *next_time == absolute_time)
{
if next_sensor.get_action() == SensorAction::stop_check {
segment.sensor_instructions.insert(0, next_sensor);
} else {
segment.sensor_instructions.push(next_sensor);
}
}
while let Some((_, next_relay)) =
relay_instructions.next_if(|(next_time, _)| *next_time == absolute_time)
{
segment.relay_instructions.push(next_relay);
}
let mut segment_res = CompilerResult::status_only(format!(
"Construct segment at absolute time {}ms",
absolute_time
));
segment_res.require(segment.check_segment_length(&concrete.environment.config));
check!(res, segment_res);
if !segment.sensor_instructions.is_empty() || !segment.relay_instructions.is_empty() {
segments.push(segment);
}
}
res.with_value(segments)
}
pub fn construct_test(
test_body_timeline: Timeline,
abort_timelines: Vec<Timeline>,
concrete_test: &ConcreteTest,
) -> CompilerResult<Test> {
let mut res = CompilerResult::new("Construct test timeline");
let mut test = Test::new();
for (idx, abort) in abort_timelines.into_iter().enumerate() {
test.aborts[idx].segments = check!(res, segments_from_timeline(abort, &concrete_test));
}
test.body.segments = check!(
res,
segments_from_timeline(test_body_timeline, &concrete_test)
);
res.with_value(test)
}
pub fn construct_timelines(
concrete_test: &ConcreteTest,
) -> CompilerResult<(Timeline, Vec<Timeline>)> {
let mut res = CompilerResult::new("Construct timelines for test body and aborts");
let test_body_timeline = check!(
res,
Timeline::try_new(&concrete_test, &concrete_test.test.test_body.statements)
);
let mut abort_timelines: Vec<Timeline> = Vec::new();
for idx in 0..concrete_test.test.aborts.len() {
abort_timelines.push(check!(
res,
Timeline::try_new(&concrete_test, &concrete_test.test.aborts[idx].statements)
));
}
res.with_value((test_body_timeline, abort_timelines))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::result::diagnostic::Location;
use crate::result::Status;
use crate::test_descriptor::ast::{
RelayOp, RelayStatement, SensorBound, SensorBoundNumeric, SensorConstraint,
SensorStatement, SensorTime,
};
#[test]
fn enforce_sensor_statement_ordering() {
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 constraints = vec![constraint1];
let instant_time1 = SensorTime::Instant { time: 1 };
let instant_time2 = SensorTime::Instant { time: 2 };
let instant_time3 = SensorTime::Instant { time: 3 };
let interval_time1 = SensorTime::Interval { start: 1, end: 5 };
let interval_time2 = SensorTime::Interval { start: 2, end: 5 };
let interval_time3 = SensorTime::Interval { start: 3, end: 5 };
let statement_instant_1 = Statement::Sensor(SensorStatement {
time: instant_time1,
constraints: constraints.clone(),
abort: "abort1".to_string(),
metadata: empty_location,
});
let statement_instant_2 = Statement::Sensor(SensorStatement {
time: instant_time2,
constraints: constraints.clone(),
abort: "abort1".to_string(),
metadata: empty_location,
});
let statement_instant_3 = Statement::Sensor(SensorStatement {
time: instant_time3,
constraints: constraints.clone(),
abort: "abort1".to_string(),
metadata: empty_location,
});
let statement_interval_1 = Statement::Sensor(SensorStatement {
time: interval_time1,
constraints: constraints.clone(),
abort: "abort1".to_string(),
metadata: empty_location,
});
let statement_interval_2 = Statement::Sensor(SensorStatement {
time: interval_time2,
constraints: constraints.clone(),
abort: "abort1".to_string(),
metadata: empty_location,
});
let statement_interval_3 = Statement::Sensor(SensorStatement {
time: interval_time3,
constraints: constraints.clone(),
abort: "abort1".to_string(),
metadata: empty_location,
});
let statements = vec![
statement_instant_2.clone(),
statement_instant_2.clone(),
statement_interval_2.clone(),
statement_instant_3.clone(),
statement_interval_3.clone(),
];
assert_ne!(
validate_statement_ordering(&statements).get_status(),
Status::Failed
);
let statements = vec![
statement_instant_2.clone(),
statement_instant_2.clone(),
statement_interval_2.clone(),
statement_instant_3.clone(),
statement_interval_3.clone(),
statement_instant_1.clone(),
];
assert_eq!(
validate_statement_ordering(&statements).get_status(),
Status::Failed
);
let statements = vec![
statement_instant_2.clone(),
statement_instant_2.clone(),
statement_interval_2.clone(),
statement_instant_3.clone(),
statement_interval_3.clone(),
statement_interval_1.clone(),
];
assert_eq!(
validate_statement_ordering(&statements).get_status(),
Status::Failed
);
}
#[test]
fn enforce_relay_statement_ordering() {
let empty_location = Location::from_raw(0, 0, 0);
let statement_1 = Statement::Relay(RelayStatement {
time: 1,
id: "relay1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
});
let statement_2 = Statement::Relay(RelayStatement {
time: 2,
id: "relay1".to_string(),
op: RelayOp::Set,
metadata: empty_location,
});
let statement_3 = Statement::Relay(RelayStatement {
time: 3,
id: "relay1".to_string(),
op: RelayOp::Unset,
metadata: empty_location,
});
let statements = &vec![
statement_2.clone(),
statement_2.clone(),
statement_3.clone(),
];
assert_ne!(
validate_statement_ordering(&statements).get_status(),
Status::Failed
);
let statements = &vec![statement_2.clone(), statement_2, statement_3, statement_1];
assert_eq!(
validate_statement_ordering(&statements).get_status(),
Status::Failed
);
}
#[test]
fn combine_timelines() {
}
}