Skip to content

Commit

Permalink
resolve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
housengw committed Jan 25, 2022
2 parents dc25e21 + 2d7e975 commit f7df745
Show file tree
Hide file tree
Showing 87 changed files with 8,021 additions and 4,805 deletions.
4 changes: 4 additions & 0 deletions .github/scripts/test-lfc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ bin/lfc --federated --rti rti test/C/src/Minimal.lf
# -h,--help Display this information.
bin/lfc --help

# -l, --lint Enable linting during build.
bin/lfc -l test/Python/src/Minimal.lf
bin/lfc --lint test/Python/src/Minimal.lf

# -n,--no-compile Do not invoke target compiler.
bin/lfc -n test/C/src/Minimal.lf
bin/lfc --no-compile test/C/src/Minimal.lf
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/lsp-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ jobs:
uses: actions/[email protected]
with:
java-version: 11
- name: Check out lingua-franca repository
uses: actions/checkout@v2
with:
repository: lf-lang/lingua-franca
submodules: true
ref: ${{ inputs.compiler-ref }}
- name: Setup Node.js environment
uses: actions/[email protected]
- name: Install pnpm
Expand All @@ -61,6 +55,12 @@ jobs:
vcpkgDirectory: ${{ github.workspace }}/vcpkg/
vcpkgTriplet: x64-windows-static
if: ${{ runner.os == 'Windows' }}
- name: Check out lingua-franca repository
uses: actions/checkout@v2
with:
repository: lf-lang/lingua-franca
submodules: true
ref: ${{ inputs.compiler-ref }}
- name: Run language server Python tests without PyLint
run: ./gradlew test --tests org.lflang.tests.lsp.LspTests.pythonSyntaxOnlyValidationTest
- name: Report to CodeCov
Expand Down
2 changes: 2 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Version 0.1.0-beta-SNAPSHOT
- LF programs with the TypeScript target can now be compiled on Windows (#850).
- In the VS Code extension, generated code is validated when an LF file is saved for all targets except C (#828). Generated C code is only validated when it is fully compiled.

## Language

Expand Down
200 changes: 200 additions & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* Example of a basic digital twin setup, with two federates
* maintaining a shared state "lock state".
*
* For run instructions, see README.md in the same directory.
*
* @author Hou Seng Wong ([email protected])
*/

target Python {
docker: true,
files: ["../utils.py"]
};

preamble {=
import curses
import threading
from utils import Logger, Window
import enum

class LockStates(enum.Enum):
Locked = 0
DriverUnlocked = 1
AllUnlocked = 2

class LockStateNames(enum.Enum):
Locked = "Locked"
DriverUnlocked = "Driver's door unlocked"
AllUnlocked = "All doors unlocked"
=}

/**
* A key fob that detects "lock" and "unlock" key presses,
* and sends and receives lock state to and from other key fobs.
*/
reactor DoubleUnlockKeyFob(auto_lock_duration(5)) {
/* logger / window related state variables */
state logger({=None=});
state window({=None=});
state main_message_begins(0);

/* KeyFob related state variables */
state lock_state(0);
state listener({=None=});
state auto_lock_counter;

/* I/O ports and actions */
input get_lock_action;
input get_lock_press_from_tester;
output send_lock_action;
physical action press_lock;
physical action press_unlock;
logical action handle_press_lock;
logical action handle_press_unlock;
logical action do_lock;

/* Autolock timer */
timer autolock_timer(0, 100 msec);

preamble {=
def lock_state_str(self, lock_state):
if lock_state == LockStates.Locked:
return LockStateNames.Locked.value
elif lock_state == LockStates.DriverUnlocked:
return LockStateNames.DriverUnlocked.value
elif lock_state == LockStates.AllUnlocked:
return LockStateNames.AllUnlocked.value
else:
return "ERROR: Lock state is invalid"

def print_lock_state(self):
self.window.change_line(self.main_message_begins, f"Lock State: {self.lock_state_str(self.lock_state)}")

def print_log(self):
if self.logger.log_size() > 0:
for i, line in enumerate(self.logger.get_log()):
self.window.change_line(self.main_message_begins + 1 + i, line)

def format_log_message(self, line):
elapsed_ptime, tag, remote, do_lock, auto = line
return (f"At (tag: ({'{:,}'.format(tag.time)} ns, {tag.microstep}), "
f"lag: {'{:,}'.format(elapsed_ptime - tag.time)} ns), "
f"{'[Auto] ' if auto else ''}{'[Remote]' if remote else '[Local]'} lock action: {'Lock' if do_lock else 'Unlock'}")

# log structure: (elapsed_physical_time:int, tag:int, remote:bool, do_lock:bool, auto:bool)
def append_log(self, auto, remote, do_lock):
elapsed_tag = Tag(get_elapsed_logical_time(), get_microstep())
log_entry = (get_elapsed_physical_time(), elapsed_tag, remote, do_lock, auto)
self.logger.append_log(self.format_log_message(log_entry))

def listen_for_keypress(self, press_lock, press_unlock):
key = ""
while key != ord("q"):
key = self.window.getch()
if key == ord("l"):
press_lock.schedule(0)
elif key == ord("u"):
press_unlock.schedule(0)
request_stop()

def reset_autolock_counter(self):
self.auto_lock_counter = self.auto_lock_duration
=}

reaction(startup) -> press_lock, press_unlock {=
# Set up the logger and the curses window
self.window = Window()
self.logger = Logger()
messages = [
"Press 'l' to send a lock signal, 'u' to send an unlock signal, 'q' to quit",
"",
"Rules:",
"1. A lock signal locks all doors.",
"2. An unlock signal unlocks driver's door if driver's door was locked.",
"3. An unlock signal unlocks all door if driver's door was NOT locked.",
"",
"All doors automatically lock after 5 seconds if no unlock signal is received. ",
""
]
for i, msg in enumerate(messages):
self.window.change_line(i, msg)
self.main_message_begins = len(messages)
self.lock_state = LockStates.Locked
self.reset_autolock_counter()
self.print_lock_state()
self.print_log()
self.window.refresh()

# Spawn thread to listen for key presses
t = threading.Thread(target=self.listen_for_keypress, args=(press_lock, press_unlock))
self.listener = t
t.start()
=}

reaction(press_lock) -> handle_press_lock {=
handle_press_lock.schedule(0)
=}

reaction(press_unlock) -> handle_press_unlock {=
handle_press_unlock.schedule(0)
=}

reaction(handle_press_lock) -> do_lock, send_lock_action {=
self.append_log(auto=False, remote=False, do_lock=True)
do_lock.schedule(0, True)
send_lock_action.set(True)
=}

reaction(handle_press_unlock) -> do_lock, send_lock_action {=
self.append_log(auto=False, remote=False, do_lock=False)
do_lock.schedule(0, False)
send_lock_action.set(False)
=}

reaction(get_lock_press_from_tester) -> handle_press_lock, handle_press_unlock {=
press_lock_val = get_lock_press_from_tester.value
if press_lock_val:
handle_press_lock.schedule(0)
else:
handle_press_unlock.schedule(0)
=}

reaction(get_lock_action) -> do_lock {=
self.append_log(auto=False, remote=True, do_lock=get_lock_action.value)
do_lock.schedule(0, get_lock_action.value)
=}

reaction(do_lock) {=
if do_lock.value:
self.lock_state = LockStates.Locked
elif self.lock_state == LockStates.Locked:
self.lock_state = LockStates.DriverUnlocked
self.reset_autolock_counter()
elif self.lock_state == LockStates.DriverUnlocked:
self.lock_state = LockStates.AllUnlocked
self.print_lock_state()
self.print_log()
self.window.refresh()
=}

reaction(autolock_timer) -> do_lock {=
if self.auto_lock_counter > 0:
self.auto_lock_counter -= 0.1
elif self.lock_state != LockStates.Locked:
self.append_log(auto=True, remote=False, do_lock=True)
do_lock.schedule(0, True)
=}

reaction(shutdown) {=
self.listener.join()
curses.endwin()
=}
}

federated reactor {
fob = new DoubleUnlockKeyFob();
twin = new DoubleUnlockKeyFob();
fob.send_lock_action -> twin.get_lock_action;
twin.send_lock_action -> fob.get_lock_action;
}
6 changes: 6 additions & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Double Unlock Key Fob (Digital Twin) Example

This example is similar to the Key Fob Demo, but with 3 states: Locked, Driver's Door Unlocked, and All Doors Unlocked.
A simulator program `Simulator.lf` is also included to demonstrate generating simulated signals at arbitrary logical time.

To run the program, do `lfc Simulator.lf` and use the bash scripts to launch each federate in a separate terminal window.
63 changes: 63 additions & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Example of a reactor that sends simulated key presses at arbitrary
* logical time to the key fobs.
*
* For run instructions, see README.md in the same directory.
*
* @author Hou Seng Wong ([email protected])
*/

target Python {
docker: true,
files: ["../utils.py"]
};

import DoubleUnlockKeyFob from "DoubleUnlockDemo.lf";


reactor DoubleUnlockKeyFobTester(initial_delay(5)) {
state logger({=None=});
state window({=None=});

logical action do_test;
logical action simulate_press_fob;
logical action simulate_press_twin;
output send_lock_press_to_fob;
output send_lock_press_to_twin;

reaction(startup) -> do_test {=
print(f"Test starts in {self.initial_delay} seconds...")
do_test.schedule(SEC(self.initial_delay))
=}

reaction(do_test) -> simulate_press_fob, simulate_press_twin {=
simulate_press_fob.schedule(0, False)
simulate_press_twin.schedule(0, False)

# Feel free to add other simulated key presses...
# simulate_press_fob.schedule(0, True)
# simulate_press_twin.schedule(0, True)
=}

reaction(simulate_press_fob) -> send_lock_press_to_fob {=
tag = get_current_tag()
print(f"Sent lock press {simulate_press_fob.value} to fob at ({tag.time}, {tag.microstep})")
send_lock_press_to_fob.set(simulate_press_fob.value)
=}

reaction(simulate_press_twin) -> send_lock_press_to_twin {=
tag = get_current_tag()
print(f"Sent lock press {simulate_press_twin.value} to twin at ({tag.time}, {tag.microstep})")
send_lock_press_to_twin.set(simulate_press_twin.value)
=}
}

federated reactor {
fob = new DoubleUnlockKeyFob();
twin = new DoubleUnlockKeyFob();
tester = new DoubleUnlockKeyFobTester();
tester.send_lock_press_to_fob -> fob.get_lock_press_from_tester;
tester.send_lock_press_to_twin -> twin.get_lock_press_from_tester;
fob.send_lock_action -> twin.get_lock_action;
twin.send_lock_action -> fob.get_lock_action;
}
3 changes: 3 additions & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/run_fob.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd ../../../src-gen/DigitalTwin/DoubleUnlock/Simulator/fob
python3 Simulator_fob.py -i 1
1 change: 1 addition & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/run_rti.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RTI -i 1 -n 3
3 changes: 3 additions & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/run_simulator.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd ../../../src-gen/DigitalTwin/DoubleUnlock/Simulator/simulator
python3 Simulator_tester.py -i 1
3 changes: 3 additions & 0 deletions example/Python/src/DigitalTwin/DoubleUnlock/run_twin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
cd ../../../src-gen/DigitalTwin/DoubleUnlock/Simulator/twin
python3 Simulator_twin.py -i 1
Loading

0 comments on commit f7df745

Please sign in to comment.