Skip to content

Latest commit

 

History

History
116 lines (83 loc) · 3.93 KB

File metadata and controls

116 lines (83 loc) · 3.93 KB

Lab 2: Building a reactor pipeline

Theme: compose reactors with ports, connections, parameters, and state. Estimated time: 12 minutes. Prerequisites: Lab 1. Files: SensorPipeline.lf

Learning objectives

  • Declare input and output ports and connect reactors.
  • Use parameters (set at instantiation) and state variables (changed at run time).
  • Declare a reaction's triggers and effects.
  • Predict how timer periods and the program timeout shape the output.

The plan

The starter file defines a Sensor and a Display, but they are not finished and not yet connected. You will complete the Sensor's behavior, give the Display something to show, wire the two together, and then play with the timing. The starter compiles as is; follow the TODO markers.

Syntax you will need (all from Lab 1 or shown here):

  • State variable: state raw = 0

  • Output port: output reading, input port: input reading

  • A reaction that writes a port lists it as an effect after ->: reaction(sample) -> reading {= reading.set(42) =}

  • Instantiation and connection in the main reactor:

    a = new SomeReactor()
    b = new OtherReactor()
    a.someOutput -> b.someInput
    

Compile and run after every exercise:

lfc src/lab2/SensorPipeline.lf
bin/SensorPipeline

Exercise 1: run the starter

Checkpoint: the program compiles and exits immediately (the main reactor is still empty, so there is nothing to run; lfc may warn about that). That is expected.

Exercise 2: give the Sensor state

In Sensor, declare the state variable raw (starting at 0) and increment it in the reaction to sample.

Exercise 3: add the ports

  1. Declare the output port reading in Sensor and make the sample reaction produce raw * gain on it. Remember to add -> reading to the reaction signature: a reaction may only write ports it declares as effects.
  2. Declare the input port reading in Display, and add a reaction triggered by it that counts it in received and prints the value with the elapsed logical time in milliseconds (lf.time.logical_elapsed() // MSEC(1)).

Checkpoint: everything still compiles. Running still prints nothing interesting; nothing is connected yet.

Exercise 4: compose the pipeline

In the main reactor, instantiate Sensor as s and Display as d, and connect s.reading -> d.reading.

Checkpoint: running now prints a reading every second: values 3, 6, 9, 12, 15 at logical times 0 through 4000 ms, then Display received 5 readings in total.

Exercise 5: change the period, predict first

Before touching the code: with s = new Sensor(period = 500 ms) and the timeout still 4 s, how many readings will the Display report? Write your number down, then make the change and run.

Checkpoint: 9 readings (logical times 0, 500, ..., 4000 ms). Parameters are per-instance: the Sensor class did not change, only this instance's configuration.

Exercise 6: change the timeout, predict first

Change the target property timeout to 2 s. Predict the count, run, and check.

Checkpoint: 5 readings (0, 500, 1000, 1500, 2000 ms).

Exercise 7: look at the structure

Open the diagram in VS Code (as in Lab 1). You should see two boxes with a connection from the Sensor's output port to the Display's input port, the timer inside the Sensor, and the reactions in each reactor.

Reflection

  • Why does LF distinguish parameters from state variables, when both are "variables of the reactor"?
  • The Display prints the same logical times as the Sensor produced. What does that tell you about when a connected reaction runs relative to the event that triggered it?

Optional challenge

Add a second Display instance and connect the same Sensor output to both (s.reading -> d2.reading as a second connection statement). One output port can feed any number of inputs.

Next: Lab 3 looks closely at time itself: tags, delays, microsteps, and lag.