1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
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;
// TODO There should be a better way than making this public
pub mod sensors;

/// Combined sensor and relay timeline
/// Relay operations only use one endpoint
/// Sensor constraints use interval between two endpoints
#[derive(Debug)]
pub struct Timeline {
    relay_instructions: Vec<(u32, RelayInstr)>,
    sensor_instructions: Vec<(u32, SensorInstr)>,
    // A field with the sensor timeline before it was converted into a single vector of instructions
    // This field is used to emit sensor bounds in the mission_control crate
    pub timeline_sensor: SensorsTimeline,
}

impl Timeline {
    /// Construct timeline of sensor and relay statements
    // TODO combine with construct_relay_sensor_timelines into just one function
    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,
        ));
        // Reduce sensor timeline to combine simultaneous sensor constraints
        for (_, timeline) in timeline_sensor.sensor_timelines.iter_mut() {
            res.require(timeline.reduce());
        }
        // Validate relay timeline
        res.require(timeline_relay.validate(concrete));

        let timeline = check!(
            res,
            combine_relay_sensor_timelines(concrete, timeline_sensor, timeline_relay)
        );
        res.with_value(timeline)
    }
}

/// Validate all statements throughout the test.tdf increase in time
// TODO consider removing this from timeline code
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;

    // validate ordering of sensor and relay statements
    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 {
                    // Update both times because sections prevent all
                    // future statements from going backwards in time
                    last_sensor_time = time + section_statement.time.duration;
                    last_relay_time = time + section_statement.time.duration;

                    // Validate ordering of statements within section
                    res.require(validate_statement_ordering(&section_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 {
                    // Update just sensor time because sensors only
                    // restrict other sensor statements
                    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 {
                    // Update just relay time because sensors only
                    // restrict other relay statements
                    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");
    // iterate through ast
    // for each statement call the corresponding add function
    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,
                    &section_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
}

/// Combine relay and sensor timelines into one unified timeline consisting of
/// concrete relay and sensor instructions ready to be emitted as binary segments
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");

    // Convert both timelines into a vector of time, instruction tuples
    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)
}

/// Convert test timeline into a vector of segments
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 let
    // possibly peekable
    // Perform a merge-sort like algorithm to combine instructions at the same time into one segment
    //TODODOTOO
    while sensor_instructions.peek().is_some() || relay_instructions.peek().is_some() {
        // Update absolute time to smallest time remaining in either instruction vectors
        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));
        // Add every sensor instruction with the current absolute time
        while let Some((_, next_sensor)) =
            sensor_instructions.next_if(|(next_time, _)| *next_time == absolute_time)
        {
            // Put sensor stop checks at the beginning of each segment
            if next_sensor.get_action() == SensorAction::stop_check {
                segment.sensor_instructions.insert(0, next_sensor);
            } else {
                segment.sensor_instructions.push(next_sensor);
            }
        }
        // Add every relay instruction with the current absolute time
        while let Some((_, next_relay)) =
            relay_instructions.next_if(|(next_time, _)| *next_time == absolute_time)
        {
            segment.relay_instructions.push(next_relay);
        }
        // Little bit of a hack to improve the error reporting
        // construct a new compiler result for each segment
        // that labels the message with the absolute time.
        // This way if the segment length is too long, we can
        // tell the user exactly what time the segment was for.
        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);

        // Don't append an empty segment
        if !segment.sensor_instructions.is_empty() || !segment.relay_instructions.is_empty() {
            segments.push(segment);
        }
    }
    res.with_value(segments)
}

/// Construct test containing instruction segments
/// with relative times for outputting as a TBF
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");

    //Create test struct and convert timelines into test
    let mut test = Test::new();

    //Add aborts to test
    for (idx, abort) in abort_timelines.into_iter().enumerate() {
        test.aborts[idx].segments = check!(res, segments_from_timeline(abort, &concrete_test));
    }

    //Add test body to test
    test.body.segments = check!(
        res,
        segments_from_timeline(test_body_timeline, &concrete_test)
    );

    res.with_value(test)
}

/// Construct timelines for test body and aborts
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() {
        /*
        Need to test combining relay and sensor timelines in various ways
        Testing the function: combine_relay_sensor_timelines

        Unfortunately this function is also responsible for converting statements to instructions
        which requires a concrete test to exist. Concrete tests are complicated to create.

        */
    }
}