Theme: compose reactors with ports, connections, parameters, and state. Estimated time: 12 minutes. Prerequisites: Lab 1. Files: SensorPipeline.lf
- 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 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/SensorPipelineCheckpoint: 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.
In Sensor, declare the state variable raw (starting at 0) and
increment it in the reaction to sample.
- Declare the output port
readinginSensorand make the sample reaction produceraw * gainon it. Remember to add-> readingto the reaction signature: a reaction may only write ports it declares as effects. - Declare the input port
readinginDisplay, and add a reaction triggered by it that counts it inreceivedand 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.
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.
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.
Change the target property timeout to 2 s. Predict the count, run, and
check.
Checkpoint: 5 readings (0, 500, 1000, 1500, 2000 ms).
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.
- 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?
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.