From f8c379dac89615bf0065677e004d15a05e8a2b3d Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Fri, 9 Jun 2023 13:41:58 +0200 Subject: [PATCH 1/3] move experimental examples --- C/src/experimental/README.md | 3 --- Python/src/experimental/README.md | 3 --- .../C/src}/Autoware/Autoware.lf | 0 .../C/src}/Autoware/parse_trace.sh | 0 .../C/src}/Controller/ResponseTime.lf | 0 .../C/src}/Controller/ResponseTime2.lf | 0 .../C/src}/Controller/ResponseTime3.lf | 0 .../C/src}/Intersection/Intersection.lf | 0 .../C/src}/Logic/Logic.lf | 0 .../C/src}/Microsteps/Anomaly.lf | 0 .../pacman/PacMan_BehaviorTree_Mockup_Modes.lf | 0 .../PacMan_BehaviorTree_Mockup_Modes_Triggers.lf | 0 .../pacman/PacMan_BehaviorTree_Mockup_Reactors.lf | 0 .../pacman/observations-on-behavior-trees.pptx | Bin .../BehaviorTrees/robohub_example_advanced.lf | 0 .../BehaviorTrees/robohub_example_simple.lf | 0 .../C/src}/ModalModels/Motivation/Chrono/Chrono.lf | 0 .../Motivation/SineAvgMax/sine_max_avg.lf | 0 .../Motivation/SineAvgMax/sine_max_avg_v2.lf | 0 .../src}/ModalModels/ReflexGame/ModalReflexGame.lf | 0 .../C/src}/Mutations/ScatterGather.lf | 0 .../C/src}/Mutations/SieveOfEratosthenes.lf | 0 .../C/src}/PowerTrain/PowerTrain.lf | 0 .../C/src}/PowerTrain/README.md | 0 .../C/src}/Safety/Cutter.lf | 0 .../C/src}/SolarUAV/SolarUAV.lf | 0 .../C/src}/SpatAnalysis/MQTTPublisher.lf | 0 .../C/src}/SpatAnalysis/MQTTSubscriber.lf | 0 .../C/src}/SpatAnalysis/README.md | 0 .../C/src}/SpatAnalysis/influxWrite.lf | 0 .../C/src}/SpatAnalysis/package.json | 0 .../C/src}/SpatAnalysis/spatReceiver.lf | 0 .../C/src}/SpatAnalysis/spatRecommender.lf | 0 .../C/src}/context-manager/ContextManager.lf | 0 .../C/src}/context-manager/ContextManager2.lf | 0 .../C/src}/protection-relay/ProtectionRelay.lf | 0 .../C/src}/soafee/soafee.lf | 0 .../src}/FurutaPendulum/FurutaPendulumStabilize.lf | 0 .../src}/FurutaPendulum/PendulumSimulationEuler.lf | 0 .../src}/FurutaPendulum/PendulumSimulationRK4.lf | 0 .../Python/src}/FurutaPendulum/SmallTest.lf | 0 .../Python/src}/GeneralModels/bankedsimius.lf | 0 .../Python/src}/GeneralModels/headedswarm.lf | 0 .../Python/src}/GeneralModels/queenant.lf | 0 .../Python/src}/GeneralModels/simius.lf | 0 .../Python/src}/GeneralModels/swarm.lf | 0 .../Python/src}/GeneralModels/tester.lf | 0 .../src}/Intersection/Carla/CarlaIntersection.lf | 0 .../Python/src}/Intersection/Carla/CarlaSim.lf | 0 .../Python/src}/Intersection/Carla/ROS/.gitignore | 0 .../Python/src}/Intersection/Carla/ROS/README.md | 0 .../launch/intersection_demo.launch.py | 0 .../Carla/ROS/carla_intersection/package.xml | 0 .../carla_intersection/resource/carla_intersection | 0 .../Carla/ROS/carla_intersection/setup.cfg | 0 .../Carla/ROS/carla_intersection/setup.py | 0 .../Carla/ROS/carla_intersection/src/__init__.py | 0 .../Carla/ROS/carla_intersection/src/carla_sim.py | 0 .../ROS/carla_intersection/src/carla_sim_node.py | 0 .../Carla/ROS/carla_intersection/src/constants.py | 0 .../ROS/carla_intersection/src/launch_parameters.py | 0 .../Carla/ROS/carla_intersection/src/ros_utils.py | 0 .../Carla/ROS/carla_intersection/src/rsu.py | 0 .../Carla/ROS/carla_intersection/src/rsu_node.py | 0 .../Carla/ROS/carla_intersection/src/utils.py | 0 .../Carla/ROS/carla_intersection/src/vehicle.py | 0 .../ROS/carla_intersection/src/vehicle_node.py | 0 .../ROS/carla_intersection_msgs/CMakeLists.txt | 0 .../Carla/ROS/carla_intersection_msgs/msg/Grant.msg | 0 .../ROS/carla_intersection_msgs/msg/Request.msg | 0 .../carla_intersection_msgs/msg/VehicleCommand.msg | 0 .../Carla/ROS/carla_intersection_msgs/package.xml | 0 .../Python/src}/Intersection/Carla/RSU.lf | 0 .../Python/src}/Intersection/Carla/Vehicle.lf | 0 .../Python/src}/Intersection/Carla/run-carla.sh | 0 .../Python/src}/Intersection/Intersection.lf | 0 .../Python/src}/Mining/BusyMine.lf | 0 .../Python/src}/Mining/MineTest.lf | 0 .../Python/src}/Mining/MineWObserver.lf | 0 .../Python/src}/Mining/ModalMine.lf | 0 .../Python/src}/Mining/PhosphateMine.lf | 0 .../Python/src}/Mining/SingleModeMine.lf | 0 .../Python/src}/Mining/include/AIPhosphate.py | 0 .../Python/src}/Mining/include/hbphosphate.py | 0 .../Python/src}/Mining/include/images/Blinky.png | Bin .../Python/src}/Mining/include/images/Clyde.png | Bin .../Python/src}/Mining/include/images/Inky.png | Bin .../Python/src}/Mining/include/images/Pinky.png | Bin .../Python/src}/Mining/include/images/Trollman.png | Bin .../Python/src}/Mining/include/images/pacman.jpg | Bin .../Python/src}/Mining/include/images/pacman.png | Bin .../Python/src}/Mining/include/images/roomb1.png | Bin .../Python/src}/Mining/include/images/roomba.png | Bin .../src}/Mining/include/images/scaredghost.png | Bin .../Python/src}/Mining/include/images/user.png | Bin .../src}/Mining/include/images/wheelchair.png | Bin .../Python/src}/Pac-Man/BankedObserver.lf | 0 .../Python/src}/Pac-Man/ContainedPlayer.lf | 0 .../Python/src}/Pac-Man/Foomba.lf | 0 .../Python/src}/Pac-Man/KielPacManTree.lf | 0 .../Python/src}/Pac-Man/PacMan.lf | 0 .../Python/src}/Pac-Man/PacManKielTree.lf | 0 .../Python/src}/Pac-Man/PacManNMBT.lf | 0 .../Python/src}/Pac-Man/PacManTesting.lf | 0 .../Python/src}/Pac-Man/PacManWFrenzy.lf | 0 .../Python/src}/Pac-Man/PacManWModalBT.lf | 0 .../Python/src}/Pac-Man/PacManWRestart.lf | 0 .../Python/src}/Pac-Man/PacManWithBank.lf | 0 .../Python/src}/Pac-Man/README.md | 0 .../Python/src}/Pac-Man/include/AIPacSupport.py | 0 .../Python/src}/Pac-Man/include/hbpacman.py | 0 .../Python/src}/Pac-Man/include/images/Blinky.png | Bin .../Python/src}/Pac-Man/include/images/Clyde.png | Bin .../Python/src}/Pac-Man/include/images/Inky.png | Bin .../Python/src}/Pac-Man/include/images/Pinky.png | Bin .../Python/src}/Pac-Man/include/images/Trollman.png | Bin .../Python/src}/Pac-Man/include/images/pacman.jpg | Bin .../Python/src}/Pac-Man/include/images/pacman.png | Bin .../Python/src}/Pac-Man/include/images/roomb1.png | Bin .../Python/src}/Pac-Man/include/images/roomba.png | Bin .../src}/Pac-Man/include/images/scaredghost.png | Bin .../Python/src}/Pac-Man/include/images/user.png | Bin .../src}/Pac-Man/include/images/wheelchair.png | Bin .../Python/src}/Pac-Man/oldWBank.lf | 0 .../Python/src}/Testing/HistoryMRTest.lf | 0 125 files changed, 6 deletions(-) delete mode 100644 C/src/experimental/README.md delete mode 100644 Python/src/experimental/README.md rename {C/src/experimental => experimental/C/src}/Autoware/Autoware.lf (100%) rename {C/src/experimental => experimental/C/src}/Autoware/parse_trace.sh (100%) rename {C/src/experimental => experimental/C/src}/Controller/ResponseTime.lf (100%) rename {C/src/experimental => experimental/C/src}/Controller/ResponseTime2.lf (100%) rename {C/src/experimental => experimental/C/src}/Controller/ResponseTime3.lf (100%) rename {C/src/experimental => experimental/C/src}/Intersection/Intersection.lf (100%) rename {C/src/experimental => experimental/C/src}/Logic/Logic.lf (100%) rename {C/src/experimental => experimental/C/src}/Microsteps/Anomaly.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/BehaviorTrees/pacman/observations-on-behavior-trees.pptx (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/BehaviorTrees/robohub_example_advanced.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/BehaviorTrees/robohub_example_simple.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/Motivation/Chrono/Chrono.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf (100%) rename {C/src/experimental => experimental/C/src}/ModalModels/ReflexGame/ModalReflexGame.lf (100%) rename {C/src/experimental => experimental/C/src}/Mutations/ScatterGather.lf (100%) rename {C/src/experimental => experimental/C/src}/Mutations/SieveOfEratosthenes.lf (100%) rename {C/src/experimental => experimental/C/src}/PowerTrain/PowerTrain.lf (100%) rename {C/src/experimental => experimental/C/src}/PowerTrain/README.md (100%) rename {C/src/experimental => experimental/C/src}/Safety/Cutter.lf (100%) rename {C/src/experimental => experimental/C/src}/SolarUAV/SolarUAV.lf (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/MQTTPublisher.lf (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/MQTTSubscriber.lf (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/README.md (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/influxWrite.lf (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/package.json (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/spatReceiver.lf (100%) rename {C/src/experimental => experimental/C/src}/SpatAnalysis/spatRecommender.lf (100%) rename {C/src/experimental => experimental/C/src}/context-manager/ContextManager.lf (100%) rename {C/src/experimental => experimental/C/src}/context-manager/ContextManager2.lf (100%) rename {C/src/experimental => experimental/C/src}/protection-relay/ProtectionRelay.lf (100%) rename {C/src/experimental => experimental/C/src}/soafee/soafee.lf (100%) rename {Python/src/experimental => experimental/Python/src}/FurutaPendulum/FurutaPendulumStabilize.lf (100%) rename {Python/src/experimental => experimental/Python/src}/FurutaPendulum/PendulumSimulationEuler.lf (100%) rename {Python/src/experimental => experimental/Python/src}/FurutaPendulum/PendulumSimulationRK4.lf (100%) rename {Python/src/experimental => experimental/Python/src}/FurutaPendulum/SmallTest.lf (100%) rename {Python/src/experimental => experimental/Python/src}/GeneralModels/bankedsimius.lf (100%) rename {Python/src/experimental => experimental/Python/src}/GeneralModels/headedswarm.lf (100%) rename {Python/src/experimental => experimental/Python/src}/GeneralModels/queenant.lf (100%) rename {Python/src/experimental => experimental/Python/src}/GeneralModels/simius.lf (100%) rename {Python/src/experimental => experimental/Python/src}/GeneralModels/swarm.lf (100%) rename {Python/src/experimental => experimental/Python/src}/GeneralModels/tester.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/CarlaIntersection.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/CarlaSim.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/.gitignore (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/README.md (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/launch/intersection_demo.launch.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/package.xml (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/resource/carla_intersection (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/setup.cfg (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/setup.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/__init__.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/carla_sim.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/carla_sim_node.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/constants.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/launch_parameters.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/ros_utils.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/rsu.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/rsu_node.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/utils.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/vehicle.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection/src/vehicle_node.py (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection_msgs/CMakeLists.txt (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection_msgs/msg/Grant.msg (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection_msgs/msg/Request.msg (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection_msgs/msg/VehicleCommand.msg (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/ROS/carla_intersection_msgs/package.xml (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/RSU.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/Vehicle.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Carla/run-carla.sh (100%) rename {Python/src/experimental => experimental/Python/src}/Intersection/Intersection.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/BusyMine.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/MineTest.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/MineWObserver.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/ModalMine.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/PhosphateMine.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/SingleModeMine.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/AIPhosphate.py (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/hbphosphate.py (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/Blinky.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/Clyde.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/Inky.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/Pinky.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/Trollman.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/pacman.jpg (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/pacman.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/roomb1.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/roomba.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/scaredghost.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/user.png (100%) rename {Python/src/experimental => experimental/Python/src}/Mining/include/images/wheelchair.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/BankedObserver.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/ContainedPlayer.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/Foomba.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/KielPacManTree.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacMan.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManKielTree.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManNMBT.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManTesting.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManWFrenzy.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManWModalBT.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManWRestart.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/PacManWithBank.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/README.md (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/AIPacSupport.py (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/hbpacman.py (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/Blinky.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/Clyde.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/Inky.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/Pinky.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/Trollman.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/pacman.jpg (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/pacman.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/roomb1.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/roomba.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/scaredghost.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/user.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/include/images/wheelchair.png (100%) rename {Python/src/experimental => experimental/Python/src}/Pac-Man/oldWBank.lf (100%) rename {Python/src/experimental => experimental/Python/src}/Testing/HistoryMRTest.lf (100%) diff --git a/C/src/experimental/README.md b/C/src/experimental/README.md deleted file mode 100644 index 25272bd4..00000000 --- a/C/src/experimental/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Experimental - -This directory contains a collection of programs and notes associated with Lingua Franca topics that are immature and under discussion. Some of these Lingua Franca programs do not compile, but rather are meant to illustrate possible future development ideas. \ No newline at end of file diff --git a/Python/src/experimental/README.md b/Python/src/experimental/README.md deleted file mode 100644 index 25272bd4..00000000 --- a/Python/src/experimental/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Experimental - -This directory contains a collection of programs and notes associated with Lingua Franca topics that are immature and under discussion. Some of these Lingua Franca programs do not compile, but rather are meant to illustrate possible future development ideas. \ No newline at end of file diff --git a/C/src/experimental/Autoware/Autoware.lf b/experimental/C/src/Autoware/Autoware.lf similarity index 100% rename from C/src/experimental/Autoware/Autoware.lf rename to experimental/C/src/Autoware/Autoware.lf diff --git a/C/src/experimental/Autoware/parse_trace.sh b/experimental/C/src/Autoware/parse_trace.sh similarity index 100% rename from C/src/experimental/Autoware/parse_trace.sh rename to experimental/C/src/Autoware/parse_trace.sh diff --git a/C/src/experimental/Controller/ResponseTime.lf b/experimental/C/src/Controller/ResponseTime.lf similarity index 100% rename from C/src/experimental/Controller/ResponseTime.lf rename to experimental/C/src/Controller/ResponseTime.lf diff --git a/C/src/experimental/Controller/ResponseTime2.lf b/experimental/C/src/Controller/ResponseTime2.lf similarity index 100% rename from C/src/experimental/Controller/ResponseTime2.lf rename to experimental/C/src/Controller/ResponseTime2.lf diff --git a/C/src/experimental/Controller/ResponseTime3.lf b/experimental/C/src/Controller/ResponseTime3.lf similarity index 100% rename from C/src/experimental/Controller/ResponseTime3.lf rename to experimental/C/src/Controller/ResponseTime3.lf diff --git a/C/src/experimental/Intersection/Intersection.lf b/experimental/C/src/Intersection/Intersection.lf similarity index 100% rename from C/src/experimental/Intersection/Intersection.lf rename to experimental/C/src/Intersection/Intersection.lf diff --git a/C/src/experimental/Logic/Logic.lf b/experimental/C/src/Logic/Logic.lf similarity index 100% rename from C/src/experimental/Logic/Logic.lf rename to experimental/C/src/Logic/Logic.lf diff --git a/C/src/experimental/Microsteps/Anomaly.lf b/experimental/C/src/Microsteps/Anomaly.lf similarity index 100% rename from C/src/experimental/Microsteps/Anomaly.lf rename to experimental/C/src/Microsteps/Anomaly.lf diff --git a/C/src/experimental/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf similarity index 100% rename from C/src/experimental/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf rename to experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf diff --git a/C/src/experimental/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf similarity index 100% rename from C/src/experimental/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf rename to experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf diff --git a/C/src/experimental/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf similarity index 100% rename from C/src/experimental/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf rename to experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf diff --git a/C/src/experimental/ModalModels/BehaviorTrees/pacman/observations-on-behavior-trees.pptx b/experimental/C/src/ModalModels/BehaviorTrees/pacman/observations-on-behavior-trees.pptx similarity index 100% rename from C/src/experimental/ModalModels/BehaviorTrees/pacman/observations-on-behavior-trees.pptx rename to experimental/C/src/ModalModels/BehaviorTrees/pacman/observations-on-behavior-trees.pptx diff --git a/C/src/experimental/ModalModels/BehaviorTrees/robohub_example_advanced.lf b/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_advanced.lf similarity index 100% rename from C/src/experimental/ModalModels/BehaviorTrees/robohub_example_advanced.lf rename to experimental/C/src/ModalModels/BehaviorTrees/robohub_example_advanced.lf diff --git a/C/src/experimental/ModalModels/BehaviorTrees/robohub_example_simple.lf b/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_simple.lf similarity index 100% rename from C/src/experimental/ModalModels/BehaviorTrees/robohub_example_simple.lf rename to experimental/C/src/ModalModels/BehaviorTrees/robohub_example_simple.lf diff --git a/C/src/experimental/ModalModels/Motivation/Chrono/Chrono.lf b/experimental/C/src/ModalModels/Motivation/Chrono/Chrono.lf similarity index 100% rename from C/src/experimental/ModalModels/Motivation/Chrono/Chrono.lf rename to experimental/C/src/ModalModels/Motivation/Chrono/Chrono.lf diff --git a/C/src/experimental/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf b/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf similarity index 100% rename from C/src/experimental/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf rename to experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf diff --git a/C/src/experimental/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf b/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf similarity index 100% rename from C/src/experimental/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf rename to experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf diff --git a/C/src/experimental/ModalModels/ReflexGame/ModalReflexGame.lf b/experimental/C/src/ModalModels/ReflexGame/ModalReflexGame.lf similarity index 100% rename from C/src/experimental/ModalModels/ReflexGame/ModalReflexGame.lf rename to experimental/C/src/ModalModels/ReflexGame/ModalReflexGame.lf diff --git a/C/src/experimental/Mutations/ScatterGather.lf b/experimental/C/src/Mutations/ScatterGather.lf similarity index 100% rename from C/src/experimental/Mutations/ScatterGather.lf rename to experimental/C/src/Mutations/ScatterGather.lf diff --git a/C/src/experimental/Mutations/SieveOfEratosthenes.lf b/experimental/C/src/Mutations/SieveOfEratosthenes.lf similarity index 100% rename from C/src/experimental/Mutations/SieveOfEratosthenes.lf rename to experimental/C/src/Mutations/SieveOfEratosthenes.lf diff --git a/C/src/experimental/PowerTrain/PowerTrain.lf b/experimental/C/src/PowerTrain/PowerTrain.lf similarity index 100% rename from C/src/experimental/PowerTrain/PowerTrain.lf rename to experimental/C/src/PowerTrain/PowerTrain.lf diff --git a/C/src/experimental/PowerTrain/README.md b/experimental/C/src/PowerTrain/README.md similarity index 100% rename from C/src/experimental/PowerTrain/README.md rename to experimental/C/src/PowerTrain/README.md diff --git a/C/src/experimental/Safety/Cutter.lf b/experimental/C/src/Safety/Cutter.lf similarity index 100% rename from C/src/experimental/Safety/Cutter.lf rename to experimental/C/src/Safety/Cutter.lf diff --git a/C/src/experimental/SolarUAV/SolarUAV.lf b/experimental/C/src/SolarUAV/SolarUAV.lf similarity index 100% rename from C/src/experimental/SolarUAV/SolarUAV.lf rename to experimental/C/src/SolarUAV/SolarUAV.lf diff --git a/C/src/experimental/SpatAnalysis/MQTTPublisher.lf b/experimental/C/src/SpatAnalysis/MQTTPublisher.lf similarity index 100% rename from C/src/experimental/SpatAnalysis/MQTTPublisher.lf rename to experimental/C/src/SpatAnalysis/MQTTPublisher.lf diff --git a/C/src/experimental/SpatAnalysis/MQTTSubscriber.lf b/experimental/C/src/SpatAnalysis/MQTTSubscriber.lf similarity index 100% rename from C/src/experimental/SpatAnalysis/MQTTSubscriber.lf rename to experimental/C/src/SpatAnalysis/MQTTSubscriber.lf diff --git a/C/src/experimental/SpatAnalysis/README.md b/experimental/C/src/SpatAnalysis/README.md similarity index 100% rename from C/src/experimental/SpatAnalysis/README.md rename to experimental/C/src/SpatAnalysis/README.md diff --git a/C/src/experimental/SpatAnalysis/influxWrite.lf b/experimental/C/src/SpatAnalysis/influxWrite.lf similarity index 100% rename from C/src/experimental/SpatAnalysis/influxWrite.lf rename to experimental/C/src/SpatAnalysis/influxWrite.lf diff --git a/C/src/experimental/SpatAnalysis/package.json b/experimental/C/src/SpatAnalysis/package.json similarity index 100% rename from C/src/experimental/SpatAnalysis/package.json rename to experimental/C/src/SpatAnalysis/package.json diff --git a/C/src/experimental/SpatAnalysis/spatReceiver.lf b/experimental/C/src/SpatAnalysis/spatReceiver.lf similarity index 100% rename from C/src/experimental/SpatAnalysis/spatReceiver.lf rename to experimental/C/src/SpatAnalysis/spatReceiver.lf diff --git a/C/src/experimental/SpatAnalysis/spatRecommender.lf b/experimental/C/src/SpatAnalysis/spatRecommender.lf similarity index 100% rename from C/src/experimental/SpatAnalysis/spatRecommender.lf rename to experimental/C/src/SpatAnalysis/spatRecommender.lf diff --git a/C/src/experimental/context-manager/ContextManager.lf b/experimental/C/src/context-manager/ContextManager.lf similarity index 100% rename from C/src/experimental/context-manager/ContextManager.lf rename to experimental/C/src/context-manager/ContextManager.lf diff --git a/C/src/experimental/context-manager/ContextManager2.lf b/experimental/C/src/context-manager/ContextManager2.lf similarity index 100% rename from C/src/experimental/context-manager/ContextManager2.lf rename to experimental/C/src/context-manager/ContextManager2.lf diff --git a/C/src/experimental/protection-relay/ProtectionRelay.lf b/experimental/C/src/protection-relay/ProtectionRelay.lf similarity index 100% rename from C/src/experimental/protection-relay/ProtectionRelay.lf rename to experimental/C/src/protection-relay/ProtectionRelay.lf diff --git a/C/src/experimental/soafee/soafee.lf b/experimental/C/src/soafee/soafee.lf similarity index 100% rename from C/src/experimental/soafee/soafee.lf rename to experimental/C/src/soafee/soafee.lf diff --git a/Python/src/experimental/FurutaPendulum/FurutaPendulumStabilize.lf b/experimental/Python/src/FurutaPendulum/FurutaPendulumStabilize.lf similarity index 100% rename from Python/src/experimental/FurutaPendulum/FurutaPendulumStabilize.lf rename to experimental/Python/src/FurutaPendulum/FurutaPendulumStabilize.lf diff --git a/Python/src/experimental/FurutaPendulum/PendulumSimulationEuler.lf b/experimental/Python/src/FurutaPendulum/PendulumSimulationEuler.lf similarity index 100% rename from Python/src/experimental/FurutaPendulum/PendulumSimulationEuler.lf rename to experimental/Python/src/FurutaPendulum/PendulumSimulationEuler.lf diff --git a/Python/src/experimental/FurutaPendulum/PendulumSimulationRK4.lf b/experimental/Python/src/FurutaPendulum/PendulumSimulationRK4.lf similarity index 100% rename from Python/src/experimental/FurutaPendulum/PendulumSimulationRK4.lf rename to experimental/Python/src/FurutaPendulum/PendulumSimulationRK4.lf diff --git a/Python/src/experimental/FurutaPendulum/SmallTest.lf b/experimental/Python/src/FurutaPendulum/SmallTest.lf similarity index 100% rename from Python/src/experimental/FurutaPendulum/SmallTest.lf rename to experimental/Python/src/FurutaPendulum/SmallTest.lf diff --git a/Python/src/experimental/GeneralModels/bankedsimius.lf b/experimental/Python/src/GeneralModels/bankedsimius.lf similarity index 100% rename from Python/src/experimental/GeneralModels/bankedsimius.lf rename to experimental/Python/src/GeneralModels/bankedsimius.lf diff --git a/Python/src/experimental/GeneralModels/headedswarm.lf b/experimental/Python/src/GeneralModels/headedswarm.lf similarity index 100% rename from Python/src/experimental/GeneralModels/headedswarm.lf rename to experimental/Python/src/GeneralModels/headedswarm.lf diff --git a/Python/src/experimental/GeneralModels/queenant.lf b/experimental/Python/src/GeneralModels/queenant.lf similarity index 100% rename from Python/src/experimental/GeneralModels/queenant.lf rename to experimental/Python/src/GeneralModels/queenant.lf diff --git a/Python/src/experimental/GeneralModels/simius.lf b/experimental/Python/src/GeneralModels/simius.lf similarity index 100% rename from Python/src/experimental/GeneralModels/simius.lf rename to experimental/Python/src/GeneralModels/simius.lf diff --git a/Python/src/experimental/GeneralModels/swarm.lf b/experimental/Python/src/GeneralModels/swarm.lf similarity index 100% rename from Python/src/experimental/GeneralModels/swarm.lf rename to experimental/Python/src/GeneralModels/swarm.lf diff --git a/Python/src/experimental/GeneralModels/tester.lf b/experimental/Python/src/GeneralModels/tester.lf similarity index 100% rename from Python/src/experimental/GeneralModels/tester.lf rename to experimental/Python/src/GeneralModels/tester.lf diff --git a/Python/src/experimental/Intersection/Carla/CarlaIntersection.lf b/experimental/Python/src/Intersection/Carla/CarlaIntersection.lf similarity index 100% rename from Python/src/experimental/Intersection/Carla/CarlaIntersection.lf rename to experimental/Python/src/Intersection/Carla/CarlaIntersection.lf diff --git a/Python/src/experimental/Intersection/Carla/CarlaSim.lf b/experimental/Python/src/Intersection/Carla/CarlaSim.lf similarity index 100% rename from Python/src/experimental/Intersection/Carla/CarlaSim.lf rename to experimental/Python/src/Intersection/Carla/CarlaSim.lf diff --git a/Python/src/experimental/Intersection/Carla/ROS/.gitignore b/experimental/Python/src/Intersection/Carla/ROS/.gitignore similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/.gitignore rename to experimental/Python/src/Intersection/Carla/ROS/.gitignore diff --git a/Python/src/experimental/Intersection/Carla/ROS/README.md b/experimental/Python/src/Intersection/Carla/ROS/README.md similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/README.md rename to experimental/Python/src/Intersection/Carla/ROS/README.md diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/launch/intersection_demo.launch.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/launch/intersection_demo.launch.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/launch/intersection_demo.launch.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/launch/intersection_demo.launch.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/package.xml b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/package.xml similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/package.xml rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/package.xml diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/resource/carla_intersection b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/resource/carla_intersection similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/resource/carla_intersection rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/resource/carla_intersection diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/setup.cfg b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/setup.cfg similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/setup.cfg rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/setup.cfg diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/setup.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/setup.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/setup.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/setup.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/__init__.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/__init__.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/__init__.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/__init__.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/carla_sim.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/carla_sim.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/carla_sim.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/carla_sim.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/carla_sim_node.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/carla_sim_node.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/carla_sim_node.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/carla_sim_node.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/constants.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/constants.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/constants.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/constants.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/launch_parameters.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/launch_parameters.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/launch_parameters.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/launch_parameters.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/ros_utils.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/ros_utils.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/ros_utils.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/ros_utils.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/rsu.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/rsu.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/rsu.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/rsu.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/rsu_node.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/rsu_node.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/rsu_node.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/rsu_node.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/utils.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/utils.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/utils.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/utils.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/vehicle.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/vehicle.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/vehicle.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/vehicle.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/vehicle_node.py b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/vehicle_node.py similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection/src/vehicle_node.py rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection/src/vehicle_node.py diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/CMakeLists.txt b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/CMakeLists.txt similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/CMakeLists.txt rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/CMakeLists.txt diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/msg/Grant.msg b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/msg/Grant.msg similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/msg/Grant.msg rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/msg/Grant.msg diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/msg/Request.msg b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/msg/Request.msg similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/msg/Request.msg rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/msg/Request.msg diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/msg/VehicleCommand.msg b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/msg/VehicleCommand.msg similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/msg/VehicleCommand.msg rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/msg/VehicleCommand.msg diff --git a/Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/package.xml b/experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/package.xml similarity index 100% rename from Python/src/experimental/Intersection/Carla/ROS/carla_intersection_msgs/package.xml rename to experimental/Python/src/Intersection/Carla/ROS/carla_intersection_msgs/package.xml diff --git a/Python/src/experimental/Intersection/Carla/RSU.lf b/experimental/Python/src/Intersection/Carla/RSU.lf similarity index 100% rename from Python/src/experimental/Intersection/Carla/RSU.lf rename to experimental/Python/src/Intersection/Carla/RSU.lf diff --git a/Python/src/experimental/Intersection/Carla/Vehicle.lf b/experimental/Python/src/Intersection/Carla/Vehicle.lf similarity index 100% rename from Python/src/experimental/Intersection/Carla/Vehicle.lf rename to experimental/Python/src/Intersection/Carla/Vehicle.lf diff --git a/Python/src/experimental/Intersection/Carla/run-carla.sh b/experimental/Python/src/Intersection/Carla/run-carla.sh similarity index 100% rename from Python/src/experimental/Intersection/Carla/run-carla.sh rename to experimental/Python/src/Intersection/Carla/run-carla.sh diff --git a/Python/src/experimental/Intersection/Intersection.lf b/experimental/Python/src/Intersection/Intersection.lf similarity index 100% rename from Python/src/experimental/Intersection/Intersection.lf rename to experimental/Python/src/Intersection/Intersection.lf diff --git a/Python/src/experimental/Mining/BusyMine.lf b/experimental/Python/src/Mining/BusyMine.lf similarity index 100% rename from Python/src/experimental/Mining/BusyMine.lf rename to experimental/Python/src/Mining/BusyMine.lf diff --git a/Python/src/experimental/Mining/MineTest.lf b/experimental/Python/src/Mining/MineTest.lf similarity index 100% rename from Python/src/experimental/Mining/MineTest.lf rename to experimental/Python/src/Mining/MineTest.lf diff --git a/Python/src/experimental/Mining/MineWObserver.lf b/experimental/Python/src/Mining/MineWObserver.lf similarity index 100% rename from Python/src/experimental/Mining/MineWObserver.lf rename to experimental/Python/src/Mining/MineWObserver.lf diff --git a/Python/src/experimental/Mining/ModalMine.lf b/experimental/Python/src/Mining/ModalMine.lf similarity index 100% rename from Python/src/experimental/Mining/ModalMine.lf rename to experimental/Python/src/Mining/ModalMine.lf diff --git a/Python/src/experimental/Mining/PhosphateMine.lf b/experimental/Python/src/Mining/PhosphateMine.lf similarity index 100% rename from Python/src/experimental/Mining/PhosphateMine.lf rename to experimental/Python/src/Mining/PhosphateMine.lf diff --git a/Python/src/experimental/Mining/SingleModeMine.lf b/experimental/Python/src/Mining/SingleModeMine.lf similarity index 100% rename from Python/src/experimental/Mining/SingleModeMine.lf rename to experimental/Python/src/Mining/SingleModeMine.lf diff --git a/Python/src/experimental/Mining/include/AIPhosphate.py b/experimental/Python/src/Mining/include/AIPhosphate.py similarity index 100% rename from Python/src/experimental/Mining/include/AIPhosphate.py rename to experimental/Python/src/Mining/include/AIPhosphate.py diff --git a/Python/src/experimental/Mining/include/hbphosphate.py b/experimental/Python/src/Mining/include/hbphosphate.py similarity index 100% rename from Python/src/experimental/Mining/include/hbphosphate.py rename to experimental/Python/src/Mining/include/hbphosphate.py diff --git a/Python/src/experimental/Mining/include/images/Blinky.png b/experimental/Python/src/Mining/include/images/Blinky.png similarity index 100% rename from Python/src/experimental/Mining/include/images/Blinky.png rename to experimental/Python/src/Mining/include/images/Blinky.png diff --git a/Python/src/experimental/Mining/include/images/Clyde.png b/experimental/Python/src/Mining/include/images/Clyde.png similarity index 100% rename from Python/src/experimental/Mining/include/images/Clyde.png rename to experimental/Python/src/Mining/include/images/Clyde.png diff --git a/Python/src/experimental/Mining/include/images/Inky.png b/experimental/Python/src/Mining/include/images/Inky.png similarity index 100% rename from Python/src/experimental/Mining/include/images/Inky.png rename to experimental/Python/src/Mining/include/images/Inky.png diff --git a/Python/src/experimental/Mining/include/images/Pinky.png b/experimental/Python/src/Mining/include/images/Pinky.png similarity index 100% rename from Python/src/experimental/Mining/include/images/Pinky.png rename to experimental/Python/src/Mining/include/images/Pinky.png diff --git a/Python/src/experimental/Mining/include/images/Trollman.png b/experimental/Python/src/Mining/include/images/Trollman.png similarity index 100% rename from Python/src/experimental/Mining/include/images/Trollman.png rename to experimental/Python/src/Mining/include/images/Trollman.png diff --git a/Python/src/experimental/Mining/include/images/pacman.jpg b/experimental/Python/src/Mining/include/images/pacman.jpg similarity index 100% rename from Python/src/experimental/Mining/include/images/pacman.jpg rename to experimental/Python/src/Mining/include/images/pacman.jpg diff --git a/Python/src/experimental/Mining/include/images/pacman.png b/experimental/Python/src/Mining/include/images/pacman.png similarity index 100% rename from Python/src/experimental/Mining/include/images/pacman.png rename to experimental/Python/src/Mining/include/images/pacman.png diff --git a/Python/src/experimental/Mining/include/images/roomb1.png b/experimental/Python/src/Mining/include/images/roomb1.png similarity index 100% rename from Python/src/experimental/Mining/include/images/roomb1.png rename to experimental/Python/src/Mining/include/images/roomb1.png diff --git a/Python/src/experimental/Mining/include/images/roomba.png b/experimental/Python/src/Mining/include/images/roomba.png similarity index 100% rename from Python/src/experimental/Mining/include/images/roomba.png rename to experimental/Python/src/Mining/include/images/roomba.png diff --git a/Python/src/experimental/Mining/include/images/scaredghost.png b/experimental/Python/src/Mining/include/images/scaredghost.png similarity index 100% rename from Python/src/experimental/Mining/include/images/scaredghost.png rename to experimental/Python/src/Mining/include/images/scaredghost.png diff --git a/Python/src/experimental/Mining/include/images/user.png b/experimental/Python/src/Mining/include/images/user.png similarity index 100% rename from Python/src/experimental/Mining/include/images/user.png rename to experimental/Python/src/Mining/include/images/user.png diff --git a/Python/src/experimental/Mining/include/images/wheelchair.png b/experimental/Python/src/Mining/include/images/wheelchair.png similarity index 100% rename from Python/src/experimental/Mining/include/images/wheelchair.png rename to experimental/Python/src/Mining/include/images/wheelchair.png diff --git a/Python/src/experimental/Pac-Man/BankedObserver.lf b/experimental/Python/src/Pac-Man/BankedObserver.lf similarity index 100% rename from Python/src/experimental/Pac-Man/BankedObserver.lf rename to experimental/Python/src/Pac-Man/BankedObserver.lf diff --git a/Python/src/experimental/Pac-Man/ContainedPlayer.lf b/experimental/Python/src/Pac-Man/ContainedPlayer.lf similarity index 100% rename from Python/src/experimental/Pac-Man/ContainedPlayer.lf rename to experimental/Python/src/Pac-Man/ContainedPlayer.lf diff --git a/Python/src/experimental/Pac-Man/Foomba.lf b/experimental/Python/src/Pac-Man/Foomba.lf similarity index 100% rename from Python/src/experimental/Pac-Man/Foomba.lf rename to experimental/Python/src/Pac-Man/Foomba.lf diff --git a/Python/src/experimental/Pac-Man/KielPacManTree.lf b/experimental/Python/src/Pac-Man/KielPacManTree.lf similarity index 100% rename from Python/src/experimental/Pac-Man/KielPacManTree.lf rename to experimental/Python/src/Pac-Man/KielPacManTree.lf diff --git a/Python/src/experimental/Pac-Man/PacMan.lf b/experimental/Python/src/Pac-Man/PacMan.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacMan.lf rename to experimental/Python/src/Pac-Man/PacMan.lf diff --git a/Python/src/experimental/Pac-Man/PacManKielTree.lf b/experimental/Python/src/Pac-Man/PacManKielTree.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManKielTree.lf rename to experimental/Python/src/Pac-Man/PacManKielTree.lf diff --git a/Python/src/experimental/Pac-Man/PacManNMBT.lf b/experimental/Python/src/Pac-Man/PacManNMBT.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManNMBT.lf rename to experimental/Python/src/Pac-Man/PacManNMBT.lf diff --git a/Python/src/experimental/Pac-Man/PacManTesting.lf b/experimental/Python/src/Pac-Man/PacManTesting.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManTesting.lf rename to experimental/Python/src/Pac-Man/PacManTesting.lf diff --git a/Python/src/experimental/Pac-Man/PacManWFrenzy.lf b/experimental/Python/src/Pac-Man/PacManWFrenzy.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManWFrenzy.lf rename to experimental/Python/src/Pac-Man/PacManWFrenzy.lf diff --git a/Python/src/experimental/Pac-Man/PacManWModalBT.lf b/experimental/Python/src/Pac-Man/PacManWModalBT.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManWModalBT.lf rename to experimental/Python/src/Pac-Man/PacManWModalBT.lf diff --git a/Python/src/experimental/Pac-Man/PacManWRestart.lf b/experimental/Python/src/Pac-Man/PacManWRestart.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManWRestart.lf rename to experimental/Python/src/Pac-Man/PacManWRestart.lf diff --git a/Python/src/experimental/Pac-Man/PacManWithBank.lf b/experimental/Python/src/Pac-Man/PacManWithBank.lf similarity index 100% rename from Python/src/experimental/Pac-Man/PacManWithBank.lf rename to experimental/Python/src/Pac-Man/PacManWithBank.lf diff --git a/Python/src/experimental/Pac-Man/README.md b/experimental/Python/src/Pac-Man/README.md similarity index 100% rename from Python/src/experimental/Pac-Man/README.md rename to experimental/Python/src/Pac-Man/README.md diff --git a/Python/src/experimental/Pac-Man/include/AIPacSupport.py b/experimental/Python/src/Pac-Man/include/AIPacSupport.py similarity index 100% rename from Python/src/experimental/Pac-Man/include/AIPacSupport.py rename to experimental/Python/src/Pac-Man/include/AIPacSupport.py diff --git a/Python/src/experimental/Pac-Man/include/hbpacman.py b/experimental/Python/src/Pac-Man/include/hbpacman.py similarity index 100% rename from Python/src/experimental/Pac-Man/include/hbpacman.py rename to experimental/Python/src/Pac-Man/include/hbpacman.py diff --git a/Python/src/experimental/Pac-Man/include/images/Blinky.png b/experimental/Python/src/Pac-Man/include/images/Blinky.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/Blinky.png rename to experimental/Python/src/Pac-Man/include/images/Blinky.png diff --git a/Python/src/experimental/Pac-Man/include/images/Clyde.png b/experimental/Python/src/Pac-Man/include/images/Clyde.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/Clyde.png rename to experimental/Python/src/Pac-Man/include/images/Clyde.png diff --git a/Python/src/experimental/Pac-Man/include/images/Inky.png b/experimental/Python/src/Pac-Man/include/images/Inky.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/Inky.png rename to experimental/Python/src/Pac-Man/include/images/Inky.png diff --git a/Python/src/experimental/Pac-Man/include/images/Pinky.png b/experimental/Python/src/Pac-Man/include/images/Pinky.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/Pinky.png rename to experimental/Python/src/Pac-Man/include/images/Pinky.png diff --git a/Python/src/experimental/Pac-Man/include/images/Trollman.png b/experimental/Python/src/Pac-Man/include/images/Trollman.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/Trollman.png rename to experimental/Python/src/Pac-Man/include/images/Trollman.png diff --git a/Python/src/experimental/Pac-Man/include/images/pacman.jpg b/experimental/Python/src/Pac-Man/include/images/pacman.jpg similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/pacman.jpg rename to experimental/Python/src/Pac-Man/include/images/pacman.jpg diff --git a/Python/src/experimental/Pac-Man/include/images/pacman.png b/experimental/Python/src/Pac-Man/include/images/pacman.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/pacman.png rename to experimental/Python/src/Pac-Man/include/images/pacman.png diff --git a/Python/src/experimental/Pac-Man/include/images/roomb1.png b/experimental/Python/src/Pac-Man/include/images/roomb1.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/roomb1.png rename to experimental/Python/src/Pac-Man/include/images/roomb1.png diff --git a/Python/src/experimental/Pac-Man/include/images/roomba.png b/experimental/Python/src/Pac-Man/include/images/roomba.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/roomba.png rename to experimental/Python/src/Pac-Man/include/images/roomba.png diff --git a/Python/src/experimental/Pac-Man/include/images/scaredghost.png b/experimental/Python/src/Pac-Man/include/images/scaredghost.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/scaredghost.png rename to experimental/Python/src/Pac-Man/include/images/scaredghost.png diff --git a/Python/src/experimental/Pac-Man/include/images/user.png b/experimental/Python/src/Pac-Man/include/images/user.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/user.png rename to experimental/Python/src/Pac-Man/include/images/user.png diff --git a/Python/src/experimental/Pac-Man/include/images/wheelchair.png b/experimental/Python/src/Pac-Man/include/images/wheelchair.png similarity index 100% rename from Python/src/experimental/Pac-Man/include/images/wheelchair.png rename to experimental/Python/src/Pac-Man/include/images/wheelchair.png diff --git a/Python/src/experimental/Pac-Man/oldWBank.lf b/experimental/Python/src/Pac-Man/oldWBank.lf similarity index 100% rename from Python/src/experimental/Pac-Man/oldWBank.lf rename to experimental/Python/src/Pac-Man/oldWBank.lf diff --git a/Python/src/experimental/Testing/HistoryMRTest.lf b/experimental/Python/src/Testing/HistoryMRTest.lf similarity index 100% rename from Python/src/experimental/Testing/HistoryMRTest.lf rename to experimental/Python/src/Testing/HistoryMRTest.lf From 176bf753106947c131261eecdc3be56112ff2d17 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 20 Jun 2023 13:06:31 -0700 Subject: [PATCH 2/3] Tabs -> spaces; 4 spaces -> 2 spaces. --- C/src/ChatApplication/SimpleChat.lf | 108 +- C/src/Delay.lf | 64 +- .../DistributedDatabase/FederatedDatabase.lf | 36 +- .../DistributedDatabase/ReplicatedDatabase.lf | 276 +-- .../ReplicatedDatabaseThree.lf | 48 +- C/src/DistributedHelloWorld/HelloWorld.lf | 68 +- .../DistributedHelloWorld/HelloWorldAfter.lf | 10 +- .../HelloWorldDecentralized.lf | 54 +- .../HelloWorldDecentralizedSTP.lf | 20 +- .../HelloWorldPhysical.lf | 10 +- .../HelloWorldPhysicalAfter.lf | 10 +- .../docker/HelloWorldContainerized.lf | 12 +- .../FederatedResourceManagement.lf | 42 +- .../ResourceManagement.lf | 606 ++--- C/src/Parallelism/ForkJoin.lf | 78 +- C/src/Parallelism/Pipeline.lf | 78 +- C/src/ProtocolBuffers/HelloProtocolBuffers.lf | 60 +- C/src/ROS/BasicROS.lf | 226 +- C/src/ROS/PTIDES-ROS.lf | 234 +- C/src/ReflexGame/ReflexGame.lf | 184 +- C/src/ReflexGame/ReflexGameTest.lf | 84 +- C/src/RockPaperScissors.lf | 64 +- C/src/SleepingBarber.lf | 428 ++-- C/src/Smokers.lf | 128 +- C/src/TrainDoor/TrainDoor.lf | 184 +- C/src/TrainDoor/TrainDoorAsymmetric.lf | 24 +- C/src/TrainDoor/TrainDoorSimplest.lf | 14 +- C/src/TrainDoor/TrainDoorWithDeadlines.lf | 34 +- C/src/TrainDoor/TrainDoorWithDoorOpenState.lf | 36 +- C/src/TrainDoor/TrainDoorWithOpen.lf | 32 +- C/src/browser-ui/BrowserUI.lf | 336 +-- C/src/browser-ui/WebSocket.lf | 102 +- C/src/browser-ui/WebSocketServer.lf | 504 ++--- C/src/car-brake/CarBrake.lf | 148 +- C/src/car-brake/CarBrake2.lf | 10 +- C/src/car-brake/CarBrake3.lf | 10 +- C/src/deadlines/AnytimePrime.lf | 72 +- C/src/deadlines/Deadline.lf | 124 +- C/src/deadlines/PeriodicDeadline.lf | 130 +- C/src/keyboard/Keyboard.lf | 100 +- C/src/leader-election/Election.lf | 62 +- C/src/lib/PoissonClock.lf | 34 +- C/src/lib/PrintToFile.lf | 30 +- C/src/lib/Random.lf | 30 +- C/src/lib/RandomDelay.lf | 26 +- .../FurutaPendulum/FurutaPendulum.lf | 30 +- .../FurutaPendulumDisturbance.lf | 40 +- .../FurutaPendulum/PendulumController.lf | 210 +- .../FurutaPendulum/PendulumSimulation.lf | 260 +-- C/src/modal_models/FurutaPendulum/Print.lf | 62 +- C/src/mqtt/MQTTDistributed.lf | 44 +- C/src/mqtt/MQTTDistributedActivity.lf | 56 +- C/src/mqtt/MQTTLegacy.lf | 56 +- C/src/mqtt/MQTTLogical.lf | 42 +- C/src/mqtt/MQTTPhysical.lf | 40 +- C/src/mqtt/lib/MQTTPublisher.lf | 342 +-- C/src/mqtt/lib/MQTTSubscriber.lf | 408 ++-- C/src/mqtt/lib/MQTTTestReactors.lf | 58 +- C/src/patterns/Chain_01_SendReceive.lf | 6 +- C/src/patterns/Chain_02_Pipeline.lf | 12 +- C/src/patterns/FullyConnected_00_Broadcast.lf | 50 +- .../patterns/FullyConnected_01_Addressable.lf | 44 +- C/src/patterns/Loop_01_Single.lf | 8 +- C/src/patterns/Loop_02_SingleDelay.lf | 10 +- C/src/patterns/lib/SendersAndReceivers.lf | 218 +- C/src/patterns/lib/TakeTime.lf | 50 +- C/src/rhythm/PlayWaveform.lf | 144 +- C/src/rhythm/Rhythm.lf | 478 ++-- C/src/rhythm/RhythmDistributed.lf | 40 +- C/src/rhythm/RhythmDistributedNoUI.lf | 48 +- C/src/rhythm/SensorSimulator.lf | 60 +- C/src/rhythm/Sound.lf | 76 +- C/src/robot/CompositeRobot.lf | 134 +- C/src/rosace/AircraftSimulator.lf | 330 +-- C/src/rosace/Rosace.lf | 70 +- C/src/rosace/RosaceController.lf | 408 ++-- C/src/rosace/RosaceWithUI.lf | 116 +- C/src/sdv/ParkingAssist.lf | 170 +- C/src/simulation/MemoryHierarchy.lf | 118 +- C/src/simulation/PoissonProcess.lf | 42 +- CCpp/src/DoorLock/DoorLock.lf | 202 +- CCpp/src/DoorLock/lib/AuthSim.lf | 22 +- CCpp/src/DoorLock/lib/PropagationDelaySim.lf | 22 +- CCpp/src/DoorLock/lib/UserInteraction.lf | 144 +- .../ROS/MigrationGuide/lf-project/src/Main.lf | 8 +- .../MigrationGuide/lf-project/src/Receiver.lf | 38 +- .../MigrationGuide/lf-project/src/Sender.lf | 46 +- CCpp/src/ROS/ROSBuiltInSerialization.lf | 60 +- CCpp/src/ROS/ROSSerialization.lf | 136 +- Cpp/AlarmClock/src/AlarmClock.lf | 22 +- Cpp/AlarmClock/src/Clock.lf | 364 +-- Cpp/AlarmClock/src/Network.lf | 400 ++-- Cpp/CarBrake/src/CarBrake.lf | 134 +- Cpp/CarBrake/src/CarBrake2.lf | 158 +- .../src/FullyConnected_00_Broadcast.lf | 36 +- .../src/FullyConnected_01_Addressable.lf | 36 +- .../src/MatrixConnectedRowsAndColumns.lf | 88 +- Cpp/ROS2/src/MinimalPublisher.lf | 40 +- Cpp/ROS2/src/MinimalSubscriber.lf | 42 +- Cpp/ReflexGame/src/ReflexGame.lf | 192 +- Cpp/RequestResponse/src/Add.lf | 36 +- Cpp/RequestResponse/src/AddService.lf | 26 +- Cpp/RequestResponse/src/AddWithContext.lf | 66 +- Cpp/RequestResponse/src/ContextManager.lf | 36 +- Cpp/RequestResponse/src/MAC.lf | 156 +- Cpp/RequestResponse/src/MultiplyService.lf | 26 +- .../DoubleUnlock/DoubleUnlockDemo.lf | 352 +-- .../src/DigitalTwin/DoubleUnlock/Simulator.lf | 88 +- Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf | 208 +- Python/src/Piano/Piano.lf | 372 ++-- .../src/ROS/PythonMigration/lf-python/Main.lf | 6 +- .../ROS/PythonMigration/lf-python/Receiver.lf | 68 +- .../ROS/PythonMigration/lf-python/Sender.lf | 66 +- Python/src/ReflexGame/ReflexGame.lf | 300 +-- Python/src/TrainDoor/TrainDoor.lf | 176 +- Python/src/YOLOv5/YOLOv5_Webcam.lf | 334 +-- Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf | 76 +- Python/src/acas/ACASXu.lf | 92 +- Python/src/acas/ACASXu2.lf | 114 +- Python/src/acas/lib/ACASController.lf | 174 +- Python/src/acas/lib/ACASNN.lf | 96 +- Python/src/acas/lib/Aircraft.lf | 82 +- Python/src/acas/lib/XYPlotter.lf | 60 +- Rust/src/CounterProgram.lf | 40 +- Rust/src/Snake/KeyboardEvents.lf | 82 +- Rust/src/Snake/Snake.lf | 228 +- TypeScript/src/ChatApplication/SimpleChat.lf | 76 +- .../src/DistributedHelloWorld/HelloWorld.lf | 44 +- .../src/HTTPSRequestReactor/HTTPSRequest.lf | 60 +- .../src/HTTPServerReactor/HTTPServer.lf | 72 +- TypeScript/src/SimpleWebserver.lf | 64 +- experimental/C/src/Autoware/Autoware.lf | 444 ++-- experimental/C/src/Controller/ResponseTime.lf | 84 +- .../C/src/Controller/ResponseTime2.lf | 104 +- .../C/src/Controller/ResponseTime3.lf | 144 +- .../C/src/Intersection/Intersection.lf | 242 +- experimental/C/src/Logic/Logic.lf | 80 +- experimental/C/src/Microsteps/Anomaly.lf | 86 +- .../PacMan_BehaviorTree_Mockup_Modes.lf | 56 +- ...cMan_BehaviorTree_Mockup_Modes_Triggers.lf | 66 +- .../PacMan_BehaviorTree_Mockup_Reactors.lf | 128 +- .../BehaviorTrees/robohub_example_advanced.lf | 212 +- .../BehaviorTrees/robohub_example_simple.lf | 178 +- .../ModalModels/Motivation/Chrono/Chrono.lf | 228 +- .../Motivation/SineAvgMax/sine_max_avg.lf | 212 +- .../Motivation/SineAvgMax/sine_max_avg_v2.lf | 94 +- .../ModalModels/ReflexGame/ModalReflexGame.lf | 208 +- experimental/C/src/Mutations/ScatterGather.lf | 196 +- .../C/src/Mutations/SieveOfEratosthenes.lf | 210 +- experimental/C/src/PowerTrain/PowerTrain.lf | 390 ++-- .../SieveOfErastothenes.lf | 78 +- .../SieveOfErastothenesUnrolled.lf | 628 +++--- experimental/C/src/Safety/Cutter.lf | 110 +- experimental/C/src/SolarUAV/SolarUAV.lf | 360 +-- .../C/src/SpatAnalysis/MQTTPublisher.lf | 274 +-- .../C/src/SpatAnalysis/MQTTSubscriber.lf | 384 ++-- .../C/src/SpatAnalysis/influxWrite.lf | 234 +- .../C/src/SpatAnalysis/spatReceiver.lf | 108 +- .../C/src/SpatAnalysis/spatRecommender.lf | 248 +-- .../C/src/context-manager/ContextManager.lf | 216 +- .../C/src/context-manager/ContextManager2.lf | 264 +-- .../C/src/protection-relay/ProtectionRelay.lf | 160 +- experimental/C/src/soafee/soafee.lf | 208 +- .../FurutaPendulum/FurutaPendulumStabilize.lf | 96 +- .../FurutaPendulum/PendulumSimulationEuler.lf | 236 +- .../FurutaPendulum/PendulumSimulationRK4.lf | 292 +-- .../Python/src/FurutaPendulum/SmallTest.lf | 160 +- .../Python/src/GeneralModels/bankedsimius.lf | 136 +- .../Python/src/GeneralModels/headedswarm.lf | 160 +- .../Python/src/GeneralModels/queenant.lf | 122 +- .../Python/src/GeneralModels/simius.lf | 166 +- .../Python/src/GeneralModels/swarm.lf | 96 +- .../Python/src/GeneralModels/tester.lf | 602 ++--- .../Python/src/Intersection/Intersection.lf | 908 ++++---- experimental/Python/src/Mining/BusyMine.lf | 1726 +++++++-------- experimental/Python/src/Mining/MineTest.lf | 1156 +++++----- .../Python/src/Mining/MineWObserver.lf | 1418 ++++++------ experimental/Python/src/Mining/ModalMine.lf | 1972 ++++++++--------- .../Python/src/Mining/PhosphateMine.lf | 1386 ++++++------ .../Python/src/Mining/SingleModeMine.lf | 1216 +++++----- .../Python/src/Pac-Man/BankedObserver.lf | 936 ++++---- .../Python/src/Pac-Man/ContainedPlayer.lf | 538 ++--- experimental/Python/src/Pac-Man/Foomba.lf | 1218 +++++----- .../Python/src/Pac-Man/KielPacManTree.lf | 464 ++-- experimental/Python/src/Pac-Man/PacMan.lf | 746 +++---- .../Python/src/Pac-Man/PacManKielTree.lf | 1324 +++++------ experimental/Python/src/Pac-Man/PacManNMBT.lf | 1166 +++++----- .../Python/src/Pac-Man/PacManTesting.lf | 1262 +++++------ .../Python/src/Pac-Man/PacManWFrenzy.lf | 1288 +++++------ .../Python/src/Pac-Man/PacManWModalBT.lf | 1416 ++++++------ .../Python/src/Pac-Man/PacManWRestart.lf | 748 +++---- .../Python/src/Pac-Man/PacManWithBank.lf | 872 ++++---- experimental/Python/src/Pac-Man/oldWBank.lf | 1248 +++++------ .../Python/src/Testing/HistoryMRTest.lf | 74 +- 194 files changed, 23449 insertions(+), 23449 deletions(-) diff --git a/C/src/ChatApplication/SimpleChat.lf b/C/src/ChatApplication/SimpleChat.lf index 631787d9..10bcb7ab 100644 --- a/C/src/ChatApplication/SimpleChat.lf +++ b/C/src/ChatApplication/SimpleChat.lf @@ -15,82 +15,82 @@ * @author Hokeun Kim (hokeunkim@berkeley.edu) */ target C { - keepalive: true, - coordination-options: { - advance-message-interval: 10 msec - } + keepalive: true, + coordination-options: { + advance-message-interval: 10 msec + } } preamble {= - #include + #include =} reactor InputHandler { - preamble {= - // Global buffer. - char buf[256]; - void* read_input(void* response) { - int c; - int i = 0; - while(1) { - printf("Press Enter a message.\n"); - while((c = getchar()) != '\n') { - buf[i++] = c; - if (c == EOF) { - lf_request_stop(); - break; - } - } - buf[i] = 0; - printf("User input: %s\n", buf); - i = 0; + preamble {= + // Global buffer. + char buf[256]; + void* read_input(void* response) { + int c; + int i = 0; + while(1) { + printf("Press Enter a message.\n"); + while((c = getchar()) != '\n') { + buf[i++] = c; + if (c == EOF) { + lf_request_stop(); + break; + } + } + buf[i] = 0; + printf("User input: %s\n", buf); + i = 0; - char* ptr = buf; - // The following copies the char*, not the string. - lf_schedule_copy(response, 0, &ptr, 1); - if (c == EOF) { - break; - } - } - return NULL; + char* ptr = buf; + // The following copies the char*, not the string. + lf_schedule_copy(response, 0, &ptr, 1); + if (c == EOF) { + break; } - =} + } + return NULL; + } + =} - physical action response: string - output out: string + physical action response: string + output out: string - reaction(startup) -> response {= - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, response); - =} + reaction(startup) -> response {= + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, response); + =} - reaction(response) -> out {= - printf("Reacting to physical action at %lld\n", lf_time_logical_elapsed()); - lf_set(out, response->value); - =} + reaction(response) -> out {= + printf("Reacting to physical action at %lld\n", lf_time_logical_elapsed()); + lf_set(out, response->value); + =} } reactor Printer { - input in: string + input in: string - reaction(in) {= printf("Received: %s\n", in->value); =} + reaction(in) {= printf("Received: %s\n", in->value); =} } reactor ChatHandler { - input receive: string - output send: string - u = new InputHandler() - r = new Printer() + input receive: string + output send: string + u = new InputHandler() + r = new Printer() - reaction(u.out) -> send {= lf_set(send, u.out->value); =} + reaction(u.out) -> send {= lf_set(send, u.out->value); =} - reaction(receive) -> r.in {= lf_set(r.in, receive->value); =} + reaction(receive) -> r.in {= lf_set(r.in, receive->value); =} } federated reactor SimpleChat { - a = new ChatHandler() + a = new ChatHandler() - b = new ChatHandler() - b.send -> a.receive - a.send -> b.receive + b = new ChatHandler() + b.send -> a.receive + a.send -> b.receive } diff --git a/C/src/Delay.lf b/C/src/Delay.lf index 1ca8f6ef..4538f4d0 100644 --- a/C/src/Delay.lf +++ b/C/src/Delay.lf @@ -9,54 +9,54 @@ target C {timeout: 1 sec}; main reactor { - ramp = new Ramp(); - delay = new Delay2(); - print = new Print(); - ramp.y -> delay.x; - delay.y -> print.x; - - ramp2 = new Ramp(); - print2 = new Print(); - ramp2.y -> print2.x after 50 msec; + ramp = new Ramp(); + delay = new Delay2(); + print = new Print(); + ramp.y -> delay.x; + delay.y -> print.x; + + ramp2 = new Ramp(); + print2 = new Print(); + ramp2.y -> print2.x after 50 msec; } /** * Generate a counting sequence with outputs every 100 msec. */ reactor Ramp { - timer t(0, 100 msec); - output y:int; - state count:int(0); - reaction(t) -> y {= - lf_set(y, self->count); - self->count++; - =} + timer t(0, 100 msec); + output y:int; + state count:int(0); + reaction(t) -> y {= + lf_set(y, self->count); + self->count++; + =} } /** * Realize a logical delay of 50 msec. */ reactor Delay2 { - logical action a(50 msec):int; - input x:int; - output y:int; - reaction(a) -> y {= - lf_set(y, a->value); - =} - reaction(x) -> a {= - lf_schedule_int(a, 0, x->value); - =} + logical action a(50 msec):int; + input x:int; + output y:int; + reaction(a) -> y {= + lf_set(y, a->value); + =} + reaction(x) -> a {= + lf_schedule_int(a, 0, x->value); + =} } /** * Print the (elapsed) logical and physical times at which inputs are received. */ reactor Print { - input x:int; - reaction(x) {= - printf("Logical time: %lld, Physical time %lld" - ", Value: %d\n", - lf_time_logical_elapsed(), - lf_time_physical_elapsed(), x->value); - =} + input x:int; + reaction(x) {= + printf("Logical time: %lld, Physical time %lld" + ", Value: %d\n", + lf_time_logical_elapsed(), + lf_time_physical_elapsed(), x->value); + =} } diff --git a/C/src/DistributedDatabase/FederatedDatabase.lf b/C/src/DistributedDatabase/FederatedDatabase.lf index 2e6a5626..ff3a1ef8 100644 --- a/C/src/DistributedDatabase/FederatedDatabase.lf +++ b/C/src/DistributedDatabase/FederatedDatabase.lf @@ -6,28 +6,28 @@ * @author Soroush Bateni */ target C { - timeout: 5 sec, - coordination: centralized + timeout: 5 sec, + coordination: centralized } import Platform from "ReplicatedDatabase.lf" federated reactor ( - query_period:time(1 sec), - num_remote_inputs:int(1) + query_period:time(1 sec), + num_remote_inputs:int(1) ) { - a = new Platform( - query_period = query_period, - update_period = 5 sec, - update_amount = 100, - name = "San Francisco", - num_remote_inputs = num_remote_inputs); - b = new Platform( - query_period = query_period, - update_period = 1 sec, - update_amount = -20, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - b.publish -> a.update; - a.publish -> b.update; + a = new Platform( + query_period = query_period, + update_period = 5 sec, + update_amount = 100, + name = "San Francisco", + num_remote_inputs = num_remote_inputs); + b = new Platform( + query_period = query_period, + update_period = 1 sec, + update_amount = -20, + name = "Berkeley", + num_remote_inputs = num_remote_inputs); + b.publish -> a.update; + a.publish -> b.update; } diff --git a/C/src/DistributedDatabase/ReplicatedDatabase.lf b/C/src/DistributedDatabase/ReplicatedDatabase.lf index 264bd998..b10696c5 100644 --- a/C/src/DistributedDatabase/ReplicatedDatabase.lf +++ b/C/src/DistributedDatabase/ReplicatedDatabase.lf @@ -15,27 +15,27 @@ * @author Soroush Bateni */ target C { - timeout: 5 sec + timeout: 5 sec } main reactor ( - query_period:time(1 sec), - num_remote_inputs:int(1) + query_period:time(1 sec), + num_remote_inputs:int(1) ) { - a = new Platform( - query_period = query_period, - update_period = 5 sec, - update_amount = 100, - name = "San Francisco", - num_remote_inputs = num_remote_inputs); - b = new Platform( - query_period = query_period, - update_period = 1 sec, - update_amount = -20, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - b.publish -> a.update; - a.publish -> b.update; + a = new Platform( + query_period = query_period, + update_period = 5 sec, + update_amount = 100, + name = "San Francisco", + num_remote_inputs = num_remote_inputs); + b = new Platform( + query_period = query_period, + update_period = 1 sec, + update_amount = -20, + name = "Berkeley", + num_remote_inputs = num_remote_inputs); + b.publish -> a.update; + a.publish -> b.update; } /** @@ -62,56 +62,56 @@ main reactor ( * @output update Issue an update. */ reactor Server( - name:char*("unnamed server"), - query_period:time(150 msec), - update_period:time(100 msec), - update_deadline:time(200 msec), - update_amount:int(0), - server_name:char*("unnamed server") + name:char*("unnamed server"), + query_period:time(150 msec), + update_period:time(100 msec), + update_deadline:time(200 msec), + update_amount:int(0), + server_name:char*("unnamed server") ) { - timer query_trigger(0, query_period); - timer update_trigger(0, update_period); - input reply:int; - output query:bool; - output update:int; - state queries_outstanding:int(0); - reaction(query_trigger) -> query {= - lf_set(query, true); - self->queries_outstanding++; - =} - - reaction(update_trigger) -> update {= - lf_set(update, self->update_amount); - =} deadline(update_deadline) {= - tag_t tag = lf_tag(); - lf_print_error("At tag (%lld, %u), deadline missed at database \"%s\". Rejecting update.\n" - " Elapsed physical time is %lld.", - tag.time - lf_time_start(), - tag.microstep, - self->name, - lf_time_physical_elapsed() - ); - =} - - reaction(reply) {= - lf_print("***** At tag (%lld, %u), server \"%s\" reports balance: %d.", - lf_time_logical_elapsed(), lf_tag().microstep, self->server_name, reply->value - ); - self->queries_outstanding--; - =} - - reaction(shutdown) {= - if (self->queries_outstanding != 0) { - lf_print_error("Server \"%s\": Number of queries with no reply: %d.", - self->server_name, - self->queries_outstanding - ); - } else { - lf_print("Server \"%s\" successfully replied to all queries.", - self->server_name - ); - } - =} + timer query_trigger(0, query_period); + timer update_trigger(0, update_period); + input reply:int; + output query:bool; + output update:int; + state queries_outstanding:int(0); + reaction(query_trigger) -> query {= + lf_set(query, true); + self->queries_outstanding++; + =} + + reaction(update_trigger) -> update {= + lf_set(update, self->update_amount); + =} deadline(update_deadline) {= + tag_t tag = lf_tag(); + lf_print_error("At tag (%lld, %u), deadline missed at database \"%s\". Rejecting update.\n" + " Elapsed physical time is %lld.", + tag.time - lf_time_start(), + tag.microstep, + self->name, + lf_time_physical_elapsed() + ); + =} + + reaction(reply) {= + lf_print("***** At tag (%lld, %u), server \"%s\" reports balance: %d.", + lf_time_logical_elapsed(), lf_tag().microstep, self->server_name, reply->value + ); + self->queries_outstanding--; + =} + + reaction(shutdown) {= + if (self->queries_outstanding != 0) { + lf_print_error("Server \"%s\": Number of queries with no reply: %d.", + self->server_name, + self->queries_outstanding + ); + } else { + lf_print("Server \"%s\" successfully replied to all queries.", + self->server_name + ); + } + =} } /** @@ -154,59 +154,59 @@ reactor Server( * @output balance The time value of the record. */ reactor Database( - name:char*("unnamed database"), - num_remote_inputs:int(1) + name:char*("unnamed database"), + num_remote_inputs:int(1) ) { - input local_update:int; - input[num_remote_inputs] remote_update:int; - input query:bool; - output balance:int; - state record:int(0); - - reaction(local_update, remote_update) {= - if (local_update->is_present) { - self->record += local_update->value; - } - for (int i = 0; i < remote_update_width; i++) { - if (remote_update[i]->is_present) { - self->record += remote_update[i]->value; - } - } - lf_print("At tag (%lld, %u), database \"%s\" updated balance to %d.\n" - " Elapsed physical time is %lld.", - lf_time_logical() - lf_time_start(), lf_tag().microstep, - self->name, - self->record, - lf_time_physical_elapsed() - ); - =} STP (4 msec) {= + input local_update:int; + input[num_remote_inputs] remote_update:int; + input query:bool; + output balance:int; + state record:int(0); + + reaction(local_update, remote_update) {= + if (local_update->is_present) { + self->record += local_update->value; + } + for (int i = 0; i < remote_update_width; i++) { + if (remote_update[i]->is_present) { + self->record += remote_update[i]->value; + } + } + lf_print("At tag (%lld, %u), database \"%s\" updated balance to %d.\n" + " Elapsed physical time is %lld.", + lf_time_logical() - lf_time_start(), lf_tag().microstep, + self->name, + self->record, + lf_time_physical_elapsed() + ); + =} STP (4 msec) {= #ifdef FEDERATED_DECENTRALIZED - for (int i = 0; i < remote_update_width; i++) { - if (remote_update[i]->is_present) { - lf_print_warning("At tag (%lld, %u), database \"%s\" " - "received remote update (%d) with intended tag (%lld, %u).\n" - " Balance previously reported may have been incorrect.\n" - " Elapsed physical time is %lld.", - current_tag.time - lf_time_start(), - current_tag.microstep, - self->name, - remote_update[i]->value, - remote_update[i]->intended_tag.time - lf_time_start(), - remote_update[i]->intended_tag.microstep, - lf_time_physical_elapsed() - ); - self->record += remote_update[i]->value; - } - } + for (int i = 0; i < remote_update_width; i++) { + if (remote_update[i]->is_present) { + lf_print_warning("At tag (%lld, %u), database \"%s\" " + "received remote update (%d) with intended tag (%lld, %u).\n" + " Balance previously reported may have been incorrect.\n" + " Elapsed physical time is %lld.", + current_tag.time - lf_time_start(), + current_tag.microstep, + self->name, + remote_update[i]->value, + remote_update[i]->intended_tag.time - lf_time_start(), + remote_update[i]->intended_tag.microstep, + lf_time_physical_elapsed() + ); + self->record += remote_update[i]->value; + } + } #else - // The tardy handler should not be invoked - lf_print_error_and_exit("FATAL: Update is tardy and coordination is not decentralized."); + // The tardy handler should not be invoked + lf_print_error_and_exit("FATAL: Update is tardy and coordination is not decentralized."); #endif - =} - - reaction(query) -> balance {= - lf_set(balance, self->record); - =} + =} + + reaction(query) -> balance {= + lf_set(balance, self->record); + =} } /** @@ -229,29 +229,29 @@ reactor Database( * @output publish A copy of any local updates. */ reactor Platform( - query_period:time(150 msec), - update_period:time(100 msec), - update_amount:int(0), - num_remote_inputs:int(1), - name:char*("unnamed platform") // Used for more visible logging + query_period:time(150 msec), + update_period:time(100 msec), + update_amount:int(0), + num_remote_inputs:int(1), + name:char*("unnamed platform") // Used for more visible logging ) { - input[num_remote_inputs] update:int; - output publish:int; - server = new Server( - name = name, - query_period = query_period, - update_period = update_period, - update_amount = update_amount, - server_name = name - ); - database = new Database( - name = name, - num_remote_inputs = num_remote_inputs - ); - server.query -> database.query; - server.update -> database.local_update; - database.balance -> server.reply; - server.update -> publish; - update -> database.remote_update; + input[num_remote_inputs] update:int; + output publish:int; + server = new Server( + name = name, + query_period = query_period, + update_period = update_period, + update_amount = update_amount, + server_name = name + ); + database = new Database( + name = name, + num_remote_inputs = num_remote_inputs + ); + server.query -> database.query; + server.update -> database.local_update; + database.balance -> server.reply; + server.update -> publish; + update -> database.remote_update; } diff --git a/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf b/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf index 9fc0e651..74e7746f 100644 --- a/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf +++ b/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf @@ -5,34 +5,34 @@ * @author Soroush Bateni */ target C { - timeout: 5 sec + timeout: 5 sec } import Platform from "ReplicatedDatabase.lf" main reactor ( - query_period:time(1 sec), - num_remote_inputs:int(2) + query_period:time(1 sec), + num_remote_inputs:int(2) ) { - a = new Platform( - query_period = query_period, - update_period = 5 sec, - update_amount = 100, - name = "San Francisco", - num_remote_inputs = num_remote_inputs); - b = new Platform( - query_period = query_period, - update_period = 1 sec, - update_amount = -20, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - c = new Platform( - query_period = query_period, - update_period = 3 sec, - update_amount = 10, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - b.publish, c.publish -> a.update; - a.publish, c.publish -> b.update; - a.publish, b.publish -> c.update; + a = new Platform( + query_period = query_period, + update_period = 5 sec, + update_amount = 100, + name = "San Francisco", + num_remote_inputs = num_remote_inputs); + b = new Platform( + query_period = query_period, + update_period = 1 sec, + update_amount = -20, + name = "Berkeley", + num_remote_inputs = num_remote_inputs); + c = new Platform( + query_period = query_period, + update_period = 3 sec, + update_amount = 10, + name = "Berkeley", + num_remote_inputs = num_remote_inputs); + b.publish, c.publish -> a.update; + a.publish, c.publish -> b.update; + a.publish, b.publish -> c.update; } diff --git a/C/src/DistributedHelloWorld/HelloWorld.lf b/C/src/DistributedHelloWorld/HelloWorld.lf index 4ab98f55..46940a34 100644 --- a/C/src/DistributedHelloWorld/HelloWorld.lf +++ b/C/src/DistributedHelloWorld/HelloWorld.lf @@ -23,7 +23,7 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs + timeout: 10 secs }; /** @@ -34,28 +34,28 @@ target C { * @output message The message. */ reactor MessageGenerator(prefix:string("")) { - // Output type char* instead of string is used for dynamically - // allocated character arrays (as opposed to static constant strings). - output message:char*; - state count:int(1); - // Send first message after 1 sec so that the startup reactions - // do not factor into the transport time measurement on the first message. - timer t(1 sec, 1 sec); - reaction(t) -> message {= - // With NULL, 0 arguments, snprintf tells us how many bytes are needed. - // Add one for the null terminator. - int length = snprintf(NULL, 0, "%s %d", self->prefix, self->count) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(message, length); - // Populate the output string and increment the count. - snprintf(message->value, length, "%s %d", self->prefix, self->count++); + // Output type char* instead of string is used for dynamically + // allocated character arrays (as opposed to static constant strings). + output message:char*; + state count:int(1); + // Send first message after 1 sec so that the startup reactions + // do not factor into the transport time measurement on the first message. + timer t(1 sec, 1 sec); + reaction(t) -> message {= + // With NULL, 0 arguments, snprintf tells us how many bytes are needed. + // Add one for the null terminator. + int length = snprintf(NULL, 0, "%s %d", self->prefix, self->count) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(message, length); + // Populate the output string and increment the count. + snprintf(message->value, length, "%s %d", self->prefix, self->count++); - tag_t tag = lf_tag(); - lf_print("At (elapsed) logical tag (%lld, %u), source sends message: %s", - tag.time - lf_time_start(), tag.microstep, - message->value - ); - =} + tag_t tag = lf_tag(); + lf_print("At (elapsed) logical tag (%lld, %u), source sends message: %s", + tag.time - lf_time_start(), tag.microstep, + message->value + ); + =} } /** @@ -64,18 +64,18 @@ reactor MessageGenerator(prefix:string("")) { * @input message The message. */ reactor PrintMessage { - input message:char*; - reaction(message) {= - tag_t tag = lf_tag(); - lf_print("At (elapsed) logical tag (%lld, %u), print receives: %s", - tag.time - lf_time_start(), tag.microstep, - message->value - ); - =} + input message:char*; + reaction(message) {= + tag_t tag = lf_tag(); + lf_print("At (elapsed) logical tag (%lld, %u), print receives: %s", + tag.time - lf_time_start(), tag.microstep, + message->value + ); + =} } federated reactor HelloWorld { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage(); + source.message -> print.message; +} diff --git a/C/src/DistributedHelloWorld/HelloWorldAfter.lf b/C/src/DistributedHelloWorld/HelloWorldAfter.lf index 7ddb437f..64172656 100644 --- a/C/src/DistributedHelloWorld/HelloWorldAfter.lf +++ b/C/src/DistributedHelloWorld/HelloWorldAfter.lf @@ -6,13 +6,13 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs + timeout: 10 secs }; import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" federated reactor HelloWorldAfter { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message after 10 msec; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage(); + source.message -> print.message after 10 msec; +} diff --git a/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf b/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf index d45ec2da..d0862b9c 100644 --- a/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf +++ b/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf @@ -16,38 +16,38 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs, - coordination: decentralized + timeout: 10 secs, + coordination: decentralized }; import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" reactor PrintMessageWithDetector(offset:time(10 msec)) extends PrintMessage { - // The timer here creates a worst-case scenario where the receiving - // federate has an event to process whose timestamp matches that of an - // incoming message. Without this timer, you will not see any tardy - // messages because the `print` reactor has no reason to advance its - // logical time, and hence any incoming intended tag can be handled. - timer local(offset, 1 sec); - reaction (message) {= - // Empty. The base class will react and report the incoming message. - =} STP(0) {= - lf_print_warning("Message is tardy. Intended tag is (%lld, %u).", - message->intended_tag.time - start_time, message->intended_tag.microstep - ); - =} - reaction(local) {= - tag_t tag = lf_tag(); - lf_print("Timer triggered at logical tag (%lld, %u).", - tag.time - start_time, tag.microstep - ); - =} + // The timer here creates a worst-case scenario where the receiving + // federate has an event to process whose timestamp matches that of an + // incoming message. Without this timer, you will not see any tardy + // messages because the `print` reactor has no reason to advance its + // logical time, and hence any incoming intended tag can be handled. + timer local(offset, 1 sec); + reaction (message) {= + // Empty. The base class will react and report the incoming message. + =} STP(0) {= + lf_print_warning("Message is tardy. Intended tag is (%lld, %u).", + message->intended_tag.time - start_time, message->intended_tag.microstep + ); + =} + reaction(local) {= + tag_t tag = lf_tag(); + lf_print("Timer triggered at logical tag (%lld, %u).", + tag.time - start_time, tag.microstep + ); + =} } federated reactor HelloWorldDecentralized(offset:time(10 msec)) { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessageWithDetector( - offset = offset - ); - source.message -> print.message after offset; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessageWithDetector( + offset = offset + ); + source.message -> print.message after offset; +} diff --git a/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf b/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf index 41b3649b..50729a0b 100644 --- a/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf +++ b/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf @@ -24,8 +24,8 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs, - coordination: decentralized + timeout: 10 secs, + coordination: decentralized }; import MessageGenerator from "HelloWorld.lf" import PrintMessageWithDetector from "HelloWorldDecentralized.lf" @@ -34,14 +34,14 @@ import PrintMessageWithDetector from "HelloWorldDecentralized.lf" * Subclass that simply adds an `STP` parameter, nothing more. */ reactor PrintMessageWithSTP(STP_offset:time(10 msec)) extends PrintMessageWithDetector { - + } federated reactor { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessageWithSTP( - offset = 0, - STP_offset = 10 msec - ); - source.message -> print.message; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessageWithSTP( + offset = 0, + STP_offset = 10 msec + ); + source.message -> print.message; +} diff --git a/C/src/DistributedHelloWorld/HelloWorldPhysical.lf b/C/src/DistributedHelloWorld/HelloWorldPhysical.lf index 407dbf2e..fc70ce80 100644 --- a/C/src/DistributedHelloWorld/HelloWorldPhysical.lf +++ b/C/src/DistributedHelloWorld/HelloWorldPhysical.lf @@ -14,14 +14,14 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs + timeout: 10 secs }; import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" federated reactor HelloWorldPhysical { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message ~> print.message; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage(); + source.message ~> print.message; +} diff --git a/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf b/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf index 7d659e41..049c3117 100644 --- a/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf +++ b/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf @@ -11,13 +11,13 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs + timeout: 10 secs }; import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" federated reactor HelloWorldPhysicalAfter { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage() at localhost; - source.message ~> print.message after 10 msec; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage() at localhost; + source.message ~> print.message after 10 msec; +} diff --git a/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf b/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf index 70161f69..52e934e5 100644 --- a/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf +++ b/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf @@ -8,15 +8,15 @@ * @author Edward A. Lee */ target C { - timeout: 10 secs, - docker: true + timeout: 10 secs, + docker: true }; import MessageGenerator from "../HelloWorld.lf" import PrintMessage from "../HelloWorld.lf" federated reactor HelloWorldContainerized at rti { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; -} \ No newline at end of file + source = new MessageGenerator(prefix = "Hello World"); + print = new PrintMessage(); + source.message -> print.message; +} diff --git a/C/src/DistributedResourceManagement/FederatedResourceManagement.lf b/C/src/DistributedResourceManagement/FederatedResourceManagement.lf index 0fdf2c00..90716394 100644 --- a/C/src/DistributedResourceManagement/FederatedResourceManagement.lf +++ b/C/src/DistributedResourceManagement/FederatedResourceManagement.lf @@ -2,8 +2,8 @@ * Demo program illustrating the architecture of a distributed resource management * problem like that described in: * - * Lamport, L. (1984). "Using Time Instead of Timeout for Fault-Tolerant Distributed Systems." - * ACM Transactions on Programming Languages and Systems 6(2): 254-280. + * Lamport, L. (1984). "Using Time Instead of Timeout for Fault-Tolerant Distributed Systems." + * ACM Transactions on Programming Languages and Systems 6(2): 254-280. * * The goal is distributed first-come, first-served exclusive access to a shared * resource. @@ -32,29 +32,29 @@ * @author Edward A. Lee */ target C { - timeout: 1 sec + timeout: 1 sec } import Platform from "ResourceManagement.lf" federated reactor FederatedResourceManagement( - num_other_resource_managers:int(1) + num_other_resource_managers:int(1) ) { - // Each platform needs a unique id that is between 0 and QUEUE_SIZE-1. - a = new Platform( - idle_time = 50 msec, - use_time = 60 msec, - name = "San Francisco", - id = 0, - num_other_resource_managers = num_other_resource_managers); - b = new Platform( - idle_time = 200 msec, - use_time = 40 msec, - name = "Berkeley", - id = 1, - num_other_resource_managers = num_other_resource_managers); - b.request -> a.remote_request; - b.release -> a.remote_release; - a.request -> b.remote_request; - a.release -> b.remote_release; + // Each platform needs a unique id that is between 0 and QUEUE_SIZE-1. + a = new Platform( + idle_time = 50 msec, + use_time = 60 msec, + name = "San Francisco", + id = 0, + num_other_resource_managers = num_other_resource_managers); + b = new Platform( + idle_time = 200 msec, + use_time = 40 msec, + name = "Berkeley", + id = 1, + num_other_resource_managers = num_other_resource_managers); + b.request -> a.remote_request; + b.release -> a.remote_release; + a.request -> b.remote_request; + a.release -> b.remote_release; } diff --git a/C/src/DistributedResourceManagement/ResourceManagement.lf b/C/src/DistributedResourceManagement/ResourceManagement.lf index 01decc78..47a3335f 100644 --- a/C/src/DistributedResourceManagement/ResourceManagement.lf +++ b/C/src/DistributedResourceManagement/ResourceManagement.lf @@ -33,20 +33,20 @@ * @author Edward A. Lee */ target C { - timeout: 1 sec + timeout: 1 sec } preamble {= - // Queue is a circular buffer. The tail variable points to the - // position to write to. + // Queue is a circular buffer. The tail variable points to the + // position to write to. - typedef struct request_q_t { - int* queue; - int head; - int tail; - int size; - int capacity; - } request_q_t; + typedef struct request_q_t { + int* queue; + int head; + int tail; + int size; + int capacity; + } request_q_t; =} /** @@ -63,63 +63,63 @@ preamble {= * @output update Release the resource. */ reactor Client( - idle_time: time = 150 msec, - use_time: time = 30 msec, - id: int = 0, - name: char* = "unnamed Client" // Used for more visible logging + idle_time: time = 150 msec, + use_time: time = 30 msec, + id: int = 0, + name: char* = "unnamed Client" // Used for more visible logging ) { - logical action request_trigger(idle_time) - logical action release_trigger(use_time) + logical action request_trigger(idle_time) + logical action release_trigger(use_time) - input grant: bool + input grant: bool - output request: bool - output release: bool + output request: bool + output release: bool - state requests_outstanding: int = 0 + state requests_outstanding: int = 0 - reaction(startup, release_trigger) -> release, request_trigger {= - if (release_trigger->is_present) { - tag_t tag = lf_tag(); - lf_print("%s (ID %d): At tag (%lld, %u), released access.", - self->name, - self->id, - tag.time - lf_time_start(), tag.microstep - ); - lf_set(release, true); - } - lf_schedule(request_trigger, 0); - =} + reaction(startup, release_trigger) -> release, request_trigger {= + if (release_trigger->is_present) { + tag_t tag = lf_tag(); + lf_print("%s (ID %d): At tag (%lld, %u), released access.", + self->name, + self->id, + tag.time - lf_time_start(), tag.microstep + ); + lf_set(release, true); + } + lf_schedule(request_trigger, 0); + =} - reaction(request_trigger) -> request {= - tag_t tag = lf_tag(); - lf_print("%s (ID %d): At tag (%lld, %u), requesting access.", - self->name, - self->id, - tag.time - lf_time_start(), tag.microstep - ); - self->requests_outstanding++; - lf_set(request, true); - =} + reaction(request_trigger) -> request {= + tag_t tag = lf_tag(); + lf_print("%s (ID %d): At tag (%lld, %u), requesting access.", + self->name, + self->id, + tag.time - lf_time_start(), tag.microstep + ); + self->requests_outstanding++; + lf_set(request, true); + =} - reaction(grant) -> release_trigger {= - tag_t tag = lf_tag(); - lf_print("%s (ID %d): At tag (%lld, %u), granted access.", - self->name, - self->id, - tag.time - lf_time_start(), tag.microstep - ); - self->requests_outstanding--; - lf_schedule(release_trigger, 0); - =} + reaction(grant) -> release_trigger {= + tag_t tag = lf_tag(); + lf_print("%s (ID %d): At tag (%lld, %u), granted access.", + self->name, + self->id, + tag.time - lf_time_start(), tag.microstep + ); + self->requests_outstanding--; + lf_schedule(release_trigger, 0); + =} - reaction(shutdown) {= - lf_print("%s (ID %d): Number of requests that were never granted: %d", - self->name, - self->id, - self->requests_outstanding - ); - =} + reaction(shutdown) {= + lf_print("%s (ID %d): Number of requests that were never granted: %d", + self->name, + self->id, + self->requests_outstanding + ); + =} } /** @@ -151,222 +151,222 @@ reactor Client( * @output release Broadcast release to other resource managers. */ reactor ResourceManager( - name: char* = "unnamed ResourceManager", - id: int = 0, - num_other_resource_managers: int = 1 + name: char* = "unnamed ResourceManager", + id: int = 0, + num_other_resource_managers: int = 1 ) { - input local_request: bool - input local_release: bool - input[num_other_resource_managers] remote_request: int - input[num_other_resource_managers] remote_release: int + input local_request: bool + input local_release: bool + input[num_other_resource_managers] remote_request: int + input[num_other_resource_managers] remote_release: int - output request: int - output release: int - output local_grant: bool + output request: int + output release: int + output local_grant: bool - state queue: request_q_t + state queue: request_q_t - preamble {= - // Return the index of the next entry on the queue if there is one, - // and otherwise return -1. If the argument is negative, then this - // returns the first entry on the queue or -1 if the queue is empty. - // Otherwise, if the argument specifies the index of entry on the queue, - // return the index of the next entry or -1 if there is none. - // Otherwise, return -1. - // If the entry is non-negative by not on the queue at all, return -2. - int next_index(request_q_t* queue, int index) { - if (queue->size == 0) return -1; // Queue is empty. - if (index < 0) return queue->head; - index++; - if (index >= queue->capacity) { - index = 0; - } - if (index == queue->tail) return -1; // Reached the tail. - return index; - } + preamble {= + // Return the index of the next entry on the queue if there is one, + // and otherwise return -1. If the argument is negative, then this + // returns the first entry on the queue or -1 if the queue is empty. + // Otherwise, if the argument specifies the index of entry on the queue, + // return the index of the next entry or -1 if there is none. + // Otherwise, return -1. + // If the entry is non-negative by not on the queue at all, return -2. + int next_index(request_q_t* queue, int index) { + if (queue->size == 0) return -1; // Queue is empty. + if (index < 0) return queue->head; + index++; + if (index >= queue->capacity) { + index = 0; + } + if (index == queue->tail) return -1; // Reached the tail. + return index; + } - // Push a value to the end of the queue unless the queue is full. - // Return -1 if the queue is full and 0 otherwise. - int push(request_q_t* queue, int entry) { - int next_tail = queue->tail + 1; - if (next_tail >= queue->capacity) { - next_tail = 0; - } - if (queue->size >= queue->capacity) { - // Queue is full. - lf_print_warning("Queue is full. " - "Resource request from ID %d will be dropped.", - entry - ); - return -1; - } - queue->queue[queue->tail] = entry; - queue->tail = next_tail; - queue->size++; - return 0; - } + // Push a value to the end of the queue unless the queue is full. + // Return -1 if the queue is full and 0 otherwise. + int push(request_q_t* queue, int entry) { + int next_tail = queue->tail + 1; + if (next_tail >= queue->capacity) { + next_tail = 0; + } + if (queue->size >= queue->capacity) { + // Queue is full. + lf_print_warning("Queue is full. " + "Resource request from ID %d will be dropped.", + entry + ); + return -1; + } + queue->queue[queue->tail] = entry; + queue->tail = next_tail; + queue->size++; + return 0; + } - // Pop a value from the head of the queue unless the queue is empty. - // Return the value in the argument, or if the argument is NULL, - // discard the value. Return 0 for success and -1 - // for failure (the queue is empty). - int pop(request_q_t* queue, int* result) { - if (queue->size == 0) return -1; // Queue is empty. - if (result != NULL) { - *result = queue->queue[queue->head]; - } - queue->head++; - if (queue->head >= queue->capacity) { - queue->head = 0; - } - queue->size--; - return 0; - } + // Pop a value from the head of the queue unless the queue is empty. + // Return the value in the argument, or if the argument is NULL, + // discard the value. Return 0 for success and -1 + // for failure (the queue is empty). + int pop(request_q_t* queue, int* result) { + if (queue->size == 0) return -1; // Queue is empty. + if (result != NULL) { + *result = queue->queue[queue->head]; + } + queue->head++; + if (queue->head >= queue->capacity) { + queue->head = 0; + } + queue->size--; + return 0; + } - // Return 1 if the specified entry is on the specified queue - // and 0 otherwise. - int on_queue(request_q_t* queue, int entry) { - // Iterate over the queue to check. - int j = next_index(queue, -1); // Head of the queue or -1 if empty. - while (j >= 0) { - if (queue->queue[j] == entry) { - // The entry is on the queue. - return 1; - } - j = next_index(queue, j); - } - return 0; + // Return 1 if the specified entry is on the specified queue + // and 0 otherwise. + int on_queue(request_q_t* queue, int entry) { + // Iterate over the queue to check. + int j = next_index(queue, -1); // Head of the queue or -1 if empty. + while (j >= 0) { + if (queue->queue[j] == entry) { + // The entry is on the queue. + return 1; } - =} + j = next_index(queue, j); + } + return 0; + } + =} - reaction(startup) {= - self->queue.capacity = self->num_other_resource_managers + 1; - self->queue.queue = malloc(sizeof(int) * self->queue.capacity); - self->queue.head = 0; - self->queue.tail = 0; - self->queue.size = 0; - =} + reaction(startup) {= + self->queue.capacity = self->num_other_resource_managers + 1; + self->queue.queue = malloc(sizeof(int) * self->queue.capacity); + self->queue.head = 0; + self->queue.tail = 0; + self->queue.size = 0; + =} - reaction(local_request) -> request {= - // Just forward the request. The determination of whether - // the request can be granted cannot be made until we have received - // any remote requests with the same tag. - lf_set(request, self->id); - =} + reaction(local_request) -> request {= + // Just forward the request. The determination of whether + // the request can be granted cannot be made until we have received + // any remote requests with the same tag. + lf_set(request, self->id); + =} - reaction(local_release) -> release {= - if (self->queue.queue[self->queue.head] != self->id) { - lf_print_warning("%s (ID %d): attempting to release the resource but " - "does not hold it.", - self->name, - self->id - ); - } else { - pop(&self->queue, NULL); - lf_set(release, self->id); - } - =} + reaction(local_release) -> release {= + if (self->queue.queue[self->queue.head] != self->id) { + lf_print_warning("%s (ID %d): attempting to release the resource but " + "does not hold it.", + self->name, + self->id + ); + } else { + pop(&self->queue, NULL); + lf_set(release, self->id); + } + =} - reaction(remote_release) -> local_grant {= - for (int i = 0; i < remote_release_width; i++) { - if (remote_release[i]->is_present) { - // NOTE: There should not be more than one resource release - // at any logical tag. - if (remote_release[i]->value != self->queue.queue[self->queue.head]) { - lf_print_warning("%s: Received release from manager %d, but that " - "manager does not hold the resource. Ignoring the release.", - self->name, - remote_release[i]->value - ); - } else { - pop(&self->queue, NULL); - if (self->queue.head != self->queue.tail - && self->queue.queue[self->queue.head] == self->id - ) { - // Next request on the queue is me! - lf_set(local_grant, true); - } - // Since there shouldn't be more than one request, return now. - return; - } - } + reaction(remote_release) -> local_grant {= + for (int i = 0; i < remote_release_width; i++) { + if (remote_release[i]->is_present) { + // NOTE: There should not be more than one resource release + // at any logical tag. + if (remote_release[i]->value != self->queue.queue[self->queue.head]) { + lf_print_warning("%s: Received release from manager %d, but that " + "manager does not hold the resource. Ignoring the release.", + self->name, + remote_release[i]->value + ); + } else { + pop(&self->queue, NULL); + if (self->queue.head != self->queue.tail + && self->queue.queue[self->queue.head] == self->id + ) { + // Next request on the queue is me! + lf_set(local_grant, true); + } + // Since there shouldn't be more than one request, return now. + return; } - =} + } + } + =} - reaction(local_request, remote_request) -> local_grant {= - int local = -1; // -1 indicates there is no local request. - if (local_request->is_present) { - // If the local node is already on the request queue, ignore the request. - if (on_queue(&self->queue, self->id)) { - // (this should not happen). - lf_print_warning("%s: Redundant resource request. " - "My manager ID %d is already on the queue.", - self->name, - self->id - ); - } else { - local = self->id; - } - } - for (int i = 0; i < remote_request_width; i++) { - // NOTE: Resource managers must be connected to other - // resource managers all in the same order so that they - // agree on the order in which simultaneous requests will - // be handled. - if (remote_request[i]->is_present) { - int id = remote_request[i]->value; - // Check that the ID is not already on the queue. - if (on_queue(&self->queue, id)) { - // The ID is already on the queue - // (this should not happen). - lf_print_warning("%s (ID %d): received redundant resource request. " - "Manager %d is already on the queue. Ignoring request.", - self->name, - self->id, - id - ); - } else { - // If there is a local request and the local ID has higher priority, - // push the local ID first. - if (local >= 0 && local < id) { - if (push(&self->queue, local) != 0) { - lf_print_warning("%s: (ID %d) queue is full. Ignoring local request.", - self->name, - self->id - ); - } - // Indicate that the local request has been pushed. - local = -1; - } - // Now push the remote request. - if (push(&self->queue, id) != 0) { - lf_print_warning("%s (ID %d): Queue is full. Ignoring request.", - self->name, - self->id - ); - } - } - } - } - // If there is a local request and it has not yet gotten pushed to the queue, - // push it now. - if (local >= 0) { + reaction(local_request, remote_request) -> local_grant {= + int local = -1; // -1 indicates there is no local request. + if (local_request->is_present) { + // If the local node is already on the request queue, ignore the request. + if (on_queue(&self->queue, self->id)) { + // (this should not happen). + lf_print_warning("%s: Redundant resource request. " + "My manager ID %d is already on the queue.", + self->name, + self->id + ); + } else { + local = self->id; + } + } + for (int i = 0; i < remote_request_width; i++) { + // NOTE: Resource managers must be connected to other + // resource managers all in the same order so that they + // agree on the order in which simultaneous requests will + // be handled. + if (remote_request[i]->is_present) { + int id = remote_request[i]->value; + // Check that the ID is not already on the queue. + if (on_queue(&self->queue, id)) { + // The ID is already on the queue + // (this should not happen). + lf_print_warning("%s (ID %d): received redundant resource request. " + "Manager %d is already on the queue. Ignoring request.", + self->name, + self->id, + id + ); + } else { + // If there is a local request and the local ID has higher priority, + // push the local ID first. + if (local >= 0 && local < id) { if (push(&self->queue, local) != 0) { - lf_print_warning("%s: (ID %d) queue is full. Ignoring local request.", - self->name, - self->id - ); + lf_print_warning("%s: (ID %d) queue is full. Ignoring local request.", + self->name, + self->id + ); } + // Indicate that the local request has been pushed. + local = -1; + } + // Now push the remote request. + if (push(&self->queue, id) != 0) { + lf_print_warning("%s (ID %d): Queue is full. Ignoring request.", + self->name, + self->id + ); + } } - // Finally, if there was a local request and the head of the queue - // matches the local ID, grant the request. - if (local_request->is_present && self->queue.queue[self->queue.head] == self->id) { - // Grant request. - lf_set(local_grant, true); - } - =} + } + } + // If there is a local request and it has not yet gotten pushed to the queue, + // push it now. + if (local >= 0) { + if (push(&self->queue, local) != 0) { + lf_print_warning("%s: (ID %d) queue is full. Ignoring local request.", + self->name, + self->id + ); + } + } + // Finally, if there was a local request and the head of the queue + // matches the local ID, grant the request. + if (local_request->is_present && self->queue.queue[self->queue.head] == self->id) { + // Grant request. + lf_set(local_grant, true); + } + =} - reaction(shutdown) {= free(self->queue.queue); =} + reaction(shutdown) {= free(self->queue.queue); =} } /** @@ -391,58 +391,58 @@ reactor ResourceManager( * @output release An output to broadcast a local release of the resource. */ reactor Platform( - idle_time: time = 150 msec, - use_time: time = 30 msec, - num_other_resource_managers: int = 1, - id: int = 0, - name: char* = "unnamed Platform" // Used for more visible logging + idle_time: time = 150 msec, + use_time: time = 30 msec, + num_other_resource_managers: int = 1, + id: int = 0, + name: char* = "unnamed Platform" // Used for more visible logging ) { - input[num_other_resource_managers] remote_request: int - input[num_other_resource_managers] remote_release: int + input[num_other_resource_managers] remote_request: int + input[num_other_resource_managers] remote_release: int - output request: int - output release: int + output request: int + output release: int - client = new Client( - name = name, - id = id, - idle_time = idle_time, - use_time = use_time - ) + client = new Client( + name = name, + id = id, + idle_time = idle_time, + use_time = use_time + ) - manager = new ResourceManager( - name = name, - id = id, - num_other_resource_managers = num_other_resource_managers - ) + manager = new ResourceManager( + name = name, + id = id, + num_other_resource_managers = num_other_resource_managers + ) - client.request -> manager.local_request - client.release -> manager.local_release - manager.request -> request - manager.release -> release - manager.local_grant -> client.grant - remote_request -> manager.remote_request - remote_release -> manager.remote_release + client.request -> manager.local_request + client.release -> manager.local_release + manager.request -> request + manager.release -> release + manager.local_grant -> client.grant + remote_request -> manager.remote_request + remote_release -> manager.remote_release } main reactor ResourceManagement(num_other_resource_managers: int = 1) { - // Each platform needs a unique id that is between 0 and QUEUE_SIZE-1. - a = new Platform( - idle_time = 50 msec, - use_time = 60 msec, - name = "San Francisco", - id = 0, - num_other_resource_managers = num_other_resource_managers - ) - b = new Platform( - idle_time = 200 msec, - use_time = 40 msec, - name = "Berkeley", - id = 1, - num_other_resource_managers = num_other_resource_managers - ) - b.request -> a.remote_request - b.release -> a.remote_release - a.request -> b.remote_request - a.release -> b.remote_release + // Each platform needs a unique id that is between 0 and QUEUE_SIZE-1. + a = new Platform( + idle_time = 50 msec, + use_time = 60 msec, + name = "San Francisco", + id = 0, + num_other_resource_managers = num_other_resource_managers + ) + b = new Platform( + idle_time = 200 msec, + use_time = 40 msec, + name = "Berkeley", + id = 1, + num_other_resource_managers = num_other_resource_managers + ) + b.request -> a.remote_request + b.release -> a.remote_release + a.request -> b.remote_request + a.release -> b.remote_release } diff --git a/C/src/Parallelism/ForkJoin.lf b/C/src/Parallelism/ForkJoin.lf index ae9a918f..d99d9a82 100644 --- a/C/src/Parallelism/ForkJoin.lf +++ b/C/src/Parallelism/ForkJoin.lf @@ -8,52 +8,52 @@ * 225 msec. */ target C { - timeout: 2 sec, - workers: 1, // Change to 4 to see speed up. + timeout: 2 sec, + workers: 1, // Change to 4 to see speed up. }; main reactor(width:int(4)) { - a = new Source(); - t = new[width] TakeTime(); - (a.out)+ -> t.in; - b = new Destination(width = width); - t.out -> b.in; + a = new Source(); + t = new[width] TakeTime(); + (a.out)+ -> t.in; + b = new Destination(width = width); + t.out -> b.in; } reactor Source { - timer t(0, 200 msec); - output out:int; - state s:int(0); - reaction(t) -> out {= - lf_set(out, self->s); - self->s++; - =} + timer t(0, 200 msec); + output out:int; + state s:int(0); + reaction(t) -> out {= + lf_set(out, self->s); + self->s++; + =} } reactor TakeTime { - input in:int; - output out:int; - reaction(in) -> out {= - struct timespec sleep_time = {(time_t) 0, (long)200000000}; - struct timespec remaining_time; - nanosleep(&sleep_time, &remaining_time); - int offset = 0; - for (int i = 0; i < 100000000; i++) { - offset++; - } - lf_set(out, in->value + offset); - =} + input in:int; + output out:int; + reaction(in) -> out {= + struct timespec sleep_time = {(time_t) 0, (long)200000000}; + struct timespec remaining_time; + nanosleep(&sleep_time, &remaining_time); + int offset = 0; + for (int i = 0; i < 100000000; i++) { + offset++; + } + lf_set(out, in->value + offset); + =} } reactor Destination(width:int(4)) { - state s:int(400000000); - input[width] in:int; - reaction(in) {= - int sum = 0; - for (int i = 0; i < in_width; i++) { - sum += in[i]->value; + state s:int(400000000); + input[width] in:int; + reaction(in) {= + int sum = 0; + for (int i = 0; i < in_width; i++) { + sum += in[i]->value; + } + printf("Sum of received: %d.\n", sum); + if (sum != self->s) { + printf("ERROR: Expected %d.\n", self->s); + exit(1); } - printf("Sum of received: %d.\n", sum); - if (sum != self->s) { - printf("ERROR: Expected %d.\n", self->s); - exit(1); - } - self->s += in_width; - =} + self->s += in_width; + =} } diff --git a/C/src/Parallelism/Pipeline.lf b/C/src/Parallelism/Pipeline.lf index 9c665266..57334d1d 100644 --- a/C/src/Parallelism/Pipeline.lf +++ b/C/src/Parallelism/Pipeline.lf @@ -15,9 +15,9 @@ * @author Marten Lohstroh */ target C { - workers: 4, + workers: 4, } - + /** * Send counting sequence periodically. * @@ -27,18 +27,18 @@ * @param increment The increment between outputs */ reactor SendCount( - offset:time(0), - period:time(1 sec), - init:int(0), - increment:int(1) + offset:time(0), + period:time(1 sec), + init:int(0), + increment:int(1) ) { - state count:int(init); - output out:int; - timer t(offset, period); - reaction(t) -> out {= - lf_set(out, self->count); - self->count += self->increment; - =} + state count:int(init); + output out:int; + timer t(offset, period); + reaction(t) -> out {= + lf_set(out, self->count); + self->count += self->increment; + =} } /** @@ -47,16 +47,16 @@ reactor SendCount( * value has been received. */ reactor Receive { - input in:int; - reaction(in) {= - lf_print("At elapsed tag (%lld, %d), received %d.", - lf_time_logical_elapsed(), lf_tag().microstep, - in->value - ); - if (in->value >= 10) { - lf_request_stop(); - } - =} + input in:int; + reaction(in) {= + lf_print("At elapsed tag (%lld, %d), received %d.", + lf_time_logical_elapsed(), lf_tag().microstep, + in->value + ); + if (in->value >= 10) { + lf_request_stop(); + } + =} } /** @@ -71,23 +71,23 @@ reactor Receive { * @output out The triggering input. */ reactor TakeTime( - approximate_time:time(100 msec) + approximate_time:time(100 msec) ) { - input in:int; - output out:int; - reaction(in) -> out {= - instant_t start_time = lf_time_physical(); - while (lf_time_physical() < start_time + self->approximate_time) { - // Do nothing. - } - lf_set(out, in->value); - =} + input in:int; + output out:int; + reaction(in) -> out {= + instant_t start_time = lf_time_physical(); + while (lf_time_physical() < start_time + self->approximate_time) { + // Do nothing. + } + lf_set(out, in->value); + =} } main reactor { - r0 = new SendCount(period = 100 msec); - rp = new[4] TakeTime(approximate_time = 100 msec); - r5 = new Receive(); - // Uncomment the "after" clause to expose parallelism. - r0.out, rp.out -> rp.in, r5.in // after 100 msec; -} \ No newline at end of file + r0 = new SendCount(period = 100 msec); + rp = new[4] TakeTime(approximate_time = 100 msec); + r5 = new Receive(); + // Uncomment the "after" clause to expose parallelism. + r0.out, rp.out -> rp.in, r5.in // after 100 msec; +} diff --git a/C/src/ProtocolBuffers/HelloProtocolBuffers.lf b/C/src/ProtocolBuffers/HelloProtocolBuffers.lf index b84551a0..7ab2b424 100644 --- a/C/src/ProtocolBuffers/HelloProtocolBuffers.lf +++ b/C/src/ProtocolBuffers/HelloProtocolBuffers.lf @@ -8,12 +8,12 @@ * from https: * github.com/protocolbuffers/protobuf. For Mac, it is * available from homebrew via * ``` - * brew install protobuf + * brew install protobuf * ``` * You may also need to do: * ``` - * brew install protobuf-c - * brew install coreutils + * brew install protobuf-c + * brew install coreutils * ``` * Building protobuf from source is very slow, so avoid doing that * if possible. Next install the C plugin for protocol buffers from @@ -22,17 +22,17 @@ * Navigate to the directory containing the protocol buffer definition * hello_string.proto. To compile it, run the command: * ``` - * protoc-c --c_out=. hello_string.proto + * protoc-c --c_out=. hello_string.proto * ``` * This should generate the files "hello_string.pb-c.c" and "hello_string.pb-c.h". * Move both files to the src-gen directory where the C code for this * reactor is generated by Lingua Franca. Compile there with: * ``` - * cc HelloProtocolBuffers.c hello_string.pb-c.c -l protobuf-c + * cc HelloProtocolBuffers.c hello_string.pb-c.c -l protobuf-c * ``` * and run * ``` - * ./a.out + * ./a.out * ``` * * @author Matt Weber @@ -40,31 +40,31 @@ */ target C { - protobufs: hello_string.proto + protobufs: hello_string.proto }; main reactor HelloProtocolBuffers { - - timer t; - reaction(t) {= - HelloString pack_msg = HELLO_STRING__INIT; // Macro to create the protocol buffer - void *buf; // Buffer to store the serialized data - unsigned len; // Length of the packed message - - char* hello_msg = "Hello Protocol Buffers!"; - pack_msg.message = hello_msg; - - //Pack the message into buf. - len = hello_string__get_packed_size(&pack_msg); - buf = malloc(len); - hello_string__pack(&pack_msg,buf); - - //Now unpack the message from buf. - HelloString *unpacked_msg; - unpacked_msg = hello_string__unpack(NULL, len, buf); - - //Extract and print the unpacked message. - printf("Read: %s\n", unpacked_msg->message); - free(buf); // Free the allocated serialized buffer - =} + + timer t; + reaction(t) {= + HelloString pack_msg = HELLO_STRING__INIT; // Macro to create the protocol buffer + void *buf; // Buffer to store the serialized data + unsigned len; // Length of the packed message + + char* hello_msg = "Hello Protocol Buffers!"; + pack_msg.message = hello_msg; + + //Pack the message into buf. + len = hello_string__get_packed_size(&pack_msg); + buf = malloc(len); + hello_string__pack(&pack_msg,buf); + + //Now unpack the message from buf. + HelloString *unpacked_msg; + unpacked_msg = hello_string__unpack(NULL, len, buf); + + //Extract and print the unpacked message. + printf("Read: %s\n", unpacked_msg->message); + free(buf); // Free the allocated serialized buffer + =} } diff --git a/C/src/ROS/BasicROS.lf b/C/src/ROS/BasicROS.lf index be92f23c..3a6e406b 100644 --- a/C/src/ROS/BasicROS.lf +++ b/C/src/ROS/BasicROS.lf @@ -5,24 +5,24 @@ * and a MessageReceiver reactor that subscribes to 'topic'. * * 1- To get this example working, install full ROS 2 desktop - * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). + * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). * - * Please note that 'colcon' should also be installed. - * See 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. + * Please note that 'colcon' should also be installed. + * See 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. * * 2- Follow the instruction in - * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ - * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. + * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ + * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. * * 3- Follow section 2.2 and 2.3 to modify the CMakeLists.txt and package.xml. * * 4- Replace the default C++14 standard in CMakeLists.txt (i.e., set(CMAKE_CXX_STANDARD 14)) - * with: + * with: * - * # Default to C++20 - * if(NOT CMAKE_CXX_STANDARD) - * set(CMAKE_CXX_STANDARD 20) - * endif() + * # Default to C++20 + * if(NOT CMAKE_CXX_STANDARD) + * set(CMAKE_CXX_STANDARD 20) + * endif() * * 5- Add the following lines to CMakeLists.txt: * @@ -35,135 +35,135 @@ * target_link_libraries(talker platform_support) * * install(TARGETS - * platform_support - * DESTINATION lib - * ) + * platform_support + * DESTINATION lib + * ) * * 6- Use lfc (in bin/) to compile the provided .lf file - * - * lfc BasicROS.lf + * + * lfc BasicROS.lf * * 7- Run the provided build-ROS-node.sh: * - * ./build-ROS-node.sh BasicROS cpp_pubsub + * ./build-ROS-node.sh BasicROS cpp_pubsub * * This will create a 'talker' node in the package cpp_pubsub (these names can be changed in * CMakeLists.txt and in the argument to build-ROS-node.sh). * * 7- Source the appropriate setup.bash and run the node: * - * source cpp_pubsub/install/setup.bash - * ros2 run cpp_pubsub talker + * source cpp_pubsub/install/setup.bash + * ros2 run cpp_pubsub talker * */ target C { - keepalive: true, - logging: DEBUG, - no-compile: true + keepalive: true, + logging: DEBUG, + no-compile: true }; preamble {= - #include - #include - #include - #include + #include + #include + #include + #include - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/string.hpp" + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/string.hpp" =} reactor MessageGenerator { - preamble {= - class MinimalPublisher : public rclcpp::Node { - public: - MinimalPublisher() - : Node("minimal_publisher") - { - publisher_ = this->create_publisher("topic", 10); - } - - rclcpp::Publisher::SharedPtr publisher_; - }; - =} - state minimal_publisher:{=std::shared_ptr=}; - state i:int(0); - timer t(0, 500 msec); - reaction(startup) {= - std::cout << "Executing startup." << std::endl; - char *argv[] = {(char*)"BasicROSPub", NULL}; - rclcpp::init(1, argv); - self->minimal_publisher = std::make_shared(); - =} - reaction(t) {= - auto message = std_msgs::msg::String(); - std::cout << "Executing timer reaction." << std::endl; - message.data = "Hello, world! " + std::to_string(self->i++); - RCLCPP_INFO(self->minimal_publisher->get_logger(), - "Sender publishing: '%s'", message.data.c_str()); - self->minimal_publisher->publisher_->publish(message); - rclcpp::spin_some(self->minimal_publisher); - std::cout << "Done executing timer reaction." << std::endl; - =} - - reaction(shutdown) {= - std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} + preamble {= + class MinimalPublisher : public rclcpp::Node { + public: + MinimalPublisher() + : Node("minimal_publisher") + { + publisher_ = this->create_publisher("topic", 10); + } + + rclcpp::Publisher::SharedPtr publisher_; + }; + =} + state minimal_publisher:{=std::shared_ptr=}; + state i:int(0); + timer t(0, 500 msec); + reaction(startup) {= + std::cout << "Executing startup." << std::endl; + char *argv[] = {(char*)"BasicROSPub", NULL}; + rclcpp::init(1, argv); + self->minimal_publisher = std::make_shared(); + =} + reaction(t) {= + auto message = std_msgs::msg::String(); + std::cout << "Executing timer reaction." << std::endl; + message.data = "Hello, world! " + std::to_string(self->i++); + RCLCPP_INFO(self->minimal_publisher->get_logger(), + "Sender publishing: '%s'", message.data.c_str()); + self->minimal_publisher->publisher_->publish(message); + rclcpp::spin_some(self->minimal_publisher); + std::cout << "Done executing timer reaction." << std::endl; + =} + + reaction(shutdown) {= + std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} } reactor MessageReceiver { - preamble {= - class MinimalSubscriber : public rclcpp::Node { - public: - MinimalSubscriber(void* physical_action) - : Node("minimal_subscriber"), physical_action_(physical_action) { - subscription_ = this->create_subscription( - "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); - } + preamble {= + class MinimalSubscriber : public rclcpp::Node { + public: + MinimalSubscriber(void* physical_action) + : Node("minimal_subscriber"), physical_action_(physical_action) { + subscription_ = this->create_subscription( + "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); + } - private: - void topic_callback(const std_msgs::msg::String::SharedPtr msg) const { - char* writable_string = (char*)malloc(msg->data.length() + 1); - strcpy(writable_string, msg->data.c_str()); - // writable_string[msg->data.length()] = '\0'; // Terminate with 0 - RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str()); - std::cout << "At tag (" << lf_time_logical_elapsed() << "," - << lf_tag().microstep << ") calling schedule_value with value " - << writable_string << " and length " << msg->data.length() - << "." << std::endl; - lf_schedule_copy(physical_action_, 0, &(writable_string), msg->data.length() + 1); - // std::cout << "Done calling schedule_value." << std::endl; - } - rclcpp::Subscription::SharedPtr subscription_; - void* physical_action_; - }; - =} - physical action ros_message_a:string; - state minimal_subscriber:{=std::shared_ptr=}; - reaction(startup) -> ros_message_a {= - // std::cout << "Executing startup." << std::endl; - self->minimal_subscriber = std::make_shared(ros_message_a); - =} - - reaction(ros_message_a){= - std::cout << "Physical action triggered." << std::endl; - printf("Received: %s.\n", ros_message_a->value); - =} + private: + void topic_callback(const std_msgs::msg::String::SharedPtr msg) const { + char* writable_string = (char*)malloc(msg->data.length() + 1); + strcpy(writable_string, msg->data.c_str()); + // writable_string[msg->data.length()] = '\0'; // Terminate with 0 + RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str()); + std::cout << "At tag (" << lf_time_logical_elapsed() << "," + << lf_tag().microstep << ") calling schedule_value with value " + << writable_string << " and length " << msg->data.length() + << "." << std::endl; + lf_schedule_copy(physical_action_, 0, &(writable_string), msg->data.length() + 1); + // std::cout << "Done calling schedule_value." << std::endl; + } + rclcpp::Subscription::SharedPtr subscription_; + void* physical_action_; + }; + =} + physical action ros_message_a:string; + state minimal_subscriber:{=std::shared_ptr=}; + reaction(startup) -> ros_message_a {= + // std::cout << "Executing startup." << std::endl; + self->minimal_subscriber = std::make_shared(ros_message_a); + =} + + reaction(ros_message_a){= + std::cout << "Physical action triggered." << std::endl; + printf("Received: %s.\n", ros_message_a->value); + =} - - timer t(0, 500 msec); - reaction(t) {= - rclcpp::spin_some(self->minimal_subscriber); - // std::cout << "Timer triggered." << std::endl; - =} - - reaction(shutdown) {= - // std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} + + timer t(0, 500 msec); + reaction(t) {= + rclcpp::spin_some(self->minimal_subscriber); + // std::cout << "Timer triggered." << std::endl; + =} + + reaction(shutdown) {= + // std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} } main reactor { - sender = new MessageGenerator(); - receiver = new MessageReceiver(); -} \ No newline at end of file + sender = new MessageGenerator(); + receiver = new MessageReceiver(); +} diff --git a/C/src/ROS/PTIDES-ROS.lf b/C/src/ROS/PTIDES-ROS.lf index 7cb4bf7f..1ff6426d 100644 --- a/C/src/ROS/PTIDES-ROS.lf +++ b/C/src/ROS/PTIDES-ROS.lf @@ -7,24 +7,24 @@ * and a MessageReceiver reactor that subscribes to 'topic'. * * 1- To get this example working, install full ROS 2 desktop - * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). + * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). * - * Please note that 'colcon' should also be installed. - * See 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. + * Please note that 'colcon' should also be installed. + * See 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. * * 2- Follow the instruction in - * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ - * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. + * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ + * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. * * 3- Follow section 2.2 and 2.3 to modify the CMakeLists.txt and package.xml. * * 4- Replace the default C++14 standard in CMakeLists.txt (i.e., set(CMAKE_CXX_STANDARD 14)) - * with: + * with: * - * # Default to C++20 - * if(NOT CMAKE_CXX_STANDARD) - * set(CMAKE_CXX_STANDARD 20) - * endif() + * # Default to C++20 + * if(NOT CMAKE_CXX_STANDARD) + * set(CMAKE_CXX_STANDARD 20) + * endif() * * 5- Add the following lines to CMakeLists.txt: * @@ -37,139 +37,139 @@ * target_link_libraries(talker platform_support) * * install(TARGETS - * platform_support - * DESTINATION lib - * ) + * platform_support + * DESTINATION lib + * ) * * 6- Use lfc (in bin/) to compile the provided .lf file - * - * lfc PTIDES-ROS.lf + * + * lfc PTIDES-ROS.lf * * 7- Run the provided build-ROS-node.sh: * - * ./build-ROS-node.sh PTIDES-ROS cpp_pubsub + * ./build-ROS-node.sh PTIDES-ROS cpp_pubsub * * This will create a 'talker' node in the package cpp_pubsub (these names can be changed in * CMakeLists.txt and in the argument to build-ROS-node.sh). * * 7- Source the appropriate setup.bash and run the node: * - * source cpp_pubsub/install/setup.bash - * ros2 run cpp_pubsub talker + * source cpp_pubsub/install/setup.bash + * ros2 run cpp_pubsub talker * */ target C { - keepalive: true, - no-compile: true + keepalive: true, + no-compile: true }; preamble {= - #include - #include - #include - #include + #include + #include + #include + #include - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/int64.hpp" + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/int64.hpp" =} reactor MessageGenerator { - preamble {= - class MinimalPublisher : public rclcpp::Node { - public: - MinimalPublisher() - : Node("minimal_publisher") - { - publisher_ = this->create_publisher("topic", 10); - } - - rclcpp::Publisher::SharedPtr publisher_; - }; - =} - state minimal_publisher:{=std::shared_ptr=}; - state i:int(0); - timer t(0, 500 msec); - reaction(startup) {= - // std::cout << "Executing startup." << std::endl; - char *argv[] = {(char*)"BasicROSPub", NULL}; - rclcpp::init(1, argv); - self->minimal_publisher = std::make_shared(); - =} - reaction(t) {= - auto message = std_msgs::msg::Int64(); - // std::cout << "Executing timer reaction." << std::endl; - message.data = lf_time_logical() + MSEC(25); // Add a 25 msec delay - // RCLCPP_INFO(self->minimal_publisher->get_logger(), - // "Sender publishing: '%lld'", message.data); - self->minimal_publisher->publisher_->publish(message); - rclcpp::spin_some(self->minimal_publisher); - // std::cout << "Done executing timer reaction." << std::endl; - =} - - reaction(shutdown) {= - std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} + preamble {= + class MinimalPublisher : public rclcpp::Node { + public: + MinimalPublisher() + : Node("minimal_publisher") + { + publisher_ = this->create_publisher("topic", 10); + } + + rclcpp::Publisher::SharedPtr publisher_; + }; + =} + state minimal_publisher:{=std::shared_ptr=}; + state i:int(0); + timer t(0, 500 msec); + reaction(startup) {= + // std::cout << "Executing startup." << std::endl; + char *argv[] = {(char*)"BasicROSPub", NULL}; + rclcpp::init(1, argv); + self->minimal_publisher = std::make_shared(); + =} + reaction(t) {= + auto message = std_msgs::msg::Int64(); + // std::cout << "Executing timer reaction." << std::endl; + message.data = lf_time_logical() + MSEC(25); // Add a 25 msec delay + // RCLCPP_INFO(self->minimal_publisher->get_logger(), + // "Sender publishing: '%lld'", message.data); + self->minimal_publisher->publisher_->publish(message); + rclcpp::spin_some(self->minimal_publisher); + // std::cout << "Done executing timer reaction." << std::endl; + =} + + reaction(shutdown) {= + std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} } reactor MessageReceiver { - preamble {= - class MinimalSubscriber : public rclcpp::Node { - public: - MinimalSubscriber(void* physical_action) - : Node("minimal_subscriber"), physical_action_(physical_action) { - subscription_ = this->create_subscription( - "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); - } + preamble {= + class MinimalSubscriber : public rclcpp::Node { + public: + MinimalSubscriber(void* physical_action) + : Node("minimal_subscriber"), physical_action_(physical_action) { + subscription_ = this->create_subscription( + "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); + } - private: - void topic_callback(const std_msgs::msg::Int64::SharedPtr msg) const { - // writable_string[msg->data.length()] = '\0'; // Terminate with 0 - // RCLCPP_INFO(this->get_logger(), "I heard: '%lld'", msg->data); - // std::cout << "At tag (" << lf_time_logical_elapsed() << "," - // << lf_tag().microstep << ") calling schedule_copy with value " - // << msg->data << "." << std::endl; - lf_schedule_copy(physical_action_, 0, &(msg->data), sizeof(instant_t)); - // std::cout << "Done calling schedule_value." << std::endl; - } - rclcpp::Subscription::SharedPtr subscription_; - void* physical_action_; - }; - =} - physical action ros_message_a:instant_t; - logical action ros_message_l; - state minimal_subscriber:{=std::shared_ptr=}; - reaction(startup) -> ros_message_a {= - // std::cout << "Executing startup." << std::endl; - self->minimal_subscriber = std::make_shared(ros_message_a); - =} - - reaction(ros_message_a) -> ros_message_l {= - // std::cout << "Physical action triggered." << std::endl; - // printf("Received: %lld.\n", ros_message_a->value); - lf_schedule(ros_message_l, ros_message_a->value - lf_time_logical()); - =} - - reaction(ros_message_l) {= - printf("PTIDES reaction called at tag (%lld, %u).\n", - lf_time_logical_elapsed(), - lf_tag().microstep); - =} + private: + void topic_callback(const std_msgs::msg::Int64::SharedPtr msg) const { + // writable_string[msg->data.length()] = '\0'; // Terminate with 0 + // RCLCPP_INFO(this->get_logger(), "I heard: '%lld'", msg->data); + // std::cout << "At tag (" << lf_time_logical_elapsed() << "," + // << lf_tag().microstep << ") calling schedule_copy with value " + // << msg->data << "." << std::endl; + lf_schedule_copy(physical_action_, 0, &(msg->data), sizeof(instant_t)); + // std::cout << "Done calling schedule_value." << std::endl; + } + rclcpp::Subscription::SharedPtr subscription_; + void* physical_action_; + }; + =} + physical action ros_message_a:instant_t; + logical action ros_message_l; + state minimal_subscriber:{=std::shared_ptr=}; + reaction(startup) -> ros_message_a {= + // std::cout << "Executing startup." << std::endl; + self->minimal_subscriber = std::make_shared(ros_message_a); + =} + + reaction(ros_message_a) -> ros_message_l {= + // std::cout << "Physical action triggered." << std::endl; + // printf("Received: %lld.\n", ros_message_a->value); + lf_schedule(ros_message_l, ros_message_a->value - lf_time_logical()); + =} + + reaction(ros_message_l) {= + printf("PTIDES reaction called at tag (%lld, %u).\n", + lf_time_logical_elapsed(), + lf_tag().microstep); + =} - - timer t(0, 5 msec); - reaction(t) {= - rclcpp::spin_some(self->minimal_subscriber); - // std::cout << "Timer triggered." << std::endl; - =} - - reaction(shutdown) {= - // std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} + + timer t(0, 5 msec); + reaction(t) {= + rclcpp::spin_some(self->minimal_subscriber); + // std::cout << "Timer triggered." << std::endl; + =} + + reaction(shutdown) {= + // std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} } main reactor { - sender = new MessageGenerator(); - receiver = new MessageReceiver(); -} \ No newline at end of file + sender = new MessageGenerator(); + receiver = new MessageReceiver(); +} diff --git a/C/src/ReflexGame/ReflexGame.lf b/C/src/ReflexGame/ReflexGame.lf index a0c4873f..258cf510 100644 --- a/C/src/ReflexGame/ReflexGame.lf +++ b/C/src/ReflexGame/ReflexGame.lf @@ -12,11 +12,11 @@ * @author Marten Lohstroh */ target C { - keepalive: true + keepalive: true } preamble {= - #include "include/core/platform.h" + #include "include/core/platform.h" =} /** @@ -27,43 +27,43 @@ preamble {= * @param max_time The maximum time between outputs. */ reactor RandomSource(min_time: time = 2 sec, max_time: time = 8 sec) { - preamble {= - // Generate a random additional delay over the minimum. - // Assume millisecond precision is enough. - interval_t additional_time(interval_t min_time, interval_t max_time) { - int interval_in_msec = (max_time - min_time) / MSEC(1); - return (rand() % interval_in_msec) * MSEC(1); - } - =} - input another: int - output out: int - logical action prompt(min_time) - state count: int = 0 + preamble {= + // Generate a random additional delay over the minimum. + // Assume millisecond precision is enough. + interval_t additional_time(interval_t min_time, interval_t max_time) { + int interval_in_msec = (max_time - min_time) / MSEC(1); + return (rand() % interval_in_msec) * MSEC(1); + } + =} + input another: int + output out: int + logical action prompt(min_time) + state count: int = 0 - reaction(startup) -> prompt {= - printf("***********************************************\n"); - printf("Watch for the prompt, then hit Return or Enter.\n"); - printf("Type Control-D (EOF) to quit.\n\n"); + reaction(startup) -> prompt {= + printf("***********************************************\n"); + printf("Watch for the prompt, then hit Return or Enter.\n"); + printf("Type Control-D (EOF) to quit.\n\n"); - // Random number functions are part of stdlib.h, which is included by reactor.h. - // Set a seed for random number generation based on the current time. - srand(time(0)); + // Random number functions are part of stdlib.h, which is included by reactor.h. + // Set a seed for random number generation based on the current time. + srand(time(0)); - // Schedule the first event. - lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); - =} + // Schedule the first event. + lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); + =} - reaction(prompt) -> out {= - self->count++; - printf("%d. Hit Return or Enter!", self->count); - fflush(stdout); - lf_set(out, self->count); - =} + reaction(prompt) -> out {= + self->count++; + printf("%d. Hit Return or Enter!", self->count); + fflush(stdout); + lf_set(out, self->count); + =} - reaction(another) -> prompt {= - // Schedule the next event. - lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); - =} + reaction(another) -> prompt {= + // Schedule the next event. + lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); + =} } /** @@ -72,72 +72,72 @@ reactor RandomSource(min_time: time = 2 sec, max_time: time = 8 sec) { * records the time of this event and then report the response time. */ reactor GetUserInput { - preamble {= - // Thread to read input characters until an EOF is received. - // Each time a newline is received, schedule a user_response action. - void* read_input(void* user_response) { - int c; - while(1) { - while((c = getchar()) != '\n') { - if (c == EOF) break; - } - lf_schedule_copy(user_response, 0, &c, 1); - if (c == EOF) break; - } - return NULL; + preamble {= + // Thread to read input characters until an EOF is received. + // Each time a newline is received, schedule a user_response action. + void* read_input(void* user_response) { + int c; + while(1) { + while((c = getchar()) != '\n') { + if (c == EOF) break; } - =} + lf_schedule_copy(user_response, 0, &c, 1); + if (c == EOF) break; + } + return NULL; + } + =} - physical action user_response: char - state prompt_time: time = 0 - state total_time_in_ms: int = 0 - state count: int = 0 + physical action user_response: char + state prompt_time: time = 0 + state total_time_in_ms: int = 0 + state count: int = 0 - input prompt: int - output another: int + input prompt: int + output another: int - reaction(startup) -> user_response {= - // Start the thread that listens for Enter or Return. - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, user_response); - =} + reaction(startup) -> user_response {= + // Start the thread that listens for Enter or Return. + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, user_response); + =} - reaction(prompt) {= self->prompt_time = lf_time_logical(); =} + reaction(prompt) {= self->prompt_time = lf_time_logical(); =} - reaction(user_response) -> another {= - if (user_response->value == EOF) { - lf_request_stop(); - return; - } - // If the prompt_time is 0, then the user is cheating and - // hitting return before being prompted. - if (self->prompt_time == 0LL) { - printf("YOU CHEATED!\n"); - lf_request_stop(); - } else { - int time_in_ms = (lf_time_logical() - self->prompt_time) / 1000000LL; - printf("Response time in milliseconds: %d\n", time_in_ms); - self->count++; - self->total_time_in_ms += time_in_ms; - // Reset the prompt_time to indicate that there is no new prompt. - self->prompt_time = 0LL; - // Trigger another prompt. - lf_set(another, 42); - } - =} + reaction(user_response) -> another {= + if (user_response->value == EOF) { + lf_request_stop(); + return; + } + // If the prompt_time is 0, then the user is cheating and + // hitting return before being prompted. + if (self->prompt_time == 0LL) { + printf("YOU CHEATED!\n"); + lf_request_stop(); + } else { + int time_in_ms = (lf_time_logical() - self->prompt_time) / 1000000LL; + printf("Response time in milliseconds: %d\n", time_in_ms); + self->count++; + self->total_time_in_ms += time_in_ms; + // Reset the prompt_time to indicate that there is no new prompt. + self->prompt_time = 0LL; + // Trigger another prompt. + lf_set(another, 42); + } + =} - reaction(shutdown) {= - if (self->count > 0) { - printf("\n**** Average response time: %d.\n", self->total_time_in_ms/self->count); - } else { - printf("\n**** No attempts.\n"); - } - =} + reaction(shutdown) {= + if (self->count > 0) { + printf("\n**** Average response time: %d.\n", self->total_time_in_ms/self->count); + } else { + printf("\n**** No attempts.\n"); + } + =} } main reactor ReflexGame { - p = new RandomSource() - g = new GetUserInput() - p.out -> g.prompt - g.another -> p.another + p = new RandomSource() + g = new GetUserInput() + p.out -> g.prompt + g.another -> p.another } diff --git a/C/src/ReflexGame/ReflexGameTest.lf b/C/src/ReflexGame/ReflexGameTest.lf index 324df943..4f596b31 100644 --- a/C/src/ReflexGame/ReflexGameTest.lf +++ b/C/src/ReflexGame/ReflexGameTest.lf @@ -9,55 +9,55 @@ * Control-D or Control-Z), the program stops execution. */ target C { - keepalive: true, - timeout: 5 sec + keepalive: true, + timeout: 5 sec } preamble {= - #include "include/core/platform.h" + #include "include/core/platform.h" =} main reactor { - preamble {= - // Specify a thread that sends all keyboard characters - // as valued physical actions until an EOF (control-d or - // control-z) is received. - void* read_char(void* a) { - while(1) { - char* c = (char*)malloc(sizeof(char)); - *c = getchar(); - lf_schedule_value(a, 0, c, 1); - if (*c == EOF) break; - } - return NULL; - } - =} + preamble {= + // Specify a thread that sends all keyboard characters + // as valued physical actions until an EOF (control-d or + // control-z) is received. + void* read_char(void* a) { + while(1) { + char* c = (char*)malloc(sizeof(char)); + *c = getchar(); + lf_schedule_value(a, 0, c, 1); + if (*c == EOF) break; + } + return NULL; + } + =} - state thread_id: lf_thread_t = 0 - state request_time: time = 0 - // NOTE: The following will get the default 1 nsec minimum interarrival - // time. - physical action a: char* + state thread_id: lf_thread_t = 0 + state request_time: time = 0 + // NOTE: The following will get the default 1 nsec minimum interarrival + // time. + physical action a: char* - reaction(startup) -> a {= - // Start listening for key strokes. - // Note that these will not be received until Enter. - lf_thread_create(&self->thread_id, &read_char, a); - printf("Enter character(s) followed by return: "); - self->request_time = lf_time_logical(); - =} + reaction(startup) -> a {= + // Start listening for key strokes. + // Note that these will not be received until Enter. + lf_thread_create(&self->thread_id, &read_char, a); + printf("Enter character(s) followed by return: "); + self->request_time = lf_time_logical(); + =} - reaction(a) {= - if (*a->value == EOF) { - printf("\nEnd of file received.\n"); - lf_request_stop(); - } else if (*a->value == '\n') { - printf("Enter character(s) followed by return: "); - self->request_time = lf_time_logical(); - fflush(stdout); - } else { - interval_t elapsed = lf_time_logical() - self->request_time; - printf("Character entered: %c after %lld nsec.\n", *a->value, elapsed); - } - =} + reaction(a) {= + if (*a->value == EOF) { + printf("\nEnd of file received.\n"); + lf_request_stop(); + } else if (*a->value == '\n') { + printf("Enter character(s) followed by return: "); + self->request_time = lf_time_logical(); + fflush(stdout); + } else { + interval_t elapsed = lf_time_logical() - self->request_time; + printf("Character entered: %c after %lld nsec.\n", *a->value, elapsed); + } + =} } diff --git a/C/src/RockPaperScissors.lf b/C/src/RockPaperScissors.lf index 3cf58d99..afb91fd6 100644 --- a/C/src/RockPaperScissors.lf +++ b/C/src/RockPaperScissors.lf @@ -12,40 +12,40 @@ target C preamble {= typedef enum {paper=0, rock=1, scissors=2} symbol_t; =} main reactor RockPaperScissors { - player1 = new Player(id = 1) - player2 = new Player(id = 2) + player1 = new Player(id = 1) + player2 = new Player(id = 2) - player1.reveal -> player2.observe - player2.reveal -> player1.observe + player1.reveal -> player2.observe + player2.reveal -> player1.observe } reactor Player(id: char = 0) { - preamble {= const char* symbol_names[] = {"paper", "rock", "scissors"}; =} - - input observe: symbol_t - output reveal: symbol_t - logical action repeat(1 sec) - - state choice: symbol_t - - reaction(startup) {= - // Seed the random number generator. - // Use the ID to ensure each player has a different seed. - srand((unsigned) lf_time_logical() / self->id); - =} - - reaction(startup, repeat) -> reveal {= - self->choice = rand() % 3; - lf_set(reveal, self->choice); - printf("Player %d chose '%s'\n", self->id, symbol_names[self->choice]); - =} - - reaction(observe) -> repeat {= - if (observe->value == self->choice) { - printf("Player %d declares a tie.\n", self->id); - lf_schedule(repeat, 0); - } else if (observe->value == (self->choice + 1) % 3) { - printf("Player %d won!\n", self->id); - } - =} + preamble {= const char* symbol_names[] = {"paper", "rock", "scissors"}; =} + + input observe: symbol_t + output reveal: symbol_t + logical action repeat(1 sec) + + state choice: symbol_t + + reaction(startup) {= + // Seed the random number generator. + // Use the ID to ensure each player has a different seed. + srand((unsigned) lf_time_logical() / self->id); + =} + + reaction(startup, repeat) -> reveal {= + self->choice = rand() % 3; + lf_set(reveal, self->choice); + printf("Player %d chose '%s'\n", self->id, symbol_names[self->choice]); + =} + + reaction(observe) -> repeat {= + if (observe->value == self->choice) { + printf("Player %d declares a tie.\n", self->id); + lf_schedule(repeat, 0); + } else if (observe->value == (self->choice + 1) % 3) { + printf("Player %d won!\n", self->id); + } + =} } diff --git a/C/src/SleepingBarber.lf b/C/src/SleepingBarber.lf index ee390640..a3012928 100644 --- a/C/src/SleepingBarber.lf +++ b/C/src/SleepingBarber.lf @@ -32,21 +32,21 @@ */ target C { - fast: true, - threading: false, - cmake-include: "/lib/c/reactor-c/util/deque.cmake", - files: ["/lib/c/reactor-c/util/deque.h", "/lib/c/reactor-c/util/deque.c"] + fast: true, + threading: false, + cmake-include: "/lib/c/reactor-c/util/deque.cmake", + files: ["/lib/c/reactor-c/util/deque.h", "/lib/c/reactor-c/util/deque.c"] }; preamble {= - #include "deque.h" - /** - * Return a random time that is uniformly distributed between - * 0 and max. This assumes interval_t is a 64-bit integer and - * that max is less that (2^63)/RAND_MAX so that RAND_MAX * max - * does not overflow. - */ - interval_t random_time(interval_t max); + #include "deque.h" + /** + * Return a random time that is uniformly distributed between + * 0 and max. This assumes interval_t is a 64-bit integer and + * that max is less that (2^63)/RAND_MAX so that RAND_MAX * max + * does not overflow. + */ + interval_t random_time(interval_t max); =} /** @@ -66,72 +66,72 @@ preamble {= * halting the program when all customers have been served. */ reactor CustomerFactory( - num_customers:size_t(20), - max_time_between_customers:time(10 minutes) -) { - preamble {= - /* Note that this function is shared with Barber reactor. */ - interval_t random_time(interval_t max) { - interval_t result = (((interval_t)rand()) * RAND_MAX) % max; - return result; - } - =} - output send_customer: size_t; - input[num_customers] customer_done: bool; - input[num_customers] customer_returned: bool; + num_customers:size_t(20), + max_time_between_customers:time(10 minutes) +) { + preamble {= + /* Note that this function is shared with Barber reactor. */ + interval_t random_time(interval_t max) { + interval_t result = (((interval_t)rand()) * RAND_MAX) % max; + return result; + } + =} + output send_customer: size_t; + input[num_customers] customer_done: bool; + input[num_customers] customer_returned: bool; - logical action next; // Next new customer. - logical action again:int; // Returning customer. Payload is customer ID. - - state done_customers: size_t(0); // Count of finished customers. - state attempts: size_t(0); // Count of customer attempts. - state next_customer_id: size_t(0); // ID of next new customer. - - reaction(startup, next) -> send_customer, next {= - // send the new customer to the waiting room - self->attempts++; - lf_set(send_customer, self->next_customer_id++); - - if (self->next_customer_id < self->num_customers) { - // Schedule again. - interval_t delay = random_time(self->max_time_between_customers); - lf_schedule(next, delay); - } - =} - - reaction (again) -> send_customer {= - size_t customer_id = again->value; - self->attempts++; - lf_set(send_customer, customer_id); - =} - - reaction (customer_returned) -> again {= - for (int i = 0; i < self->num_customers; i++) { - if (customer_returned[i]->is_present) { - // The customer returned because the waiting room is full. - // Schedule again. - interval_t delay = random_time(self->max_time_between_customers); - lf_schedule_int(again, delay, i); - } - } - =} - - reaction (customer_done) {= - // Only one customer can be done at any logical tag, so we - // only need to count invocations of this reaction. - self->done_customers++; - if (self->done_customers >= self->num_customers) { - lf_request_stop(); - } - =} + logical action next; // Next new customer. + logical action again:int; // Returning customer. Payload is customer ID. + + state done_customers: size_t(0); // Count of finished customers. + state attempts: size_t(0); // Count of customer attempts. + state next_customer_id: size_t(0); // ID of next new customer. + + reaction(startup, next) -> send_customer, next {= + // send the new customer to the waiting room + self->attempts++; + lf_set(send_customer, self->next_customer_id++); - reaction (shutdown) {= - char buffer[LF_TIME_BUFFER_LENGTH]; - lf_readable_time(buffer, lf_time_logical_elapsed()); - lf_print("Finished: %zu customers got haircuts in %zu attempts over %s.", - self->done_customers, self->attempts, buffer - ); - =} + if (self->next_customer_id < self->num_customers) { + // Schedule again. + interval_t delay = random_time(self->max_time_between_customers); + lf_schedule(next, delay); + } + =} + + reaction (again) -> send_customer {= + size_t customer_id = again->value; + self->attempts++; + lf_set(send_customer, customer_id); + =} + + reaction (customer_returned) -> again {= + for (int i = 0; i < self->num_customers; i++) { + if (customer_returned[i]->is_present) { + // The customer returned because the waiting room is full. + // Schedule again. + interval_t delay = random_time(self->max_time_between_customers); + lf_schedule_int(again, delay, i); + } + } + =} + + reaction (customer_done) {= + // Only one customer can be done at any logical tag, so we + // only need to count invocations of this reaction. + self->done_customers++; + if (self->done_customers >= self->num_customers) { + lf_request_stop(); + } + =} + + reaction (shutdown) {= + char buffer[LF_TIME_BUFFER_LENGTH]; + lf_readable_time(buffer, lf_time_logical_elapsed()); + lf_print("Finished: %zu customers got haircuts in %zu attempts over %s.", + self->done_customers, self->attempts, buffer + ); + =} } /** @@ -145,48 +145,48 @@ reactor CustomerFactory( * to the customer ID. */ reactor WaitingRoom(capacity:size_t(1000), num_customers:size_t(2000)) { - preamble {= - #include "deque.h" - =} - input customer_enters: size_t; - - output[num_customers] full: bool; - output[num_customers] wait: bool; - - input barber_arrives: bool; - output barber_leaves_with_customer: int; - - state queue: deque_t; - state barber_asleep: bool(true); - - reaction (customer_enters) -> full, wait, barber_leaves_with_customer {= - size_t customer_id = customer_enters->value; - - if (deque_size(&self->queue) == self->capacity) { - lf_set(full[customer_id], true); - } else { - if (self->barber_asleep) { - self->barber_asleep = false; - lf_set(barber_leaves_with_customer, customer_id); - } else { - // Note that the customer_id is being cast to a pointer - // because the payload of a queue element is a pointer. - // As long as we never dereference that pointer, it is OK - // to recast it to size_t, assuming void* has at least as - // many bits as size_t, which it must. - deque_push_back(&self->queue, (void*)customer_id); - lf_set(wait[customer_id], true); - } - } - =} - - reaction (barber_arrives) -> barber_leaves_with_customer {= - if (deque_is_empty(&self->queue)) { - self->barber_asleep = true; - } else { - lf_set(barber_leaves_with_customer, (size_t)deque_pop_front(&self->queue)); - } - =} + preamble {= + #include "deque.h" + =} + input customer_enters: size_t; + + output[num_customers] full: bool; + output[num_customers] wait: bool; + + input barber_arrives: bool; + output barber_leaves_with_customer: int; + + state queue: deque_t; + state barber_asleep: bool(true); + + reaction (customer_enters) -> full, wait, barber_leaves_with_customer {= + size_t customer_id = customer_enters->value; + + if (deque_size(&self->queue) == self->capacity) { + lf_set(full[customer_id], true); + } else { + if (self->barber_asleep) { + self->barber_asleep = false; + lf_set(barber_leaves_with_customer, customer_id); + } else { + // Note that the customer_id is being cast to a pointer + // because the payload of a queue element is a pointer. + // As long as we never dereference that pointer, it is OK + // to recast it to size_t, assuming void* has at least as + // many bits as size_t, which it must. + deque_push_back(&self->queue, (void*)customer_id); + lf_set(wait[customer_id], true); + } + } + =} + + reaction (barber_arrives) -> barber_leaves_with_customer {= + if (deque_is_empty(&self->queue)) { + self->barber_asleep = true; + } else { + lf_set(barber_leaves_with_customer, (size_t)deque_pop_front(&self->queue)); + } + =} } /** @@ -196,47 +196,47 @@ reactor WaitingRoom(capacity:size_t(1000), num_customers:size_t(2000)) { * output and its `done_cutting` input to its `done` output. */ reactor Customer(bank_index:size_t(0)) { - input room_full: bool; - input wait: bool; - input start_cutting: bool; - input done_cutting: bool; + input room_full: bool; + input wait: bool; + input start_cutting: bool; + input done_cutting: bool; - output returned: bool; - output done: bool; - - reaction (room_full) -> returned {= - char buffer[LF_TIME_BUFFER_LENGTH]; - lf_readable_time(buffer, lf_time_logical_elapsed()); - lf_print("Customer %zu: Turned away at %s. Will try later.", - self->bank_index, buffer - ); - lf_set(returned, true); - =} - - reaction (wait) {= - char buffer[LF_TIME_BUFFER_LENGTH]; - lf_readable_time(buffer, lf_time_logical_elapsed()); - lf_print("Customer %zu: Entered waiting room at %s. Waiting.", - self->bank_index, buffer - ); - =} - - reaction (start_cutting) {= - char buffer[LF_TIME_BUFFER_LENGTH]; - lf_readable_time(buffer, lf_time_logical_elapsed()); - lf_print("Customer %zu: Started a haircut at %s.", - self->bank_index, buffer - ); - =} + output returned: bool; + output done: bool; + + reaction (room_full) -> returned {= + char buffer[LF_TIME_BUFFER_LENGTH]; + lf_readable_time(buffer, lf_time_logical_elapsed()); + lf_print("Customer %zu: Turned away at %s. Will try later.", + self->bank_index, buffer + ); + lf_set(returned, true); + =} + + reaction (wait) {= + char buffer[LF_TIME_BUFFER_LENGTH]; + lf_readable_time(buffer, lf_time_logical_elapsed()); + lf_print("Customer %zu: Entered waiting room at %s. Waiting.", + self->bank_index, buffer + ); + =} + + reaction (start_cutting) {= + char buffer[LF_TIME_BUFFER_LENGTH]; + lf_readable_time(buffer, lf_time_logical_elapsed()); + lf_print("Customer %zu: Started a haircut at %s.", + self->bank_index, buffer + ); + =} - reaction (done_cutting) -> done {= - char buffer[LF_TIME_BUFFER_LENGTH]; - lf_readable_time(buffer, lf_time_logical_elapsed()); - lf_print("Customer %zu: Finished a haircut at %s.", - self->bank_index, buffer - ); - lf_set(done, true); - =} + reaction (done_cutting) -> done {= + char buffer[LF_TIME_BUFFER_LENGTH]; + lf_readable_time(buffer, lf_time_logical_elapsed()); + lf_print("Customer %zu: Finished a haircut at %s.", + self->bank_index, buffer + ); + lf_set(done, true); + =} } /** @@ -251,70 +251,70 @@ reactor Customer(bank_index:size_t(0)) { * `min_cut_time` and `max_cut_time`. */ reactor Barber( - min_cut_time:time(5 minutes), - max_cut_time:time(15 minutes), - num_customers:size_t(20) + min_cut_time:time(5 minutes), + max_cut_time:time(15 minutes), + num_customers:size_t(20) ) { - input enter: int; - - output[num_customers] start_cutting: bool; - output[num_customers] done_cutting: bool; - output next: bool; + input enter: int; - logical action done: int; - - reaction (done) -> done_cutting, next {= - int customer_id = done->value; - lf_set(done_cutting[customer_id], true); - lf_set(next, true); - =} + output[num_customers] start_cutting: bool; + output[num_customers] done_cutting: bool; + output next: bool; + + logical action done: int; + + reaction (done) -> done_cutting, next {= + int customer_id = done->value; + lf_set(done_cutting[customer_id], true); + lf_set(next, true); + =} + + reaction (enter) -> start_cutting, done {= + int customer_id = enter->value; + lf_set(start_cutting[customer_id], true); - reaction (enter) -> start_cutting, done {= - int customer_id = enter->value; - lf_set(start_cutting[customer_id], true); - - // Calculate a random delay. - interval_t delay = self->min_cut_time - + random_time(self->max_cut_time - self->min_cut_time); - - // Notify the customer - lf_schedule_int(done, delay, customer_id); - =} + // Calculate a random delay. + interval_t delay = self->min_cut_time + + random_time(self->max_cut_time - self->min_cut_time); + + // Notify the customer + lf_schedule_int(done, delay, customer_id); + =} } main reactor ( - waiting_room_capacity:size_t(7), - max_time_between_customers:time(10 minutes), - min_cut_time:time(5 minutes), - max_cut_time:time(15 minutes), - num_customers:size_t(20) + waiting_room_capacity:size_t(7), + max_time_between_customers:time(10 minutes), + min_cut_time:time(5 minutes), + max_cut_time:time(15 minutes), + num_customers:size_t(20) ) { + + factory = new CustomerFactory( + num_customers=num_customers, + max_time_between_customers=max_time_between_customers + ); + room = new WaitingRoom( + capacity=waiting_room_capacity, + num_customers=num_customers + ); + + barber = new Barber( + min_cut_time=min_cut_time, + max_cut_time=max_cut_time, + num_customers=num_customers + ) + customers = new[num_customers] Customer(); - factory = new CustomerFactory( - num_customers=num_customers, - max_time_between_customers=max_time_between_customers - ); - room = new WaitingRoom( - capacity=waiting_room_capacity, - num_customers=num_customers - ); - - barber = new Barber( - min_cut_time=min_cut_time, - max_cut_time=max_cut_time, - num_customers=num_customers - ) - customers = new[num_customers] Customer(); - - factory.send_customer -> room.customer_enters; - room.full -> customers.room_full; - room.wait -> customers.wait; - room.barber_leaves_with_customer -> barber.enter; - barber.next -> room.barber_arrives; - barber.start_cutting -> customers.start_cutting; - barber.done_cutting -> customers.done_cutting; - customers.done -> factory.customer_done; - - customers.returned -> factory.customer_returned; + factory.send_customer -> room.customer_enters; + room.full -> customers.room_full; + room.wait -> customers.wait; + room.barber_leaves_with_customer -> barber.enter; + barber.next -> room.barber_arrives; + barber.start_cutting -> customers.start_cutting; + barber.done_cutting -> customers.done_cutting; + customers.done -> factory.customer_done; + + customers.returned -> factory.customer_returned; } diff --git a/C/src/Smokers.lf b/C/src/Smokers.lf index 3360153f..3f951e56 100644 --- a/C/src/Smokers.lf +++ b/C/src/Smokers.lf @@ -19,12 +19,12 @@ * A naive solution realizes each smoker as follows (in pseudo code, * shown for the smoker that holds tobacco): * ``` - * while(true) { - * acquire_paper(); - * acquire_matches(); - * smoke(); - * release(); - * } + * while(true) { + * acquire_paper(); + * acquire_matches(); + * smoke(); + * release(); + * } * ``` * The two "acquire" functions block until the specified resource is * available on the table and then acquire exclusive access to that resource. @@ -92,67 +92,67 @@ */ target C; main reactor { - a = new Agent(); - s1 = new Smoker(smoke_time = 1 sec, has = 0); // Has tobacco. - s2 = new Smoker(smoke_time = 2 sec, has = 1); // Has paper. - s3 = new Smoker(smoke_time = 3 sec, has = 2); // Has matches. - (a.tobacco)+ -> s1.tobacco, s2.tobacco, s3.tobacco; - (a.paper)+ -> s1.paper, s2.paper, s3.paper; - (a.matches)+ -> s1.matches, s2.matches, s3.matches; - s1.done, s2.done, s3.done -> a.trigger; + a = new Agent(); + s1 = new Smoker(smoke_time = 1 sec, has = 0); // Has tobacco. + s2 = new Smoker(smoke_time = 2 sec, has = 1); // Has paper. + s3 = new Smoker(smoke_time = 3 sec, has = 2); // Has matches. + (a.tobacco)+ -> s1.tobacco, s2.tobacco, s3.tobacco; + (a.paper)+ -> s1.paper, s2.paper, s3.paper; + (a.matches)+ -> s1.matches, s2.matches, s3.matches; + s1.done, s2.done, s3.done -> a.trigger; } reactor Agent { - input[3] trigger:bool; - output tobacco:bool; - output paper:bool; - output matches:bool; - reaction(startup) {= - // At start, seed the random number. - srand((unsigned)lf_time_logical()); - =} - reaction(startup, trigger) -> tobacco, paper, matches {= - int choice = rand() % 3; - if (choice == 0) { - lf_set(tobacco, true); - lf_set(paper, true); - lf_print("Agent putting tobacco and paper on the table."); - } else if (choice == 1) { - lf_set(tobacco, true); - lf_set(matches, true); - lf_print("Agent putting tobacco and matches on the table."); - } else { - lf_set(paper, true); - lf_set(matches, true); - lf_print("Agent putting paper and matches on the table."); - } - =} + input[3] trigger:bool; + output tobacco:bool; + output paper:bool; + output matches:bool; + reaction(startup) {= + // At start, seed the random number. + srand((unsigned)lf_time_logical()); + =} + reaction(startup, trigger) -> tobacco, paper, matches {= + int choice = rand() % 3; + if (choice == 0) { + lf_set(tobacco, true); + lf_set(paper, true); + lf_print("Agent putting tobacco and paper on the table."); + } else if (choice == 1) { + lf_set(tobacco, true); + lf_set(matches, true); + lf_print("Agent putting tobacco and matches on the table."); + } else { + lf_set(paper, true); + lf_set(matches, true); + lf_print("Agent putting paper and matches on the table."); + } + =} } reactor Smoker( - smoke_time:time(1 sec), - has:int(0) // 0 for tobacco, 1 for paper, 2 for matches + smoke_time:time(1 sec), + has:int(0) // 0 for tobacco, 1 for paper, 2 for matches ) { - input tobacco:bool; - input paper:bool; - input matches:bool; - - output done:bool; - - logical action smoke; - - reaction(smoke) -> done {= - lf_print("Smoker is done smoking."); - lf_set(done, true); - =} - reaction(tobacco, paper, matches) -> smoke {= - if (self->has == 0 && paper->is_present && matches->is_present) { - lf_print("Smoker with tobacco starts smoking."); - lf_schedule(smoke, self->smoke_time); - } else if (self->has == 1 && tobacco->is_present && matches->is_present) { - lf_print("Smoker with paper starts smoking."); - lf_schedule(smoke, self->smoke_time); - } else if (self->has == 2 && tobacco->is_present && paper->is_present) { - lf_print("Smoker with matches starts smoking."); - lf_schedule(smoke, self->smoke_time); - } - =} + input tobacco:bool; + input paper:bool; + input matches:bool; + + output done:bool; + + logical action smoke; + + reaction(smoke) -> done {= + lf_print("Smoker is done smoking."); + lf_set(done, true); + =} + reaction(tobacco, paper, matches) -> smoke {= + if (self->has == 0 && paper->is_present && matches->is_present) { + lf_print("Smoker with tobacco starts smoking."); + lf_schedule(smoke, self->smoke_time); + } else if (self->has == 1 && tobacco->is_present && matches->is_present) { + lf_print("Smoker with paper starts smoking."); + lf_schedule(smoke, self->smoke_time); + } else if (self->has == 2 && tobacco->is_present && paper->is_present) { + lf_print("Smoker with matches starts smoking."); + lf_schedule(smoke, self->smoke_time); + } + =} } diff --git a/C/src/TrainDoor/TrainDoor.lf b/C/src/TrainDoor/TrainDoor.lf index cacf203e..9da251bc 100644 --- a/C/src/TrainDoor/TrainDoor.lf +++ b/C/src/TrainDoor/TrainDoor.lf @@ -8,105 +8,105 @@ target C {keepalive: true}; preamble {= - #include - struct { - void* move; - void* open; - void* close; - } buttons; - - void* read_input(void* arg) { - printf("***************************************************************\n"); - printf("Press 'o' and hit return or enter to open the door\n"); - printf("Press 'c' and hit return or enter to close the door\n"); - printf("Press 'm' and hit return or enter perturb the motion sensor\n"); - printf("Press 'Control-d' to exit\n"); - while(1) { - int c = getchar(); - if (c == 'm') { - lf_schedule(buttons.move, 0); - } - if (c == 'o') { - lf_schedule(buttons.open, 0); - } - if (c == 'c') { - lf_schedule(buttons.close, 0); - } - if (c == EOF) { - break; - } - } - request_stop(); - return NULL; - } + #include + struct { + void* move; + void* open; + void* close; + } buttons; + + void* read_input(void* arg) { + printf("***************************************************************\n"); + printf("Press 'o' and hit return or enter to open the door\n"); + printf("Press 'c' and hit return or enter to close the door\n"); + printf("Press 'm' and hit return or enter perturb the motion sensor\n"); + printf("Press 'Control-d' to exit\n"); + while(1) { + int c = getchar(); + if (c == 'm') { + lf_schedule(buttons.move, 0); + } + if (c == 'o') { + lf_schedule(buttons.open, 0); + } + if (c == 'c') { + lf_schedule(buttons.close, 0); + } + if (c == EOF) { + break; + } + } + request_stop(); + return NULL; + } =} reactor MotionDetector { - physical action movement; - state timestamp:time(0); - input check:bool; - output ok:bool; - reaction(startup) -> movement {= - buttons.move = movement; - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, NULL); - =} - reaction(movement) {= - printf("Motion detected!\n"); - self->timestamp = lf_time_logical(); - =} - reaction(check) -> ok {= - if (self->timestamp == 0L || (lf_time_logical() - self->timestamp) > SECS(2)) { - lf_set(ok, true); - } else { - lf_set(ok, false); - } - =} + physical action movement; + state timestamp:time(0); + input check:bool; + output ok:bool; + reaction(startup) -> movement {= + buttons.move = movement; + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, NULL); + =} + reaction(movement) {= + printf("Motion detected!\n"); + self->timestamp = lf_time_logical(); + =} + reaction(check) -> ok {= + if (self->timestamp == 0L || (lf_time_logical() - self->timestamp) > SECS(2)) { + lf_set(ok, true); + } else { + lf_set(ok, false); + } + =} } reactor DoorController { - - physical action open; - physical action close; - - output check:bool; - input ok:bool; - state opened:bool(false); - state requested:bool(false); - reaction(startup) -> open, close {= - buttons.open = open; - buttons.close = close; - =} - - reaction(open) -> check {= - if (self->opened) { - printf("The door is already open\n"); - } else { - printf("Checking the motion sensor\n"); - lf_set(check, false); - self->requested = true; - } - =} - - reaction(close) {= - printf("Closing the door\n"); - self->opened = false; - =} - - reaction(ok) {= - if (self->requested && ok->value) { - self->opened = true; - printf("Opening the door.\n"); - } else { - printf("Cannot open the door; recent motion detected.\n"); - } - self->requested = false; - =} + + physical action open; + physical action close; + + output check:bool; + input ok:bool; + state opened:bool(false); + state requested:bool(false); + reaction(startup) -> open, close {= + buttons.open = open; + buttons.close = close; + =} + + reaction(open) -> check {= + if (self->opened) { + printf("The door is already open\n"); + } else { + printf("Checking the motion sensor\n"); + lf_set(check, false); + self->requested = true; + } + =} + + reaction(close) {= + printf("Closing the door\n"); + self->opened = false; + =} + + reaction(ok) {= + if (self->requested && ok->value) { + self->opened = true; + printf("Opening the door.\n"); + } else { + printf("Cannot open the door; recent motion detected.\n"); + } + self->requested = false; + =} } main reactor TrainDoor { - motion = new MotionDetector(); - door = new DoorController(); - door.check -> motion.check; - motion.ok -> door.ok; + motion = new MotionDetector(); + door = new DoorController(); + door.check -> motion.check; + motion.ok -> door.ok; } diff --git a/C/src/TrainDoor/TrainDoorAsymmetric.lf b/C/src/TrainDoor/TrainDoorAsymmetric.lf index cc28373f..1e97554f 100644 --- a/C/src/TrainDoor/TrainDoorAsymmetric.lf +++ b/C/src/TrainDoor/TrainDoorAsymmetric.lf @@ -7,36 +7,36 @@ reactor Controller { output move:bool; output stop:bool; physical action external:bool; reaction(startup) {= - // ... Set up external sensing. + // ... Set up external sensing. =} reaction(external)->lock, unlock, move, stop {= - if (external->value) { - lf_set(lock, true); lf_set(move, true); - } else { - lf_set(unlock, true); lf_set(stop, true); - } + if (external->value) { + lf_set(lock, true); lf_set(move, true); + } else { + lf_set(unlock, true); lf_set(stop, true); + } =} } reactor Train { input move:bool; input stop:bool; state moving:bool(false); reaction(move) {= - self->moving = true; + self->moving = true; =} reaction(stop) {= - self->moving = false; + self->moving = false; =} } reactor Door { input lock:bool; input unlock:bool; state locked:bool(false); reaction(lock) {= - // ... Actuate to lock door. - self->locked = true; + // ... Actuate to lock door. + self->locked = true; =} reaction(unlock) {= - // ... Actuate to unlock door. - self->locked = false; + // ... Actuate to unlock door. + self->locked = false; =} } main reactor { diff --git a/C/src/TrainDoor/TrainDoorSimplest.lf b/C/src/TrainDoor/TrainDoorSimplest.lf index a5de1be3..747635ad 100644 --- a/C/src/TrainDoor/TrainDoorSimplest.lf +++ b/C/src/TrainDoor/TrainDoorSimplest.lf @@ -7,27 +7,27 @@ reactor Controller { output move:bool; physical action external_move:bool; reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} reaction(external_move)->lock, move {= - lf_set(lock, external_move->value); - lf_set(move, external_move->value); + lf_set(lock, external_move->value); + lf_set(move, external_move->value); =} } reactor Train { input move:bool; state moving:bool(false); reaction(move) {= - // ... actuate to move or stop - self->moving = move->value; + // ... actuate to move or stop + self->moving = move->value; =} } reactor Door { input lock:bool; state locked:bool(false); reaction(lock) {= - // ... Actuate to lock or unlock door. - self->locked = lock->value; + // ... Actuate to lock or unlock door. + self->locked = lock->value; =} } main reactor { diff --git a/C/src/TrainDoor/TrainDoorWithDeadlines.lf b/C/src/TrainDoor/TrainDoorWithDeadlines.lf index 20a7645d..0afdabfa 100644 --- a/C/src/TrainDoor/TrainDoorWithDeadlines.lf +++ b/C/src/TrainDoor/TrainDoorWithDeadlines.lf @@ -9,11 +9,11 @@ reactor Controller { output stop:bool; physical action external_move(100 msec):bool; reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} reaction(external_move)->lock, move, unlock, stop {= - lf_set(lock, external_move->value); - lf_set(move, external_move->value); + lf_set(lock, external_move->value); + lf_set(move, external_move->value); =} } realtime reactor Train { @@ -21,10 +21,10 @@ realtime reactor Train { input stop:bool; state moving:bool(false); reaction(stop) {= - self->moving = false; + self->moving = false; =} deadline (50 msec) {= =} reaction(move) {= - self->moving = false; + self->moving = false; =} deadline(48 msec) {= =} } realtime reactor Door { @@ -34,25 +34,25 @@ realtime reactor Door { state locked:bool(false); state open:bool(false); reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} deadline(48 msec) {= =} reaction(lock) {= - if (lock && self->open) { - // ... Actuate to close door. - self->open = false; - } - self->locked = lock->value; + if (lock && self->open) { + // ... Actuate to close door. + self->open = false; + } + self->locked = lock->value; =} deadline(50 msec) {= - // ... handle the deadline violation... + // ... handle the deadline violation... =} reaction(unlock) {= - self->locked = true; + self->locked = true; =} reaction(external_open) {= - if (!self->locked) { - // ... Actuate to open door. - self->open = true; - } + if (!self->locked) { + // ... Actuate to open door. + self->open = true; + } =} } main reactor { diff --git a/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf b/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf index 1c52743c..429e10cb 100644 --- a/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf +++ b/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf @@ -7,22 +7,22 @@ reactor Controller { output move:bool; physical action external_move:bool; reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} reaction(external_move)->lock, move {= - lf_set(lock, external_move->value); - lf_set(move, external_move->value); + lf_set(lock, external_move->value); + lf_set(move, external_move->value); =} } reactor Train { input move:bool; state moving:bool(false); reaction(move) {= - if (move) { - self->moving = true; - } else { - self->moving = false; - } + if (move) { + self->moving = true; + } else { + self->moving = false; + } =} } reactor Door { @@ -31,20 +31,20 @@ reactor Door { state locked:bool(false); state open:bool(false); reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} reaction(lock) {= - if (lock && self->open) { - // ... Actuate to close door. - self->open = false; - } - self->locked = lock->value; + if (lock && self->open) { + // ... Actuate to close door. + self->open = false; + } + self->locked = lock->value; =} reaction(external_open) {= - if (!self->locked) { - // ... Actuate to open door. - self->open = true; - } + if (!self->locked) { + // ... Actuate to open door. + self->open = true; + } =} } main reactor { diff --git a/C/src/TrainDoor/TrainDoorWithOpen.lf b/C/src/TrainDoor/TrainDoorWithOpen.lf index 286659e6..f2f8b562 100644 --- a/C/src/TrainDoor/TrainDoorWithOpen.lf +++ b/C/src/TrainDoor/TrainDoorWithOpen.lf @@ -7,14 +7,14 @@ reactor Controller { output unlock:bool; output open:bool; physical action external:bool; reaction(startup) {= - // ... Set up external sensing. + // ... Set up external sensing. =} reaction(external)->close, lock, open, unlock {= - if (external->value) { - lf_set(close, true); lf_set(lock, true); - } else { - lf_set(open, true); lf_set(unlock, true); - } + if (external->value) { + lf_set(close, true); lf_set(lock, true); + } else { + lf_set(open, true); lf_set(unlock, true); + } =} } reactor Door { @@ -24,22 +24,22 @@ reactor Door { state locked:bool(false); state is_open:bool(false); reaction(close) {= - // ... Actuate to close door. - self->is_open = false; + // ... Actuate to close door. + self->is_open = false; =} reaction(lock) {= - // ... Actuate to lock door. - if(!self->is_open) - self->locked = true; + // ... Actuate to lock door. + if(!self->is_open) + self->locked = true; =} reaction(unlock) {= - // ... Actuate to unlock door. - self->locked = false; + // ... Actuate to unlock door. + self->locked = false; =} reaction(open, ext_open) {= - // ... Actuate to open door. - if(!self->locked) - self->is_open = true; + // ... Actuate to open door. + if(!self->locked) + self->is_open = true; =} } main reactor { diff --git a/C/src/browser-ui/BrowserUI.lf b/C/src/browser-ui/BrowserUI.lf index e7fd7013..657e9a6f 100644 --- a/C/src/browser-ui/BrowserUI.lf +++ b/C/src/browser-ui/BrowserUI.lf @@ -18,29 +18,29 @@ * @author Edward A. Lee */ target C { - keepalive: true + keepalive: true } preamble {= - #ifndef BROWSERUI_H - #define BROWSERUI_H - #include "util.h" // Defines lf_print() - #include "platform.h" // Defines lf_thread_t, etc. + #ifndef BROWSERUI_H + #define BROWSERUI_H + #include "util.h" // Defines lf_print() + #include "platform.h" // Defines lf_thread_t, etc. - #include - #include - #include // Defines read(), close() - #include // Defines strchr() - #include // Defines dirname() + #include + #include + #include // Defines read(), close() + #include // Defines strchr() + #include // Defines dirname() - typedef struct browser_ui_t { - bool running; // Indicator that listener thread is running. - int client_socket; // If non-negative, client socket awaiting response. - void* req_action; // The physical action triggered upon request. - uint16_t hostport; // The port to use for HTTP access. - char* initial_page; // Initial web page contents. - } browser_ui_t; - #endif // BROWSERUI_H + typedef struct browser_ui_t { + bool running; // Indicator that listener thread is running. + int client_socket; // If non-negative, client socket awaiting response. + void* req_action; // The physical action triggered upon request. + uint16_t hostport; // The port to use for HTTP access. + char* initial_page; // Initial web page contents. + } browser_ui_t; + #endif // BROWSERUI_H =} /** @@ -58,173 +58,173 @@ preamble {= * @param hostport The host port number, which defults to 8080. */ reactor ServerUI( - initial_file:string = "page.html", - hostport:uint16_t = 8080 + initial_file:string = "page.html", + hostport:uint16_t = 8080 ) { - output initialized:bool - output request:char* - input response:char* + output initialized:bool + output request:char* + input response:char* + + physical action req_action:char* + + state browser_ui:browser_ui_t - physical action req_action:char* + reaction(startup) -> req_action {= + // Read the default file to serve. + self->browser_ui.initial_page = read_file(self->initial_file); - state browser_ui:browser_ui_t - - reaction(startup) -> req_action {= - // Read the default file to serve. - self->browser_ui.initial_page = read_file(self->initial_file); - - self->browser_ui.running = true; - self->browser_ui.client_socket = -1; // No client socket awaiting response. - self->browser_ui.req_action = req_action; - self->browser_ui.hostport = self->hostport; - - lf_thread_t listener; - lf_thread_create(&listener, &listener_thread, &self->browser_ui); - =} + self->browser_ui.running = true; + self->browser_ui.client_socket = -1; // No client socket awaiting response. + self->browser_ui.req_action = req_action; + self->browser_ui.hostport = self->hostport; - reaction(req_action) -> request, initialized {= - if (strlen(req_action->value) == 0) { - lf_set(initialized, true); - } else { - lf_set_token(request, req_action->token); - } - =} - - reaction(response) {= - if (self->browser_ui.client_socket < 0) { - lf_print_error("No pending request at the server!"); - } - char *http_response = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n"; - write(self->browser_ui.client_socket, http_response, strlen(http_response)); - write(self->browser_ui.client_socket, response->value, strlen(response->value)); - close(self->browser_ui.client_socket); - self->browser_ui.client_socket = -1; - =} - - reaction(shutdown) {= - self->browser_ui.running = false; - free(self->browser_ui.initial_page); - =} + lf_thread_t listener; + lf_thread_create(&listener, &listener_thread, &self->browser_ui); + =} + + reaction(req_action) -> request, initialized {= + if (strlen(req_action->value) == 0) { + lf_set(initialized, true); + } else { + lf_set_token(request, req_action->token); + } + =} + + reaction(response) {= + if (self->browser_ui.client_socket < 0) { + lf_print_error("No pending request at the server!"); + } + char *http_response = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n"; + write(self->browser_ui.client_socket, http_response, strlen(http_response)); + write(self->browser_ui.client_socket, response->value, strlen(response->value)); + close(self->browser_ui.client_socket); + self->browser_ui.client_socket = -1; + =} + + reaction(shutdown) {= + self->browser_ui.running = false; + free(self->browser_ui.initial_page); + =} - /** - * Read a file at path relative to the source .lf file - * and return dynamically-allocated memory with its contents. - * The caller is responsible for freeing the pointer returned. - * @param path File path relative to the source .lf file. - */ - method read_file(path:{=const char*=}): char* {= - - // Construct the path to the file to be read - char* file_path = (char *) malloc(strlen(LF_SOURCE_DIRECTORY) + strlen(path) + 2); - if (file_path == NULL) lf_print_error_and_exit("Out of memory."); + /** + * Read a file at path relative to the source .lf file + * and return dynamically-allocated memory with its contents. + * The caller is responsible for freeing the pointer returned. + * @param path File path relative to the source .lf file. + */ + method read_file(path:{=const char*=}): char* {= + + // Construct the path to the file to be read + char* file_path = (char *) malloc(strlen(LF_SOURCE_DIRECTORY) + strlen(path) + 2); + if (file_path == NULL) lf_print_error_and_exit("Out of memory."); - sprintf(file_path, "%s%s%s", LF_SOURCE_DIRECTORY, LF_FILE_SEPARATOR, path); + sprintf(file_path, "%s%s%s", LF_SOURCE_DIRECTORY, LF_FILE_SEPARATOR, path); - // Open the file for reading - FILE* file = fopen(file_path, "rb"); - if (file == NULL) lf_print_error_and_exit("Error opening file at path %s.", file_path); + // Open the file for reading + FILE* file = fopen(file_path, "rb"); + if (file == NULL) lf_print_error_and_exit("Error opening file at path %s.", file_path); - // Determine the file size - fseek(file, 0, SEEK_END); - long file_size = ftell(file); - fseek(file, 0, SEEK_SET); + // Determine the file size + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + fseek(file, 0, SEEK_SET); - // Allocate memory for the buffer - char* buffer = (char *) malloc(file_size + 1); - if (buffer == NULL) lf_print_error_and_exit("Out of memory."); + // Allocate memory for the buffer + char* buffer = (char *) malloc(file_size + 1); + if (buffer == NULL) lf_print_error_and_exit("Out of memory."); - // Read the file into the buffer - fread(buffer, file_size, 1, file); - buffer[file_size] = '\0'; + // Read the file into the buffer + fread(buffer, file_size, 1, file); + buffer[file_size] = '\0'; - free(file_path); - fclose(file); - return buffer; - =} + free(file_path); + fclose(file); + return buffer; + =} - preamble {= - const char html_header[] = "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=UTF-8\r\n\r\n"; - - void* listener_thread(void* args) { - browser_ui_t* browser_ui = (browser_ui_t*)args; - - int server_socket = socket(AF_INET, SOCK_STREAM, 0); - if (server_socket < 0) { - lf_print_error_and_exit("Error creating socket."); - } + preamble {= + const char html_header[] = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n"; - int one = 1; - // Allow reusing of local addresses. - setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); + void* listener_thread(void* args) { + browser_ui_t* browser_ui = (browser_ui_t*)args; + + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket < 0) { + lf_print_error_and_exit("Error creating socket."); + } + + int one = 1; + // Allow reusing of local addresses. + setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); + + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = INADDR_ANY; + server_address.sin_port = htons(browser_ui->hostport); + + if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { + close(server_socket); + lf_print_error_and_exit("Error binding socket."); + } + // Pending queue length of 5. + listen(server_socket, 5); + + lf_print("****** Point your browser to http://localhost:%d", browser_ui->hostport); + + while(browser_ui->running) { + struct sockaddr_in client_address; + int client_address_length = sizeof(client_address); - struct sockaddr_in server_address; - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = INADDR_ANY; - server_address.sin_port = htons(browser_ui->hostport); + browser_ui->client_socket = accept(server_socket, (struct sockaddr *)&client_address, (socklen_t*)&client_address_length); - if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { - close(server_socket); - lf_print_error_and_exit("Error binding socket."); - } - // Pending queue length of 5. - listen(server_socket, 5); - - lf_print("****** Point your browser to http://localhost:%d", browser_ui->hostport); - - while(browser_ui->running) { - struct sockaddr_in client_address; - int client_address_length = sizeof(client_address); - - browser_ui->client_socket = accept(server_socket, (struct sockaddr *)&client_address, (socklen_t*)&client_address_length); - - if (browser_ui->client_socket < 0) { - lf_print_error_and_exit("Error accepting connection."); - } + if (browser_ui->client_socket < 0) { + lf_print_error_and_exit("Error accepting connection."); + } + + char buffer[1024] = {0}; + read(browser_ui->client_socket, buffer, 1024); - char buffer[1024] = {0}; - read(browser_ui->client_socket, buffer, 1024); - - // The response depends on the path part of the request. - const char *start_of_path = strchr(buffer, ' ') + 1; - if (start_of_path != NULL && strncmp("/ ", start_of_path, 2) != 0) { - const char *end_of_path = strchr(start_of_path, ' ') + 1; - size_t length = end_of_path - start_of_path; - char* path = (char*)malloc(length + 1); - strncpy(path, start_of_path, length); - path[length] = '\0'; - lf_schedule_value(browser_ui->req_action, 0, path, length + 1); - } else { - // Default is to write initial page. - write(browser_ui->client_socket, html_header, strlen(html_header)); - write( - browser_ui->client_socket, - browser_ui->initial_page, - strlen(browser_ui->initial_page) - ); - close(browser_ui->client_socket); - browser_ui->client_socket = -1; - lf_schedule_copy(browser_ui->req_action, 0, "", 1); - } - } - return NULL; + // The response depends on the path part of the request. + const char *start_of_path = strchr(buffer, ' ') + 1; + if (start_of_path != NULL && strncmp("/ ", start_of_path, 2) != 0) { + const char *end_of_path = strchr(start_of_path, ' ') + 1; + size_t length = end_of_path - start_of_path; + char* path = (char*)malloc(length + 1); + strncpy(path, start_of_path, length); + path[length] = '\0'; + lf_schedule_value(browser_ui->req_action, 0, path, length + 1); + } else { + // Default is to write initial page. + write(browser_ui->client_socket, html_header, strlen(html_header)); + write( + browser_ui->client_socket, + browser_ui->initial_page, + strlen(browser_ui->initial_page) + ); + close(browser_ui->client_socket); + browser_ui->client_socket = -1; + lf_schedule_copy(browser_ui->req_action, 0, "", 1); } - =} + } + return NULL; + } + =} } main reactor { - state count:int = 0 - s = new ServerUI() - - reaction(s.initialized) {= - self->count = 0; - =} - reaction(s.request) -> s.response {= - char* response; - asprintf(&response, "You have pushed %d times. Path of GET request: %s", - ++self->count, - s.request->value - ); - lf_set(s.response, response); - =} -} \ No newline at end of file + state count:int = 0 + s = new ServerUI() + + reaction(s.initialized) {= + self->count = 0; + =} + reaction(s.request) -> s.response {= + char* response; + asprintf(&response, "You have pushed %d times. Path of GET request: %s", + ++self->count, + s.request->value + ); + lf_set(s.response, response); + =} +} diff --git a/C/src/browser-ui/WebSocket.lf b/C/src/browser-ui/WebSocket.lf index 90a6e5a0..97a20641 100644 --- a/C/src/browser-ui/WebSocket.lf +++ b/C/src/browser-ui/WebSocket.lf @@ -19,74 +19,74 @@ * @author Edward A. Lee */ target C { - keepalive: true, - cmake-include: ["WebSocketCmake.txt"] + keepalive: true, + cmake-include: ["WebSocketCmake.txt"] } import WebSocketServer from "WebSocketServer.lf" preamble {= - #include "hashset/hashset.h" // For storing web socket instances that are connected. + #include "hashset/hashset.h" // For storing web socket instances that are connected. =} main reactor { - state count: int = 0 - logical action send_action: web_socket_instance_t* + state count: int = 0 + logical action send_action: web_socket_instance_t* - state connected_instances: hashset_t = {= NULL =} + state connected_instances: hashset_t = {= NULL =} - s = new WebSocketServer(max_clients = 2) // Limit number of clients to 2. + s = new WebSocketServer(max_clients = 2) // Limit number of clients to 2. - reaction(startup) -> s.send {= - lf_set_destructor(s.send, web_socket_message_destructor); - lf_set_copy_constructor(s.send, web_socket_message_copy_constructor); + reaction(startup) -> s.send {= + lf_set_destructor(s.send, web_socket_message_destructor); + lf_set_copy_constructor(s.send, web_socket_message_copy_constructor); - lf_print("======== Starting server. Open WebSocket.html in your favorite browser."); - self->connected_instances = hashset_create(2); // Default capacity for four instances. - =} + lf_print("======== Starting server. Open WebSocket.html in your favorite browser."); + self->connected_instances = hashset_create(2); // Default capacity for four instances. + =} - reaction(send_action) -> s.send, send_action {= - // If the web socket is no longer connected, the instance will not be in the hashset. - if (hashset_is_member(self->connected_instances, send_action->value->wsi)) { - char* message; - asprintf(&message, "Count is: %d", self->count++); + reaction(send_action) -> s.send, send_action {= + // If the web socket is no longer connected, the instance will not be in the hashset. + if (hashset_is_member(self->connected_instances, send_action->value->wsi)) { + char* message; + asprintf(&message, "Count is: %d", self->count++); - web_socket_message_t* container = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); - container->message = message; - container->length = strlen(message) + 1; - container->wsi = send_action->value->wsi; + web_socket_message_t* container = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); + container->message = message; + container->length = strlen(message) + 1; + container->wsi = send_action->value->wsi; - lf_set(s.send, container); - // Schedule the next send. - lf_schedule_token(send_action, SEC(1), send_action->token); - } - =} + lf_set(s.send, container); + // Schedule the next send. + lf_schedule_token(send_action, SEC(1), send_action->token); + } + =} - reaction(s.connected) -> send_action {= - if (s.connected->value.connected) { - lf_print("======== Connected a new client"); - } else { - lf_print("======== Disconnected client"); - } - if (s.connected->value.connected) { - // New connection. - hashset_add(self->connected_instances, s.connected->value.wsi); + reaction(s.connected) -> send_action {= + if (s.connected->value.connected) { + lf_print("======== Connected a new client"); + } else { + lf_print("======== Disconnected client"); + } + if (s.connected->value.connected) { + // New connection. + hashset_add(self->connected_instances, s.connected->value.wsi); - // Start sending. - lf_schedule_copy(send_action, 0, &s.connected->value, 1); - } else { - // Disconnecting. Remove from hashset to prevent further scheduling. - hashset_remove(self->connected_instances, s.connected->value.wsi); - } - =} + // Start sending. + lf_schedule_copy(send_action, 0, &s.connected->value, 1); + } else { + // Disconnecting. Remove from hashset to prevent further scheduling. + hashset_remove(self->connected_instances, s.connected->value.wsi); + } + =} - reaction(s.received) {= - lf_print("======== Application received: %s", s.received->value->message); - =} + reaction(s.received) {= + lf_print("======== Application received: %s", s.received->value->message); + =} - reaction(shutdown) {= - if (self->connected_instances != NULL) { - hashset_destroy(self->connected_instances); - } - =} + reaction(shutdown) {= + if (self->connected_instances != NULL) { + hashset_destroy(self->connected_instances); + } + =} } diff --git a/C/src/browser-ui/WebSocketServer.lf b/C/src/browser-ui/WebSocketServer.lf index 7bf6369d..ad9ff1ed 100644 --- a/C/src/browser-ui/WebSocketServer.lf +++ b/C/src/browser-ui/WebSocketServer.lf @@ -44,262 +44,262 @@ * @author Edward A. Lee */ target C { - keepalive: true, - cmake-include: ["WebSocketCmake.txt"] + keepalive: true, + cmake-include: ["WebSocketCmake.txt"] } preamble {= - #ifndef WEBSOCKET_H - #define WEBSOCKET_H - #include "util.h" // Defines lf_print() - #include "platform.h" // Defines lf_thread_t, etc. - - #include - - typedef struct server_status_t { - void* connected_action; // Action to notify of changes in connected status. - void* received_action; // Action to notify of messages received. - struct lws_context* context; // The context. - int max_clients; // Maximum number of clients. - int* client_count; // Pointer to the client_count state variable. - bool running; // Indicator that the listening thread is running. - } server_status_t; - - /** - * Identifier for a web socket instance together with its connected state. - * No need for a destructor or copy constructor here because the libwebsockets - * code handles deallocating the wsi when the WS is closed. - */ - typedef struct web_socket_instance_t { - struct lws* wsi; // Web socket instance. - bool connected; - } web_socket_instance_t; - - /** - * A web socket string message together with its web socket instance. - * This needs a destructor and copy constructor because the message - * is assumed to be in allocated memory. - */ - typedef struct web_socket_message_t { - struct lws* wsi; // Web socket instance. - size_t length; - char* message; - } web_socket_message_t; - - /** Destructor for an instance of web_socket_message_t. */ - void web_socket_message_destructor(void* message); - - /** Copy constructor for an instance of web_socket_message_t. */ - void* web_socket_message_copy_constructor(void* message); - - #endif // WEBSOCKET_H + #ifndef WEBSOCKET_H + #define WEBSOCKET_H + #include "util.h" // Defines lf_print() + #include "platform.h" // Defines lf_thread_t, etc. + + #include + + typedef struct server_status_t { + void* connected_action; // Action to notify of changes in connected status. + void* received_action; // Action to notify of messages received. + struct lws_context* context; // The context. + int max_clients; // Maximum number of clients. + int* client_count; // Pointer to the client_count state variable. + bool running; // Indicator that the listening thread is running. + } server_status_t; + + /** + * Identifier for a web socket instance together with its connected state. + * No need for a destructor or copy constructor here because the libwebsockets + * code handles deallocating the wsi when the WS is closed. + */ + typedef struct web_socket_instance_t { + struct lws* wsi; // Web socket instance. + bool connected; + } web_socket_instance_t; + + /** + * A web socket string message together with its web socket instance. + * This needs a destructor and copy constructor because the message + * is assumed to be in allocated memory. + */ + typedef struct web_socket_message_t { + struct lws* wsi; // Web socket instance. + size_t length; + char* message; + } web_socket_message_t; + + /** Destructor for an instance of web_socket_message_t. */ + void web_socket_message_destructor(void* message); + + /** Copy constructor for an instance of web_socket_message_t. */ + void* web_socket_message_copy_constructor(void* message); + + #endif // WEBSOCKET_H =} reactor WebSocketServer(hostport: int = 8000, max_clients: int = 0) { - output connected: web_socket_instance_t - output received: web_socket_message_t* - input send: web_socket_message_t* - - physical action connected_action: web_socket_instance_t - physical action received_action: web_socket_message_t* - - state status: server_status_t - state client_count: int = 0 - - preamble {= - // Thread handling incoming messages. - void* websocket_thread(void* args) { - server_status_t* status = (server_status_t*)args; - while(status->running) { - // According to the docs, the timeout argument is ignored. - lws_service(status->context, 50); - } - lws_context_destroy(status->context); - return NULL; - }; - - // Callback handling HTTP requests. - static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - bool result; - server_status_t* status = (server_status_t*)lws_context_user(lws_get_context(wsi)); - if (status == NULL) { - lf_print_error_and_exit("User struct NULL in callback!"); - } - LF_PRINT_LOG("HTTP callback invoked with reason: %d", reason); - web_socket_instance_t ws_instance; - switch(reason) { - case LWS_CALLBACK_WSI_CREATE: - LF_PRINT_LOG("**** Web socket connection requested."); - break; - case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: - LF_PRINT_LOG("**** Web socket new client."); - break; - case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE: - - // Check against maximum number of connections. - if (status->max_clients > 0 && *status->client_count >= status->max_clients) { - // Deny the connection. - lf_print_warning("**** Maximum number of clients reached. Denying connection."); - // Increment the client count past the maximum because it will be - // decremented when this closes and the browser will retry. - *status->client_count = *status->client_count + 1; - return 1; - } - - *status->client_count = *status->client_count + 1; - - LF_PRINT_LOG("**** Web socket connection established for client %d.", *status->client_count); - - ws_instance.wsi = wsi; - ws_instance.connected = true; - - // NOTE: Seem to need a delay in this action because otherwise - // the application may try to send a message to the socket before - // opening has completed. - lf_schedule_copy(status->connected_action, MSEC(500), &ws_instance, 1); - break; - case LWS_CALLBACK_WSI_DESTROY: - LF_PRINT_LOG("**** Connection closed."); - - *status->client_count = *status->client_count - 1; - - ws_instance.wsi = wsi; - ws_instance.connected = false; - - lf_schedule_copy(status->connected_action, 0, &ws_instance, 1); - break; - case LWS_CALLBACK_CLOSED: - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lf_print_error("*** connection error."); - - *status->client_count = *status->client_count - 1; - - ws_instance.wsi = wsi; - ws_instance.connected = false; - - lf_schedule_copy(status->connected_action, 0, &ws_instance, 1); - break; - default: - break; - } - return 0; - } - - // Callback handling web socket requests. - static int callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - LF_PRINT_LOG("WS callback invoked with reason: %d", reason); - server_status_t* status = (server_status_t*)lws_context_user(lws_get_context(wsi)); - switch(reason) { - case LWS_CALLBACK_RECEIVE: - if (len > 0) { - LF_PRINT_LOG("**** Server received WS message."); - - web_socket_message_t* received = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); - received->wsi = wsi; - - // Message is not NULL terminated, so created a NULL-terminated version. - char* string = (char*)malloc((len + 1) * sizeof(char)); - strncpy(string, (char*)in, len); - string[len] = 0; - received->length = len + 1; - received->message = string; - lf_schedule_value(status->received_action, 0, received, len + 1); - } - break; - - // Do we need to handle LWS_CALLBACK_CLOSED? - // Seems to be handled in the HTTP callback. - - default: - break; - } - return 0; - } - - void web_socket_message_destructor(void* message) { - free(((web_socket_message_t*)message)->message); - free(message); - } - - void* web_socket_message_copy_constructor(void* message) { - web_socket_message_t* cast = (web_socket_message_t*)message; - web_socket_message_t* result = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); - size_t length = cast->length; - char* copy = (char*)malloc(length * sizeof(char) + 1); - result->message = strncpy(copy, cast->message, length); - result->message[length] = 0; - result->wsi = cast->wsi; - return result; - } - =} - - reaction(startup) -> connected_action, received_action {= - // The receiving thread dynamically allocates memory for messages. - // Set the destructor and copy constructor. - lf_set_destructor(received_action, web_socket_message_destructor); - lf_set_copy_constructor(received_action, web_socket_message_copy_constructor); - - struct lws_context_creation_info info; - memset(&info, 0, sizeof(info)); - info.port = self->hostport; - info.iface = NULL; // Can put a string here? - info.protocols = (struct lws_protocols[]) { - { - "http", - callback_http, - 0 // No per-session data. - }, - { - "ws", - callback_ws, - 0 // No per-session data. - }, - { NULL, NULL, 0 } - }; - // To get callbacks to be passed a pointer to the status struct: - info.user = &self->status; - - self->status.context = lws_create_context(&info); - if (!self->status.context) { - lf_print_error_and_exit("Failed to create server for web sockets."); - } - - self->status.connected_action = connected_action; - self->status.received_action = received_action; - - self->status.max_clients = self->max_clients; - self->status.client_count = &self->client_count; - - lf_thread_t listener; - lf_thread_create(&listener, &websocket_thread, &self->status); - self->status.running = true; - =} - - reaction(received_action) -> received {= - lf_set_token(received, received_action->token); - =} - - reaction(send) {= - // NOTE: This send must be before the reaction to connected_action - // because the latter could cause a disconnection. - if(send->value->message == NULL) { - lf_print_error("NULL message received."); - } else { - int length = strlen(send->value->message); - // The buffer needs LWS_PRE bytes _before_ the message. - // Do not include the null terminator, because this make JSON unable to parse it. - char buffer[LWS_PRE + length]; - strcpy(buffer + LWS_PRE, send->value->message); - int result = lws_write(send->value->wsi, (unsigned char*)(buffer + LWS_PRE), length, LWS_WRITE_TEXT); - if (result < length) { - lf_print_warning("Send on web socket failed. Dropping message."); - } - } - =} - - reaction(connected_action) -> connected {= - lf_set(connected, connected_action->value); - =} - - reaction(shutdown) {= self->status.running = false; =} + output connected: web_socket_instance_t + output received: web_socket_message_t* + input send: web_socket_message_t* + + physical action connected_action: web_socket_instance_t + physical action received_action: web_socket_message_t* + + state status: server_status_t + state client_count: int = 0 + + preamble {= + // Thread handling incoming messages. + void* websocket_thread(void* args) { + server_status_t* status = (server_status_t*)args; + while(status->running) { + // According to the docs, the timeout argument is ignored. + lws_service(status->context, 50); + } + lws_context_destroy(status->context); + return NULL; + }; + + // Callback handling HTTP requests. + static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { + bool result; + server_status_t* status = (server_status_t*)lws_context_user(lws_get_context(wsi)); + if (status == NULL) { + lf_print_error_and_exit("User struct NULL in callback!"); + } + LF_PRINT_LOG("HTTP callback invoked with reason: %d", reason); + web_socket_instance_t ws_instance; + switch(reason) { + case LWS_CALLBACK_WSI_CREATE: + LF_PRINT_LOG("**** Web socket connection requested."); + break; + case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: + LF_PRINT_LOG("**** Web socket new client."); + break; + case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE: + + // Check against maximum number of connections. + if (status->max_clients > 0 && *status->client_count >= status->max_clients) { + // Deny the connection. + lf_print_warning("**** Maximum number of clients reached. Denying connection."); + // Increment the client count past the maximum because it will be + // decremented when this closes and the browser will retry. + *status->client_count = *status->client_count + 1; + return 1; + } + + *status->client_count = *status->client_count + 1; + + LF_PRINT_LOG("**** Web socket connection established for client %d.", *status->client_count); + + ws_instance.wsi = wsi; + ws_instance.connected = true; + + // NOTE: Seem to need a delay in this action because otherwise + // the application may try to send a message to the socket before + // opening has completed. + lf_schedule_copy(status->connected_action, MSEC(500), &ws_instance, 1); + break; + case LWS_CALLBACK_WSI_DESTROY: + LF_PRINT_LOG("**** Connection closed."); + + *status->client_count = *status->client_count - 1; + + ws_instance.wsi = wsi; + ws_instance.connected = false; + + lf_schedule_copy(status->connected_action, 0, &ws_instance, 1); + break; + case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lf_print_error("*** connection error."); + + *status->client_count = *status->client_count - 1; + + ws_instance.wsi = wsi; + ws_instance.connected = false; + + lf_schedule_copy(status->connected_action, 0, &ws_instance, 1); + break; + default: + break; + } + return 0; + } + + // Callback handling web socket requests. + static int callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { + LF_PRINT_LOG("WS callback invoked with reason: %d", reason); + server_status_t* status = (server_status_t*)lws_context_user(lws_get_context(wsi)); + switch(reason) { + case LWS_CALLBACK_RECEIVE: + if (len > 0) { + LF_PRINT_LOG("**** Server received WS message."); + + web_socket_message_t* received = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); + received->wsi = wsi; + + // Message is not NULL terminated, so created a NULL-terminated version. + char* string = (char*)malloc((len + 1) * sizeof(char)); + strncpy(string, (char*)in, len); + string[len] = 0; + received->length = len + 1; + received->message = string; + lf_schedule_value(status->received_action, 0, received, len + 1); + } + break; + + // Do we need to handle LWS_CALLBACK_CLOSED? + // Seems to be handled in the HTTP callback. + + default: + break; + } + return 0; + } + + void web_socket_message_destructor(void* message) { + free(((web_socket_message_t*)message)->message); + free(message); + } + + void* web_socket_message_copy_constructor(void* message) { + web_socket_message_t* cast = (web_socket_message_t*)message; + web_socket_message_t* result = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); + size_t length = cast->length; + char* copy = (char*)malloc(length * sizeof(char) + 1); + result->message = strncpy(copy, cast->message, length); + result->message[length] = 0; + result->wsi = cast->wsi; + return result; + } + =} + + reaction(startup) -> connected_action, received_action {= + // The receiving thread dynamically allocates memory for messages. + // Set the destructor and copy constructor. + lf_set_destructor(received_action, web_socket_message_destructor); + lf_set_copy_constructor(received_action, web_socket_message_copy_constructor); + + struct lws_context_creation_info info; + memset(&info, 0, sizeof(info)); + info.port = self->hostport; + info.iface = NULL; // Can put a string here? + info.protocols = (struct lws_protocols[]) { + { + "http", + callback_http, + 0 // No per-session data. + }, + { + "ws", + callback_ws, + 0 // No per-session data. + }, + { NULL, NULL, 0 } + }; + // To get callbacks to be passed a pointer to the status struct: + info.user = &self->status; + + self->status.context = lws_create_context(&info); + if (!self->status.context) { + lf_print_error_and_exit("Failed to create server for web sockets."); + } + + self->status.connected_action = connected_action; + self->status.received_action = received_action; + + self->status.max_clients = self->max_clients; + self->status.client_count = &self->client_count; + + lf_thread_t listener; + lf_thread_create(&listener, &websocket_thread, &self->status); + self->status.running = true; + =} + + reaction(received_action) -> received {= + lf_set_token(received, received_action->token); + =} + + reaction(send) {= + // NOTE: This send must be before the reaction to connected_action + // because the latter could cause a disconnection. + if(send->value->message == NULL) { + lf_print_error("NULL message received."); + } else { + int length = strlen(send->value->message); + // The buffer needs LWS_PRE bytes _before_ the message. + // Do not include the null terminator, because this make JSON unable to parse it. + char buffer[LWS_PRE + length]; + strcpy(buffer + LWS_PRE, send->value->message); + int result = lws_write(send->value->wsi, (unsigned char*)(buffer + LWS_PRE), length, LWS_WRITE_TEXT); + if (result < length) { + lf_print_warning("Send on web socket failed. Dropping message."); + } + } + =} + + reaction(connected_action) -> connected {= + lf_set(connected, connected_action->value); + =} + + reaction(shutdown) {= self->status.running = false; =} } diff --git a/C/src/car-brake/CarBrake.lf b/C/src/car-brake/CarBrake.lf index 3b2e739a..6da1d443 100644 --- a/C/src/car-brake/CarBrake.lf +++ b/C/src/car-brake/CarBrake.lf @@ -8,101 +8,101 @@ * @author Edward A. Lee */ target C { - timeout: 10s, - keepalive: true + timeout: 10s, + keepalive: true }; reactor Camera { - timer t(20ms, 33ms) - output frame: bool + timer t(20ms, 33ms) + output frame: bool - reaction (t) -> frame {= - lf_set(frame, true); // send a "frame" - =} + reaction (t) -> frame {= + lf_set(frame, true); // send a "frame" + =} } reactor BrakingAssistant { - input frame: bool; - output trigger_brake: bool; - - state counter: int(0); + input frame: bool; + output trigger_brake: bool; + + state counter: int(0); - reaction(frame) -> trigger_brake {= - // processing takes some time (10 ms) - lf_sleep(MSEC(10)); - - if (self->counter % 10 == 0) { - lf_print("[automatic] Send the brake signal at physical time " PRINTF_TIME, lf_time_physical_elapsed()); - lf_set(trigger_brake, true); - } - self->counter++; - =} + reaction(frame) -> trigger_brake {= + // processing takes some time (10 ms) + lf_sleep(MSEC(10)); + + if (self->counter % 10 == 0) { + lf_print("[automatic] Send the brake signal at physical time " PRINTF_TIME, lf_time_physical_elapsed()); + lf_set(trigger_brake, true); + } + self->counter++; + =} } reactor Braking { - preamble {= - // Function to simulate the pressing of the brake pendal every 1005ms. - bool stop_thread = false; - void* press_pedal(void* pedal) { - while(!stop_thread) { - lf_sleep(MSEC(1005)); - lf_print("[manual] Pressing the brake pedal at physical time " PRINTF_TIME, - lf_time_physical_elapsed() - ); - lf_schedule(pedal, 0); - } - return NULL; - } - =} - input brake_assistant: bool; + preamble {= + // Function to simulate the pressing of the brake pendal every 1005ms. + bool stop_thread = false; + void* press_pedal(void* pedal) { + while(!stop_thread) { + lf_sleep(MSEC(1005)); + lf_print("[manual] Pressing the brake pedal at physical time " PRINTF_TIME, + lf_time_physical_elapsed() + ); + lf_schedule(pedal, 0); + } + return NULL; + } + =} + input brake_assistant: bool; - physical action pedal; + physical action pedal; - state thread: lf_thread_t; + state thread: lf_thread_t; - reaction(startup) -> pedal {= - lf_thread_create(&self->thread, &press_pedal, pedal); - =} + reaction(startup) -> pedal {= + lf_thread_create(&self->thread, &press_pedal, pedal); + =} - reaction(shutdown) {= - stop_thread = true; - void* thread_join_status; - lf_thread_join(self->thread, &thread_join_status); - =} + reaction(shutdown) {= + stop_thread = true; + void* thread_join_status; + lf_thread_join(self->thread, &thread_join_status); + =} - reaction(pedal) {= - lf_print("[system] Brake manually triggered at physical time " PRINTF_TIME, - lf_time_physical_elapsed() - ); - =} deadline (3msecs) {= - lf_print("\033[1;31m[ERROR]\033[0m Deadline on manual braking violated at physical time " PRINTF_TIME, - lf_time_physical_elapsed() - ); - =} - - reaction(brake_assistant) {= - lf_print("[system] Brake automatically triggered at physical time " PRINTF_TIME, - lf_time_physical_elapsed() - ); - =} deadline (25msecs) {= - lf_print("\033[1;31m[error]\033[0m Deadline on automatic braking violated " - "at physical time " PRINTF_TIME, lf_time_physical_elapsed() - ); - =} + reaction(pedal) {= + lf_print("[system] Brake manually triggered at physical time " PRINTF_TIME, + lf_time_physical_elapsed() + ); + =} deadline (3msecs) {= + lf_print("\033[1;31m[ERROR]\033[0m Deadline on manual braking violated at physical time " PRINTF_TIME, + lf_time_physical_elapsed() + ); + =} + + reaction(brake_assistant) {= + lf_print("[system] Brake automatically triggered at physical time " PRINTF_TIME, + lf_time_physical_elapsed() + ); + =} deadline (25msecs) {= + lf_print("\033[1;31m[error]\033[0m Deadline on automatic braking violated " + "at physical time " PRINTF_TIME, lf_time_physical_elapsed() + ); + =} } reactor Vision { - output trigger_brake: bool; - camera = new Camera(); - assistant = new BrakingAssistant(); + output trigger_brake: bool; + camera = new Camera(); + assistant = new BrakingAssistant(); - camera.frame -> assistant.frame; - assistant.trigger_brake -> trigger_brake; + camera.frame -> assistant.frame; + assistant.trigger_brake -> trigger_brake; } federated reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking(); + vision = new Vision(); - vision.trigger_brake -> braking.brake_assistant; -} \ No newline at end of file + vision.trigger_brake -> braking.brake_assistant; +} diff --git a/C/src/car-brake/CarBrake2.lf b/C/src/car-brake/CarBrake2.lf index efa3e0e7..4afd73e2 100644 --- a/C/src/car-brake/CarBrake2.lf +++ b/C/src/car-brake/CarBrake2.lf @@ -6,15 +6,15 @@ * This version is far less likely to experience deadline violations. */ target C { - keepalive: true, - timeout: 10s + keepalive: true, + timeout: 10s }; import Braking, Vision from "CarBrake.lf" federated reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking(); + vision = new Vision(); - vision.trigger_brake ~> braking.brake_assistant; + vision.trigger_brake ~> braking.brake_assistant; } diff --git a/C/src/car-brake/CarBrake3.lf b/C/src/car-brake/CarBrake3.lf index 02a2641a..752b8efa 100644 --- a/C/src/car-brake/CarBrake3.lf +++ b/C/src/car-brake/CarBrake3.lf @@ -6,15 +6,15 @@ * This version is far less likely to experience deadline violations. */ target C { - keepalive: true, - timeout: 10s + keepalive: true, + timeout: 10s }; import Braking, Vision from "CarBrake.lf" federated reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking(); + vision = new Vision(); - vision.trigger_brake -> braking.brake_assistant after 20ms; + vision.trigger_brake -> braking.brake_assistant after 20ms; } diff --git a/C/src/deadlines/AnytimePrime.lf b/C/src/deadlines/AnytimePrime.lf index 9c622203..095155b8 100644 --- a/C/src/deadlines/AnytimePrime.lf +++ b/C/src/deadlines/AnytimePrime.lf @@ -16,57 +16,57 @@ * @author Marten Lohstroh (marten@berkeley.edu) */ target C { - fast: true, - files: [ - "/lib/c/reactor-c/include/core/utils/vector.h", - "/lib/c/reactor-c/core/utils/vector.c" - ], - cmake-include: ["/lib/c/reactor-c/core/utils/vector.cmake"] + fast: true, + files: [ + "/lib/c/reactor-c/include/core/utils/vector.h", + "/lib/c/reactor-c/core/utils/vector.c" + ], + cmake-include: ["/lib/c/reactor-c/core/utils/vector.cmake"] } preamble {= - #include "vector.h" + #include "vector.h" =} reactor Prime { - output out: {= long long =} + output out: {= long long =} - reaction(startup) -> out {= - int num_primes = 1; - long long current_num = 2; - vector_t primes = vector_new(10000); - vector_push(&primes, (void*)2); + reaction(startup) -> out {= + int num_primes = 1; + long long current_num = 2; + vector_t primes = vector_new(10000); + vector_push(&primes, (void*)2); - while (!lf_check_deadline(self, true)) { - current_num++; - int i = 0; - for (i = 0; i < num_primes; i++) { - if (current_num % (long long)primes.start[i] == 0) { - break; - } - } - if (i == num_primes) { - // Add the prime to vector. - vector_push(&primes, (void*)current_num); - num_primes++; - } + while (!lf_check_deadline(self, true)) { + current_num++; + int i = 0; + for (i = 0; i < num_primes; i++) { + if (current_num % (long long)primes.start[i] == 0) { + break; } + } + if (i == num_primes) { + // Add the prime to vector. + vector_push(&primes, (void*)current_num); + num_primes++; + } + } - // Output the largest prime found. - lf_set(out, (long long)primes.start[num_primes - 1]); - =} deadline(3 sec) {= lf_print("Deadline handler called!"); =} + // Output the largest prime found. + lf_set(out, (long long)primes.start[num_primes - 1]); + =} deadline(3 sec) {= lf_print("Deadline handler called!"); =} } reactor Print { - input in: {= long long =} + input in: {= long long =} - reaction(in) {= - printf("Largest prime found within the deadline: %lld\n", in->value); - =} + reaction(in) {= + printf("Largest prime found within the deadline: %lld\n", in->value); + =} } main reactor AnytimePrime { - p = new Prime() - d = new Print() - p.out -> d.in + p = new Prime() + d = new Print() + p.out -> d.in } diff --git a/C/src/deadlines/Deadline.lf b/C/src/deadlines/Deadline.lf index 5efae3b2..4058e70a 100644 --- a/C/src/deadlines/Deadline.lf +++ b/C/src/deadlines/Deadline.lf @@ -9,87 +9,87 @@ * @author Edward A. Lee */ target C { - keepalive: true + keepalive: true } preamble {= - #include + #include =} reactor Sensor { - preamble {= - void* read_input(void* response) { - int c; - while(1) { - while((c = getchar()) != '\n') { - if (c == EOF) { - lf_request_stop(); - break; - } - } - lf_schedule(response, 0); - if (c == EOF) { - break; - } - } - return NULL; + preamble {= + void* read_input(void* response) { + int c; + while(1) { + while((c = getchar()) != '\n') { + if (c == EOF) { + lf_request_stop(); + break; + } } - =} + lf_schedule(response, 0); + if (c == EOF) { + break; + } + } + return NULL; + } + =} - physical action response - output y: bool + physical action response + output y: bool - reaction(startup) -> response {= - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, response); - printf("Press Enter to produce a sensor value.\n"); - =} + reaction(startup) -> response {= + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, response); + printf("Press Enter to produce a sensor value.\n"); + =} - reaction(response) -> y {= - printf("Reacting to physical action at %lld\n", lf_time_logical_elapsed()); - lf_set(y, true); - =} + reaction(response) -> y {= + printf("Reacting to physical action at %lld\n", lf_time_logical_elapsed()); + lf_set(y, true); + =} } reactor Analysis { - input x: bool - output y: bool - state do_work: bool = false + input x: bool + output y: bool + state do_work: bool = false - reaction(x) -> y {= - if (self->do_work) { - printf("Working for 500 ms...\n"); - usleep(500); - } else { - printf("Skipping work!\n"); - } - self->do_work = !self->do_work; - lf_set(y, true); - =} + reaction(x) -> y {= + if (self->do_work) { + printf("Working for 500 ms...\n"); + usleep(500); + } else { + printf("Skipping work!\n"); + } + self->do_work = !self->do_work; + lf_set(y, true); + =} } reactor Actuator { - input x: bool + input x: bool - reaction(x) {= - instant_t l = lf_time_logical_elapsed(); - instant_t p = lf_time_physical_elapsed(); - lf_print("Actuating... Logical time: " PRINTF_TIME - ". Physical time: " PRINTF_TIME ". Lag: " PRINTF_TIME, - l, p, p-l); - =} deadline(500 usecs) {= - instant_t d = lf_time_physical_elapsed() - - lf_time_logical_elapsed(); - lf_print("***** Deadline missed! Lag: " PRINTF_TIME - " (too late by " PRINTF_TIME " nsecs)", - d, d-500000); - =} + reaction(x) {= + instant_t l = lf_time_logical_elapsed(); + instant_t p = lf_time_physical_elapsed(); + lf_print("Actuating... Logical time: " PRINTF_TIME + ". Physical time: " PRINTF_TIME ". Lag: " PRINTF_TIME, + l, p, p-l); + =} deadline(500 usecs) {= + instant_t d = lf_time_physical_elapsed() + - lf_time_logical_elapsed(); + lf_print("***** Deadline missed! Lag: " PRINTF_TIME + " (too late by " PRINTF_TIME " nsecs)", + d, d-500000); + =} } main reactor { - sense = new Sensor() - analyze = new Analysis() - actuate = new Actuator() - sense.y -> analyze.x - analyze.y -> actuate.x + sense = new Sensor() + analyze = new Analysis() + actuate = new Actuator() + sense.y -> analyze.x + analyze.y -> actuate.x } diff --git a/C/src/deadlines/PeriodicDeadline.lf b/C/src/deadlines/PeriodicDeadline.lf index a3d0c7b4..59777a3c 100644 --- a/C/src/deadlines/PeriodicDeadline.lf +++ b/C/src/deadlines/PeriodicDeadline.lf @@ -9,97 +9,97 @@ * subsystems: * * 1. This naive system is the only one that should miss deadlines. - * It starts its periodic sensing at the starting logical time, ignoring the - * lengthy time taken by the startup reaction. + * It starts its periodic sensing at the starting logical time, ignoring the + * lengthy time taken by the startup reaction. * * 2. This better system introduces an offset in the timer that an estimate - * of the maximum time that the startup reaction is likely to take. - * Only after that offset does it start periodic sensing. + * of the maximum time that the startup reaction is likely to take. + * Only after that offset does it start periodic sensing. * * 3. This even better system does not assume any knowledge of how long the - * startup reaction will take. Instead, at the conclusion of the startup - * reaction, it schedules an action that starts the periodic sensing at - * the current physical time. + * startup reaction will take. Instead, at the conclusion of the startup + * reaction, it schedules an action that starts the periodic sensing at + * the current physical time. * * @author Edward A. Lee */ target C { - timeout: 5s + timeout: 5s } reactor Sensor { - output y:int - state count:int = 0 + output y:int + state count:int = 0 } reactor OffsetSensor(offset:time = 0) extends Sensor { - timer t(offset, 500ms) - reaction(startup) {= - // Take a long time. - lf_sleep(SEC(2)); - =} - reaction(t) -> y {= - lf_set(y, self->count++); - =} + timer t(offset, 500ms) + reaction(startup) {= + // Take a long time. + lf_sleep(SEC(2)); + =} + reaction(t) -> y {= + lf_set(y, self->count++); + =} } reactor StartupSensor extends Sensor { - logical action a - reaction(startup) -> a {= - // Take a long time. - lf_sleep(SEC(2)); - // After startup phase is over, start periodic cycle. - // Calculate an offset for a reasonable starting logical time. - instant_t offset = lf_time_physical_elapsed(); - lf_schedule(a, offset); - =} - reaction(a) -> y, a {= - lf_set(y, self->count++); - lf_schedule(a, MSEC(500)); - =} + logical action a + reaction(startup) -> a {= + // Take a long time. + lf_sleep(SEC(2)); + // After startup phase is over, start periodic cycle. + // Calculate an offset for a reasonable starting logical time. + instant_t offset = lf_time_physical_elapsed(); + lf_schedule(a, offset); + =} + reaction(a) -> y, a {= + lf_set(y, self->count++); + lf_schedule(a, MSEC(500)); + =} } reactor Compute { - input x: int - output y: int + input x: int + output y: int - reaction(x) -> y {= - // Do some work... - lf_set(y, x->value); - =} + reaction(x) -> y {= + // Do some work... + lf_set(y, x->value); + =} } reactor Actuator(id:int = 1) { - input x: int - reaction(x) {= - lf_print("Actuator %d: Actuating with lag " PRINTF_TIME, - self->id, - lf_time_physical_elapsed() - lf_time_logical_elapsed() - ); - =} deadline(30ms) {= - lf_print("Actuator %d: **** Deadline missed with lag " PRINTF_TIME, - self->id, - lf_time_physical_elapsed() - lf_time_logical_elapsed() - ); - =} + input x: int + reaction(x) {= + lf_print("Actuator %d: Actuating with lag " PRINTF_TIME, + self->id, + lf_time_physical_elapsed() - lf_time_logical_elapsed() + ); + =} deadline(30ms) {= + lf_print("Actuator %d: **** Deadline missed with lag " PRINTF_TIME, + self->id, + lf_time_physical_elapsed() - lf_time_logical_elapsed() + ); + =} } main reactor { - s1 = new OffsetSensor(offset = 0) - c1 = new Compute() - a1 = new Actuator(id = 1) - s1.y -> c1.x - c1.y -> a1.x - - s2 = new OffsetSensor(offset = 2s) - c2 = new Compute() - a2 = new Actuator(id = 2) - s2.y -> c2.x - c2.y -> a2.x + s1 = new OffsetSensor(offset = 0) + c1 = new Compute() + a1 = new Actuator(id = 1) + s1.y -> c1.x + c1.y -> a1.x + + s2 = new OffsetSensor(offset = 2s) + c2 = new Compute() + a2 = new Actuator(id = 2) + s2.y -> c2.x + c2.y -> a2.x - s3 = new StartupSensor() - c3 = new Compute() - a3 = new Actuator(id = 3) - s3.y -> c3.x - c3.y -> a3.x + s3 = new StartupSensor() + c3 = new Compute() + a3 = new Actuator(id = 3) + s3.y -> c3.x + c3.y -> a3.x } diff --git a/C/src/keyboard/Keyboard.lf b/C/src/keyboard/Keyboard.lf index d26d5390..cf50a4bc 100644 --- a/C/src/keyboard/Keyboard.lf +++ b/C/src/keyboard/Keyboard.lf @@ -19,8 +19,8 @@ target C { preamble {= typedef struct keyboard_input_t { - void* keypress; // Pointer to physical action. - int exit_key; // Keycode for exiting. + void* keypress; // Pointer to physical action. + int exit_key; // Keycode for exiting. } keyboard_input_t; =} @@ -29,69 +29,69 @@ reactor KeyboardInput(exit_key: int = 0) { physical action keypress: int preamble {= - #include "platform.h" - #include - #include - // Thread to read input characters until an EOF is received. - // Each time a character is received, schedule a keypress action. - void* read_input(void* keyboard_input) { - int c; - while((c = getch()) != EOF) { - if (c == ((keyboard_input_t*)keyboard_input)->exit_key) { - lf_request_stop(); - break; - } - lf_schedule_copy(((keyboard_input_t*)keyboard_input)->keypress, 0, &c, 1); - } - return NULL; - } - // Function to direct printed messages to the curses-managed terminal. - void print_to_terminal(const char* format, va_list args) { - static int line_count = 1; - move(line_count++, 0); - vwprintw(stdscr, format, args); - refresh(); - if(line_count >= getmaxy(stdscr)) line_count = 1; - } - // Function for orderly shutdown upon control-c. - void sig_handler(int sig) { + #include "platform.h" + #include + #include + // Thread to read input characters until an EOF is received. + // Each time a character is received, schedule a keypress action. + void* read_input(void* keyboard_input) { + int c; + while((c = getch()) != EOF) { + if (c == ((keyboard_input_t*)keyboard_input)->exit_key) { lf_request_stop(); + break; + } + lf_schedule_copy(((keyboard_input_t*)keyboard_input)->keypress, 0, &c, 1); } + return NULL; + } + // Function to direct printed messages to the curses-managed terminal. + void print_to_terminal(const char* format, va_list args) { + static int line_count = 1; + move(line_count++, 0); + vwprintw(stdscr, format, args); + refresh(); + if(line_count >= getmaxy(stdscr)) line_count = 1; + } + // Function for orderly shutdown upon control-c. + void sig_handler(int sig) { + lf_request_stop(); + } =} reaction(startup) -> keypress {= - initscr(); // Initialize the curses library - cbreak(); // Disable line buffering - noecho(); // Disable automatic echoing of typed characters - keypad(stdscr, TRUE); // Enable special keys + initscr(); // Initialize the curses library + cbreak(); // Disable line buffering + noecho(); // Disable automatic echoing of typed characters + keypad(stdscr, TRUE); // Enable special keys - move(0, 0); - if(self->exit_key != 0) { - printw("Type %c to exit.\n", self->exit_key); - } - refresh(); + move(0, 0); + if(self->exit_key != 0) { + printw("Type %c to exit.\n", self->exit_key); + } + refresh(); - // Register a print function handler so lf_print works. - lf_register_print_function(print_to_terminal, LOG_LEVEL_ALL); + // Register a print function handler so lf_print works. + lf_register_print_function(print_to_terminal, LOG_LEVEL_ALL); - if (signal(SIGINT, sig_handler) == SIG_ERR) { - lf_print_warning("Failed to register signal handler. After exit, may have to reset terminal using 'reset'."); - } + if (signal(SIGINT, sig_handler) == SIG_ERR) { + lf_print_warning("Failed to register signal handler. After exit, may have to reset terminal using 'reset'."); + } - static keyboard_input_t keyboard_input; - keyboard_input.keypress = keypress; - keyboard_input.exit_key = self->exit_key; + static keyboard_input_t keyboard_input; + keyboard_input.keypress = keypress; + keyboard_input.exit_key = self->exit_key; - // Start the thread that listens for key presses. - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, &keyboard_input); + // Start the thread that listens for key presses. + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, &keyboard_input); =} reaction(keypress) -> key {= lf_set(key, keypress->value); =} reaction(shutdown) {= - endwin(); - lf_register_print_function(NULL, -1); + endwin(); + lf_register_print_function(NULL, -1); =} } diff --git a/C/src/leader-election/Election.lf b/C/src/leader-election/Election.lf index a02b6580..06abf4c1 100644 --- a/C/src/leader-election/Election.lf +++ b/C/src/leader-election/Election.lf @@ -16,38 +16,38 @@ */ target C reactor Node( - id:int(0) + id:int(0) ) { - input in:int - output out:int + input in:int + output out:int + + logical action a:int + + initial mode NotElected { + reaction(startup) -> out {= + lf_set(out, self->id); + =} + reaction(a) -> out {= + lf_set(out, a->value); + =} + reaction(in) -> a, Elected {= + if (in->value > self->id) { + lf_schedule_int(a, MSEC(10), in->value); + } else if (in->value == self->id) { + printf("I'm elected (node %d)!\n", self->id); + lf_set_mode(Elected); + } + =} + } + mode Elected { - logical action a:int - - initial mode NotElected { - reaction(startup) -> out {= - lf_set(out, self->id); - =} - reaction(a) -> out {= - lf_set(out, a->value); - =} - reaction(in) -> a, Elected {= - if (in->value > self->id) { - lf_schedule_int(a, MSEC(10), in->value); - } else if (in->value == self->id) { - printf("I'm elected (node %d)!\n", self->id); - lf_set_mode(Elected); - } - =} - } - mode Elected { - - } + } } federated reactor { - i0 = new Node() - i1 = new Node(id = 1) - i2 = new Node(id = 2) - i0.out -> i1.in - i1.out -> i2.in - i2.out -> i0.in -} \ No newline at end of file + i0 = new Node() + i1 = new Node(id = 1) + i2 = new Node(id = 2) + i0.out -> i1.in + i1.out -> i2.in + i2.out -> i0.in +} diff --git a/C/src/lib/PoissonClock.lf b/C/src/lib/PoissonClock.lf index 6916c6b0..8e5286d9 100644 --- a/C/src/lib/PoissonClock.lf +++ b/C/src/lib/PoissonClock.lf @@ -15,22 +15,22 @@ target C import Random from "Random.lf" preamble {= - #include + #include =} reactor PoissonClock(lambda:double(1.0)) extends Random { - output event:bool - logical action a - reaction(startup) -> a {= - double delta = exponential(self->lambda); - // Convert seconds to nanoseconds. - interval_t interval = (interval_t)(delta * SEC(1)); - lf_schedule(a, interval); - =} - reaction(a) -> event, a {= - lf_set(event, true); - double delta = exponential(self->lambda); - // Convert seconds to nanoseconds. - interval_t interval = (interval_t)(delta * SEC(1)); - lf_schedule(a, interval); - =} -} \ No newline at end of file + output event:bool + logical action a + reaction(startup) -> a {= + double delta = exponential(self->lambda); + // Convert seconds to nanoseconds. + interval_t interval = (interval_t)(delta * SEC(1)); + lf_schedule(a, interval); + =} + reaction(a) -> event, a {= + lf_set(event, true); + double delta = exponential(self->lambda); + // Convert seconds to nanoseconds. + interval_t interval = (interval_t)(delta * SEC(1)); + lf_schedule(a, interval); + =} +} diff --git a/C/src/lib/PrintToFile.lf b/C/src/lib/PrintToFile.lf index 4442bc56..a5182325 100644 --- a/C/src/lib/PrintToFile.lf +++ b/C/src/lib/PrintToFile.lf @@ -3,19 +3,19 @@ */ target C reactor PrintToFile(filename:string("output.data")) { - input y:double; - state file:FILE*({=NULL=}); - reaction(startup) {= - self->file = fopen(self->filename, "w"); - if(self->file == NULL) { - lf_print_error_and_exit("Failed to open file: %s", self->filename); - } - =} - reaction(y) {= - double t = lf_time_logical_elapsed() / 1.0e9; - fprintf(self->file, "%f %f\n", t, y->value); - =} - reaction(shutdown) {= - fclose(self->file); - =} + input y:double; + state file:FILE*({=NULL=}); + reaction(startup) {= + self->file = fopen(self->filename, "w"); + if(self->file == NULL) { + lf_print_error_and_exit("Failed to open file: %s", self->filename); + } + =} + reaction(y) {= + double t = lf_time_logical_elapsed() / 1.0e9; + fprintf(self->file, "%f %f\n", t, y->value); + =} + reaction(shutdown) {= + fclose(self->file); + =} } diff --git a/C/src/lib/Random.lf b/C/src/lib/Random.lf index 9caa4c06..aef15f20 100644 --- a/C/src/lib/Random.lf +++ b/C/src/lib/Random.lf @@ -22,20 +22,20 @@ */ target C preamble {= - #include - #include + #include + #include =} reactor Random(seed:{=unsigned int=}(0)) { - reaction(startup) {= - if (self->seed == 0) { - self->seed = (unsigned int)lf_time_logical(); - } - =} - method random():int {= - return rand_r(&self->seed); - =} - method exponential(lambda:double):double {= - double u = random() / (RAND_MAX + 1.0); - return -log(1.0 - u) / lambda; - =} -} \ No newline at end of file + reaction(startup) {= + if (self->seed == 0) { + self->seed = (unsigned int)lf_time_logical(); + } + =} + method random():int {= + return rand_r(&self->seed); + =} + method exponential(lambda:double):double {= + double u = random() / (RAND_MAX + 1.0); + return -log(1.0 - u) / lambda; + =} +} diff --git a/C/src/lib/RandomDelay.lf b/C/src/lib/RandomDelay.lf index ac0de178..e5dddeb5 100644 --- a/C/src/lib/RandomDelay.lf +++ b/C/src/lib/RandomDelay.lf @@ -10,16 +10,16 @@ target C import Random from "Random.lf" reactor RandomDelay(average:time(1 sec)) extends Random { - input in:time - output out:time - logical action a:time - reaction(a) -> out {= - lf_set(out, a->value); - =} - reaction(in) -> a {= - double lambda = SEC(1) / ((double)self->average); - double exp = exponential(lambda); - interval_t delay = (interval_t)llround(exp * (double)SEC(1)); - lf_schedule_copy(a, delay, &in->value, 1); - =} -} \ No newline at end of file + input in:time + output out:time + logical action a:time + reaction(a) -> out {= + lf_set(out, a->value); + =} + reaction(in) -> a {= + double lambda = SEC(1) / ((double)self->average); + double exp = exponential(lambda); + interval_t delay = (interval_t)llround(exp * (double)SEC(1)); + lf_schedule_copy(a, delay, &in->value, 1); + =} +} diff --git a/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf b/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf index 6fe6124a..ed812939 100644 --- a/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf +++ b/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf @@ -18,27 +18,27 @@ * @author Alexander Schulz-Rosengarten */ target C { - timeout: 3 secs, - fast: true, - flags: "-lm", - build: "./build_run_plot.sh FurutaPendulum" + timeout: 3 secs, + fast: true, + flags: "-lm", + build: "./build_run_plot.sh FurutaPendulum" } import PendulumController from "PendulumController.lf"; import PendulumSimulation from "PendulumSimulation.lf"; import Print from "Print.lf"; main reactor { - s = new PendulumSimulation(); - c = new PendulumController(); - p = new Print(); + s = new PendulumSimulation(); + c = new PendulumController(); + p = new Print(); - s.phi, s.d_phi -> c.phi, c.d_phi; - s.theta, s.d_theta -> c.theta, c.d_theta; - c.control -> s.u; + s.phi, s.d_phi -> c.phi, c.d_phi; + s.theta, s.d_theta -> c.theta, c.d_theta; + c.control -> s.u; - c.control -> p.control; - c.modeID -> p.modeID; - c.energy -> p.energy; - s.theta -> p.theta; - s.phi -> p.phi; + c.control -> p.control; + c.modeID -> p.modeID; + c.energy -> p.energy; + s.theta -> p.theta; + s.phi -> p.phi; } diff --git a/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf b/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf index 76b3746f..389d33a8 100644 --- a/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf +++ b/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf @@ -6,33 +6,33 @@ * @author Alexander Schulz-Rosengarten */ target C { - timeout: 5 secs, - fast: true, - flags: "-lm", - build: "./build_run_plot.sh FurutaPendulumDisturbance" + timeout: 5 secs, + fast: true, + flags: "-lm", + build: "./build_run_plot.sh FurutaPendulumDisturbance" } import PendulumController from "PendulumController.lf"; import PendulumSimulation from "PendulumSimulation.lf"; import Print from "Print.lf"; main reactor { - s = new PendulumSimulation(); - c = new PendulumController(); - p = new Print(); + s = new PendulumSimulation(); + c = new PendulumController(); + p = new Print(); - timer disturb(3 sec); - - reaction(disturb) -> s.d {= - lf_set(s.d, 0.5); - =} + timer disturb(3 sec); + + reaction(disturb) -> s.d {= + lf_set(s.d, 0.5); + =} - s.phi, s.d_phi -> c.phi, c.d_phi; - s.theta, s.d_theta -> c.theta, c.d_theta; - c.control -> s.u; + s.phi, s.d_phi -> c.phi, c.d_phi; + s.theta, s.d_theta -> c.theta, c.d_theta; + c.control -> s.u; - c.control -> p.control; - c.modeID -> p.modeID; - c.energy -> p.energy; - s.theta -> p.theta; - s.phi -> p.phi; + c.control -> p.control; + c.modeID -> p.modeID; + c.energy -> p.energy; + s.theta -> p.theta; + s.phi -> p.phi; } diff --git a/C/src/modal_models/FurutaPendulum/PendulumController.lf b/C/src/modal_models/FurutaPendulum/PendulumController.lf index 3aa66f66..5acef877 100644 --- a/C/src/modal_models/FurutaPendulum/PendulumController.lf +++ b/C/src/modal_models/FurutaPendulum/PendulumController.lf @@ -11,113 +11,113 @@ */ target C; preamble {= - #include - #define PI 3.14159265 - #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) - double sign(double x) { - return (x > 0.0) - (x < 0.0); - } - double restrictAngle(double theta) { - return((fmod(fabs(theta) + PI, 2 * PI) - PI) * sign(theta)); - } + #include + #define PI 3.14159265 + #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) + double sign(double x) { + return (x > 0.0) - (x < 0.0); + } + double restrictAngle(double theta) { + return((fmod(fabs(theta) + PI, 2 * PI) - PI) * sign(theta)); + } =} reactor PendulumController( - h:double(0.005), // Sample interval - w0:double(6.3), - k:double(0.5), // Energy multiplier to swing up. - n:double(0.5), // Bound on swing up control magnitude. - region1:double(0.1), // Region to exit SwingUp. - region2:double(0.2), // Region to exit Stabilize. - max_speed:double(0.05), // Speed to exit Catch. - ci1:double(-1.04945717118225), - ci2:double(-0.20432286791216), - ci3:double(-0.00735846749875), - ci4:double(-0.00735846749875), - si1:double(-1.70871686211144), - si2:double(-0.30395427746831), - si3:double(-0.03254225945714), - si4:double(-0.05808270221773), - phi2:double(-7.0124562) + h:double(0.005), // Sample interval + w0:double(6.3), + k:double(0.5), // Energy multiplier to swing up. + n:double(0.5), // Bound on swing up control magnitude. + region1:double(0.1), // Region to exit SwingUp. + region2:double(0.2), // Region to exit Stabilize. + max_speed:double(0.05), // Speed to exit Catch. + ci1:double(-1.04945717118225), + ci2:double(-0.20432286791216), + ci3:double(-0.00735846749875), + ci4:double(-0.00735846749875), + si1:double(-1.70871686211144), + si2:double(-0.30395427746831), + si3:double(-0.03254225945714), + si4:double(-0.05808270221773), + phi2:double(-7.0124562) ) { - input theta:double; - input d_theta:double; - input phi:double; - input d_phi:double; - - output control:double; - output modeID:double; - output energy:double; - - state phi0:double(0.0); - - initial mode SwingUp { - reaction(theta, d_theta) d_phi -> control, modeID, energy {= - double th = restrictAngle(theta->value); - double E = 0.5 - * d_theta->value - * d_theta->value - / (self->w0 * self->w0) - + cos(th) - 1.0; - double c = sign(d_theta->value * cos(th)); - double out = sign(E) * MIN(fabs(self->k * E), self->n) * c; - lf_set(control, out); - lf_set(energy, E); - lf_set(modeID, -1); - =} - reaction(theta) -> Catch {= - if (fabs(theta->value) < self->region1) { - lf_set_mode(Catch); - } - =} - } - - mode Catch { - reaction(theta, d_theta, phi, d_phi) -> control, modeID, energy {= - double th = restrictAngle(theta->value); - lf_set(control, -1.0 * ( - th * self->ci1 - + d_theta->value * self->ci2 - + (phi->value - self->phi2) * self->ci3 - + d_phi->value * self->ci4 - )); - lf_set(modeID, 0); - double E = 0.5 - * d_theta->value - * d_theta->value - / (self->w0 * self->w0) - + cos(th) - 1.0; - lf_set(energy, E); - =} - reaction(phi, d_phi) -> Stabilize {= - if (fabs(d_phi->value) < self->max_speed) { - lf_set_mode(Stabilize); - self->phi0 = phi->value; - } - =} - } + input theta:double; + input d_theta:double; + input phi:double; + input d_phi:double; + + output control:double; + output modeID:double; + output energy:double; + + state phi0:double(0.0); + + initial mode SwingUp { + reaction(theta, d_theta) d_phi -> control, modeID, energy {= + double th = restrictAngle(theta->value); + double E = 0.5 + * d_theta->value + * d_theta->value + / (self->w0 * self->w0) + + cos(th) - 1.0; + double c = sign(d_theta->value * cos(th)); + double out = sign(E) * MIN(fabs(self->k * E), self->n) * c; + lf_set(control, out); + lf_set(energy, E); + lf_set(modeID, -1); + =} + reaction(theta) -> Catch {= + if (fabs(theta->value) < self->region1) { + lf_set_mode(Catch); + } + =} + } + + mode Catch { + reaction(theta, d_theta, phi, d_phi) -> control, modeID, energy {= + double th = restrictAngle(theta->value); + lf_set(control, -1.0 * ( + th * self->ci1 + + d_theta->value * self->ci2 + + (phi->value - self->phi2) * self->ci3 + + d_phi->value * self->ci4 + )); + lf_set(modeID, 0); + double E = 0.5 + * d_theta->value + * d_theta->value + / (self->w0 * self->w0) + + cos(th) - 1.0; + lf_set(energy, E); + =} + reaction(phi, d_phi) -> Stabilize {= + if (fabs(d_phi->value) < self->max_speed) { + lf_set_mode(Stabilize); + self->phi0 = phi->value; + } + =} + } - mode Stabilize { - reaction(theta, d_theta, phi, d_phi) -> control, modeID, energy {= - double th = restrictAngle(theta->value); - lf_set(control, -1.0 * ( - th * self->si1 - + d_theta->value * self->si2 - + (phi->value - self->phi0) * self->si3 - + d_phi->value * self->si4 - )); - double E = 0.5 - * d_theta->value - * d_theta->value - / (self->w0 * self->w0) - + cos(th) - 1.0; - lf_set(energy, E); - lf_set(modeID, 1); - =} - reaction(theta) -> SwingUp {= - double th = restrictAngle(theta->value); - if (fabs(th) > self->region2) { - lf_set_mode(SwingUp); - } - =} - } + mode Stabilize { + reaction(theta, d_theta, phi, d_phi) -> control, modeID, energy {= + double th = restrictAngle(theta->value); + lf_set(control, -1.0 * ( + th * self->si1 + + d_theta->value * self->si2 + + (phi->value - self->phi0) * self->si3 + + d_phi->value * self->si4 + )); + double E = 0.5 + * d_theta->value + * d_theta->value + / (self->w0 * self->w0) + + cos(th) - 1.0; + lf_set(energy, E); + lf_set(modeID, 1); + =} + reaction(theta) -> SwingUp {= + double th = restrictAngle(theta->value); + if (fabs(th) > self->region2) { + lf_set_mode(SwingUp); + } + =} + } } diff --git a/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf b/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf index af43a3a7..0b597ca2 100644 --- a/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf +++ b/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf @@ -36,135 +36,135 @@ target C; * @author Edward A. Lee */ reactor PendulumSimulation( - initial_theta:double(-3.14159), // Initial pendulum angle. - sample_period:time(5 msec), // Sample period. - g:double(9.81), // Acceleration of gravity. - alpha:double(0.00260569), - beta:double(0.05165675), - gamma:double(9.7055e-4), - epsilon:double(0.08103060) + initial_theta:double(-3.14159), // Initial pendulum angle. + sample_period:time(5 msec), // Sample period. + g:double(9.81), // Acceleration of gravity. + alpha:double(0.00260569), + beta:double(0.05165675), + gamma:double(9.7055e-4), + epsilon:double(0.08103060) ){ - preamble {= - #include - =} - input u:double; // Control input. - input d:double; // Impulsive disturbance - - output theta:double; // Pendulum angle. - output d_theta:double; // Pendulum angular velocity. - output phi:double; // Arm angle. - output d_phi:double; // Arm angular velocity. - - state x:double[4](0.0, 0.0, 0.0, 0.0); // [theta, d_theta, phi, d_phi] - state first:bool(true); - state latest_u:double(0.0); - - timer t(0, sample_period); - - reaction(t) -> theta, d_theta, phi, d_phi {= - if (!self->first) { - // Update the state. - double x0_dot = self->x[1]; - double x1_dot = 1.0/( - self->alpha * self->beta - + pow(self->alpha * sin(self->x[0]), 2.0) - - pow(self->gamma * cos(self->x[0]), 2.0) - ) * ( - ( - self->alpha - * self->beta - + pow(self->alpha * sin(self->x[0]), 2.0) - ) - * pow(self->x[3], 2.0) - * sin(self->x[0]) - * cos(self->x[0]) - - - pow(self->gamma * self->x[1], 2.0) - * sin(self->x[0]) - * cos(self->x[0]) - + - 2.0 - * self->alpha - * self->gamma\ - * self->x[1] - * self->x[3] - * sin(self->x[0]) - * pow(cos(self->x[0]), 2.0) - - - self->gamma - * cos(self->x[0]) - * self->g - * self->latest_u - + - ( - self->alpha - * self->beta - + pow(self->alpha * sin(self->x[0]), 2.0) - ) - * self->epsilon / self->alpha * sin(self->x[0]) - ); - double x2_dot = self->x[3]; - double x3_dot = (1.0 / ( - self->alpha * self->beta - + pow(self->alpha * sin(self->x[0]), 2.0) - - pow(self->gamma * cos(self->x[0]), 2.0) - )) * ( - -self->gamma - * self->alpha - * pow(self->x[3], 2.0) - * sin(self->x[0]) - * pow(cos(self->x[1]), 2.0) - - - self->gamma - * self->epsilon - * sin(self->x[0]) - * cos(self->x[0]) - + - self->gamma - * self->alpha - * pow(self->x[1], 2.0) - * sin(self->x[0]) - - - 2 - * pow(self->alpha, 2.0) - * self->x[1] - * self->x[3] - * sin(self->x[0]) - * cos(self->x[0]) - + - self->alpha - * self->g - * self->latest_u - ); - double sample_period = self->sample_period * 1e-9; - self->x[0] += x0_dot * sample_period; - self->x[1] += x1_dot * sample_period; - self->x[2] += x2_dot * sample_period; - self->x[3] += x3_dot * sample_period; - } else { - self->x[0] = self->initial_theta; - self->first = false; - } - // Output the state. - lf_set(theta, self->x[0]); - lf_set(d_theta, self->x[1]); - lf_set(phi, self->x[2]); - lf_set(d_phi, self->x[3]); - =} - reaction(d) {= - // NOTE: If the disturbance is coincident with a sample, - // then it won't have any effect on theta until the next sample. - // Hence, this reaction needs to be after the previous. - // NOTE: If the disturbance is in between samples, then - // we effectively shift the disturbance to be - // simultaneous with the next sample. A more accurate model - // would apply the disturbance immediately. - // The update of the state vector in the reaction below - // should first be factored out to a method, when methods - // are implemented in C. - self->x[1] += d->value; - =} - reaction(u) {= - self->latest_u = u->value; - =} + preamble {= + #include + =} + input u:double; // Control input. + input d:double; // Impulsive disturbance + + output theta:double; // Pendulum angle. + output d_theta:double; // Pendulum angular velocity. + output phi:double; // Arm angle. + output d_phi:double; // Arm angular velocity. + + state x:double[4](0.0, 0.0, 0.0, 0.0); // [theta, d_theta, phi, d_phi] + state first:bool(true); + state latest_u:double(0.0); + + timer t(0, sample_period); + + reaction(t) -> theta, d_theta, phi, d_phi {= + if (!self->first) { + // Update the state. + double x0_dot = self->x[1]; + double x1_dot = 1.0/( + self->alpha * self->beta + + pow(self->alpha * sin(self->x[0]), 2.0) + - pow(self->gamma * cos(self->x[0]), 2.0) + ) * ( + ( + self->alpha + * self->beta + + pow(self->alpha * sin(self->x[0]), 2.0) + ) + * pow(self->x[3], 2.0) + * sin(self->x[0]) + * cos(self->x[0]) + - + pow(self->gamma * self->x[1], 2.0) + * sin(self->x[0]) + * cos(self->x[0]) + + + 2.0 + * self->alpha + * self->gamma\ + * self->x[1] + * self->x[3] + * sin(self->x[0]) + * pow(cos(self->x[0]), 2.0) + - + self->gamma + * cos(self->x[0]) + * self->g + * self->latest_u + + + ( + self->alpha + * self->beta + + pow(self->alpha * sin(self->x[0]), 2.0) + ) + * self->epsilon / self->alpha * sin(self->x[0]) + ); + double x2_dot = self->x[3]; + double x3_dot = (1.0 / ( + self->alpha * self->beta + + pow(self->alpha * sin(self->x[0]), 2.0) + - pow(self->gamma * cos(self->x[0]), 2.0) + )) * ( + -self->gamma + * self->alpha + * pow(self->x[3], 2.0) + * sin(self->x[0]) + * pow(cos(self->x[1]), 2.0) + - + self->gamma + * self->epsilon + * sin(self->x[0]) + * cos(self->x[0]) + + + self->gamma + * self->alpha + * pow(self->x[1], 2.0) + * sin(self->x[0]) + - + 2 + * pow(self->alpha, 2.0) + * self->x[1] + * self->x[3] + * sin(self->x[0]) + * cos(self->x[0]) + + + self->alpha + * self->g + * self->latest_u + ); + double sample_period = self->sample_period * 1e-9; + self->x[0] += x0_dot * sample_period; + self->x[1] += x1_dot * sample_period; + self->x[2] += x2_dot * sample_period; + self->x[3] += x3_dot * sample_period; + } else { + self->x[0] = self->initial_theta; + self->first = false; + } + // Output the state. + lf_set(theta, self->x[0]); + lf_set(d_theta, self->x[1]); + lf_set(phi, self->x[2]); + lf_set(d_phi, self->x[3]); + =} + reaction(d) {= + // NOTE: If the disturbance is coincident with a sample, + // then it won't have any effect on theta until the next sample. + // Hence, this reaction needs to be after the previous. + // NOTE: If the disturbance is in between samples, then + // we effectively shift the disturbance to be + // simultaneous with the next sample. A more accurate model + // would apply the disturbance immediately. + // The update of the state vector in the reaction below + // should first be factored out to a method, when methods + // are implemented in C. + self->x[1] += d->value; + =} + reaction(u) {= + self->latest_u = u->value; + =} } diff --git a/C/src/modal_models/FurutaPendulum/Print.lf b/C/src/modal_models/FurutaPendulum/Print.lf index 951cbcbd..85895b84 100644 --- a/C/src/modal_models/FurutaPendulum/Print.lf +++ b/C/src/modal_models/FurutaPendulum/Print.lf @@ -4,42 +4,42 @@ target C; preamble {= - #include - #define PI 3.14159265 + #include + #define PI 3.14159265 =} reactor Print(filename:string("pendulum.csv")) { - input control:double; - input modeID:double; - input energy:double; - input theta:double; - input phi:double; + input control:double; + input modeID:double; + input energy:double; + input theta:double; + input phi:double; - state file:FILE*({=NULL=}); + state file:FILE*({=NULL=}); - reaction(startup) {= - self->file = fopen(self->filename, "w"); - if(self->file == NULL) { - lf_print_error_and_exit("Failed to open file: %s", self->filename); - } else { - fprintf(self->file, "Time,Control,Mode,Energy,Theta,Phi\n"); - } - =} + reaction(startup) {= + self->file = fopen(self->filename, "w"); + if(self->file == NULL) { + lf_print_error_and_exit("Failed to open file: %s", self->filename); + } else { + fprintf(self->file, "Time,Control,Mode,Energy,Theta,Phi\n"); + } + =} - reaction(control, modeID, energy, theta, phi) {= - double t = lf_time_logical_elapsed() / 1.0e9; - fprintf(self->file, - "%f,%f,%f,%f,%f,%f\n", - t, - control->value, - modeID->value, - energy->value, - fmod(theta->value, PI), - fmod(phi->value, PI) - ); - =} + reaction(control, modeID, energy, theta, phi) {= + double t = lf_time_logical_elapsed() / 1.0e9; + fprintf(self->file, + "%f,%f,%f,%f,%f,%f\n", + t, + control->value, + modeID->value, + energy->value, + fmod(theta->value, PI), + fmod(phi->value, PI) + ); + =} - reaction(shutdown) {= - fclose(self->file); - =} + reaction(shutdown) {= + fclose(self->file); + =} } diff --git a/C/src/mqtt/MQTTDistributed.lf b/C/src/mqtt/MQTTDistributed.lf index de84cc03..88b017fd 100644 --- a/C/src/mqtt/MQTTDistributed.lf +++ b/C/src/mqtt/MQTTDistributed.lf @@ -25,11 +25,11 @@ * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", - "include/mosquitto-extension.cmake"], - timeout: 10 secs, - coordination: centralized, + cmake-include: [ + "include/paho-extension.cmake", + "include/mosquitto-extension.cmake"], + timeout: 10 secs, + coordination: centralized, }; import MQTTPublisher from "lib/MQTTPublisher.lf"; @@ -37,27 +37,27 @@ import MQTTSubscriber from "lib/MQTTSubscriber.lf"; import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; reactor Source { - msg = new MessageGenerator(root = "Hello World"); - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883", - include_timestamp = true - ); - msg.message->pub.in; + msg = new MessageGenerator(root = "Hello World"); + pub = new MQTTPublisher( + topic = "my/test", + address = "tcp://localhost:1883", + include_timestamp = true + ); + msg.message->pub.in; } reactor Destination { - sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = false, - offset = 0 sec - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + sub = new MQTTSubscriber( + address = "tcp://localhost:1883", + topic = "my/test", + use_physical_time = false, + offset = 0 sec + ); + dsp = new PrintMessage(); + sub.message->dsp.message; } federated reactor { - source = new Source(); - destination = new Destination(); + source = new Source(); + destination = new Destination(); } diff --git a/C/src/mqtt/MQTTDistributedActivity.lf b/C/src/mqtt/MQTTDistributedActivity.lf index 867a75e4..cb17816c 100644 --- a/C/src/mqtt/MQTTDistributedActivity.lf +++ b/C/src/mqtt/MQTTDistributedActivity.lf @@ -12,11 +12,11 @@ * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", - "include/mosquitto-extension.cmake"], - timeout: 10 secs, - coordination: centralized, + cmake-include: [ + "include/paho-extension.cmake", + "include/mosquitto-extension.cmake"], + timeout: 10 secs, + coordination: centralized, }; import MQTTPublisher from "lib/MQTTPublisher.lf"; @@ -24,34 +24,34 @@ import MQTTSubscriber from "lib/MQTTSubscriber.lf"; import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; reactor Source { - msg = new MessageGenerator(root = "Hello World"); - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883", - include_timestamp = true - ); - msg.message->pub.in; + msg = new MessageGenerator(root = "Hello World"); + pub = new MQTTPublisher( + topic = "my/test", + address = "tcp://localhost:1883", + include_timestamp = true + ); + msg.message->pub.in; } reactor Destination { - timer t(1001 ms, 1 s) - sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = false, - offset = 0 sec + timer t(1001 ms, 1 s) + sub = new MQTTSubscriber( + address = "tcp://localhost:1883", + topic = "my/test", + use_physical_time = false, + offset = 0 sec + ); + dsp = new PrintMessage(); + sub.message->dsp.message; + reaction(t) {= + tag_t tag = lf_tag(); + lf_print("Destination: Activity at " PRINTF_TAG, + tag.time - start_time, tag.microstep ); - dsp = new PrintMessage(); - sub.message->dsp.message; - reaction(t) {= - tag_t tag = lf_tag(); - lf_print("Destination: Activity at " PRINTF_TAG, - tag.time - start_time, tag.microstep - ); - =} + =} } federated reactor { - source = new Source(); - destination = new Destination(); + source = new Source(); + destination = new Destination(); } diff --git a/C/src/mqtt/MQTTLegacy.lf b/C/src/mqtt/MQTTLegacy.lf index 8c9a1c48..1c910982 100644 --- a/C/src/mqtt/MQTTLegacy.lf +++ b/C/src/mqtt/MQTTLegacy.lf @@ -6,19 +6,19 @@ * to. For example, you can subscribe to these messages using * the command-line utility (in another window): * - * mosquitto_sub -t 'legacy' + * mosquitto_sub -t 'legacy' * * You can publish your own messages on this topic using any * MQTT publisher, such as the command line utility: * - * mosquitto_pub -t 'legacy' -m '******* My own message!' + * mosquitto_pub -t 'legacy' -m '******* My own message!' * * This is a federated program, the publisher and subscriber run * in separate programs. This would work pretty much the same * way, however, as an unfederated program. To run as an * unfederated program, add to cmake-include the following file: * - * "include/net_utils.cmake" + * "include/net_utils.cmake" * * and change the `federated` keyword to `main`. * @@ -27,11 +27,11 @@ * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", - "include/mosquitto-extension.cmake"], - timeout: 5 min, - coordination: centralized, + cmake-include: [ + "include/paho-extension.cmake", + "include/mosquitto-extension.cmake"], + timeout: 5 min, + coordination: centralized, }; import MQTTPublisher from "lib/MQTTPublisher.lf"; @@ -39,30 +39,30 @@ import MQTTSubscriber from "lib/MQTTSubscriber.lf"; import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; reactor Publisher { - msg = new MessageGenerator( - root = "Legacy Message", - period = 5 sec - ); - pub = new MQTTPublisher( - topic = "legacy", - address = "tcp://localhost:1883", - include_timestamp = false - ); - msg.message->pub.in; + msg = new MessageGenerator( + root = "Legacy Message", + period = 5 sec + ); + pub = new MQTTPublisher( + topic = "legacy", + address = "tcp://localhost:1883", + include_timestamp = false + ); + msg.message->pub.in; } reactor Subscriber { - sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "legacy", - use_physical_time = true, - offset = 0 sec - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + sub = new MQTTSubscriber( + address = "tcp://localhost:1883", + topic = "legacy", + use_physical_time = true, + offset = 0 sec + ); + dsp = new PrintMessage(); + sub.message->dsp.message; } federated reactor { - source = new Publisher(); - destination = new Subscriber(); + source = new Publisher(); + destination = new Subscriber(); } diff --git a/C/src/mqtt/MQTTLogical.lf b/C/src/mqtt/MQTTLogical.lf index 6ac5d287..55ba4ffc 100644 --- a/C/src/mqtt/MQTTLogical.lf +++ b/C/src/mqtt/MQTTLogical.lf @@ -16,11 +16,11 @@ * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", // For #include "MQTTClient.h" - "include/net_utils.cmake" // For encode_int64() - ], - timeout: 10 secs, + cmake-include: [ + "include/paho-extension.cmake", // For #include "MQTTClient.h" + "include/net_utils.cmake" // For encode_int64() + ], + timeout: 10 secs, }; import MQTTPublisher from "lib/MQTTPublisher.lf"; @@ -28,20 +28,20 @@ import MQTTSubscriber from "lib/MQTTSubscriber.lf"; import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; main reactor { - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883", - include_timestamp = true - ); - msg = new MessageGenerator(root = "Hello World"); - msg.message->pub.in; - - sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = false, - offset = 0 - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + pub = new MQTTPublisher( + topic = "my/test", + address = "tcp://localhost:1883", + include_timestamp = true + ); + msg = new MessageGenerator(root = "Hello World"); + msg.message->pub.in; + + sub = new MQTTSubscriber( + address = "tcp://localhost:1883", + topic = "my/test", + use_physical_time = false, + offset = 0 + ); + dsp = new PrintMessage(); + sub.message->dsp.message; } diff --git a/C/src/mqtt/MQTTPhysical.lf b/C/src/mqtt/MQTTPhysical.lf index 06a76bb2..f430c621 100644 --- a/C/src/mqtt/MQTTPhysical.lf +++ b/C/src/mqtt/MQTTPhysical.lf @@ -13,11 +13,11 @@ * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", // For #include "MQTTClient.h" - "include/net_utils.cmake" // For encode_int64() - ], - timeout: 10 secs, + cmake-include: [ + "include/paho-extension.cmake", // For #include "MQTTClient.h" + "include/net_utils.cmake" // For encode_int64() + ], + timeout: 10 secs, }; import MQTTPublisher from "lib/MQTTPublisher.lf"; @@ -25,19 +25,19 @@ import MQTTSubscriber from "lib/MQTTSubscriber.lf"; import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; main reactor { - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883" - ); - msg = new MessageGenerator(root = "Hello World"); - msg.message->pub.in; - - sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = true, - offset = 0 - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + pub = new MQTTPublisher( + topic = "my/test", + address = "tcp://localhost:1883" + ); + msg = new MessageGenerator(root = "Hello World"); + msg.message->pub.in; + + sub = new MQTTSubscriber( + address = "tcp://localhost:1883", + topic = "my/test", + use_physical_time = true, + offset = 0 + ); + dsp = new PrintMessage(); + sub.message->dsp.message; } diff --git a/C/src/mqtt/lib/MQTTPublisher.lf b/C/src/mqtt/lib/MQTTPublisher.lf index 2aab350c..7f9cdbbf 100644 --- a/C/src/mqtt/lib/MQTTPublisher.lf +++ b/C/src/mqtt/lib/MQTTPublisher.lf @@ -13,10 +13,10 @@ target C * then two things happen: * * 1. The publisher ensures that the message is null terminated by - * adding a null terminator if needed. This ensures that the message - * can be treated as a string at the receiving end. + * adding a null terminator if needed. This ensures that the message + * can be treated as a string at the receiving end. * 2. The publisher appends to the end of the message the current logical - * time at which the publishing occurs. + * time at which the publishing occurs. * * This can be useful if the receiving end will be an instance * of `MQTTSubscriber` in another Lingua Franca program. @@ -33,184 +33,184 @@ target C * @author Edward A. Lee */ reactor MQTTPublisher ( - topic:string("DefaultTopic"), - address:string("tcp://localhost:1883"), - include_timestamp:bool(false), - timeout:time(10 sec) + topic:string("DefaultTopic"), + address:string("tcp://localhost:1883"), + include_timestamp:bool(false), + timeout:time(10 sec) ) { - preamble {= - #include "MQTTClient.h" - #include "core/federated/net_util.h" - - // Count of instances of this reactor so that unique client IDs are generated. - static size_t _lf_MQTTPublisher_count = 0; - - // Connection options for the client. - // Making this global means that all instances of this reactor have - // the same connection options. - MQTTClient_connectOptions pub_connect_options = MQTTClient_connectOptions_initializer; + preamble {= + #include "MQTTClient.h" + #include "core/federated/net_util.h" + + // Count of instances of this reactor so that unique client IDs are generated. + static size_t _lf_MQTTPublisher_count = 0; + + // Connection options for the client. + // Making this global means that all instances of this reactor have + // the same connection options. + MQTTClient_connectOptions pub_connect_options = MQTTClient_connectOptions_initializer; - // Struct type used to keep track of messages in flight between reactions. - typedef struct inflight_t { - bool message_in_flight; - MQTTClient_deliveryToken delivery_token; - char* message; - } inflight_t; - - // Callback invoked once delivery is complete. - void pub_delivered(void *inflight, MQTTClient_deliveryToken dt) { - LF_PRINT_LOG("MQTTPublisher: Message with token value %d delivery confirmed\n", dt); - ((inflight_t*)inflight)->message_in_flight = false; - free(((inflight_t*)inflight)->message); - ((inflight_t*)inflight)->delivery_token = 0; - ((inflight_t*)inflight)->message = NULL; - } - // Callback invoked if the connection is lost. - void pub_connection_lost(void *context, char *cause) { - lf_print_error("\nMQTTPublisher: Connection lost. Cause: %s\n", cause); - } - =} + // Struct type used to keep track of messages in flight between reactions. + typedef struct inflight_t { + bool message_in_flight; + MQTTClient_deliveryToken delivery_token; + char* message; + } inflight_t; - /** - * Input type char* instead of string is used for dynamically - * allocated character arrays (as opposed to static constant strings). - */ - input in:char*; + // Callback invoked once delivery is complete. + void pub_delivered(void *inflight, MQTTClient_deliveryToken dt) { + LF_PRINT_LOG("MQTTPublisher: Message with token value %d delivery confirmed\n", dt); + ((inflight_t*)inflight)->message_in_flight = false; + free(((inflight_t*)inflight)->message); + ((inflight_t*)inflight)->delivery_token = 0; + ((inflight_t*)inflight)->message = NULL; + } + // Callback invoked if the connection is lost. + void pub_connection_lost(void *context, char *cause) { + lf_print_error("\nMQTTPublisher: Connection lost. Cause: %s\n", cause); + } + =} + + /** + * Input type char* instead of string is used for dynamically + * allocated character arrays (as opposed to static constant strings). + */ + input in:char*; + + /** State variable that keeps track of a message in flight. */ + state inflight:inflight_t({={false, 0, NULL}=}); + + /** Client ID. This is automatically generated. */ + state clientID:char*({= NULL =}); + + /** The client object. */ + state client:MQTTClient({=NULL=}); + + /** The message object. */ + state mqtt_msg:MQTTClient_message({=MQTTClient_message_initializer=}); + + /** Connect to the broker. Exit if this fails. */ + reaction(startup){= + // In case there are multiple instances of this or the subscriber, enter + // a critical section. The Paho MQTT functions are not thread safe. + lf_critical_section_enter(); - /** State variable that keeps track of a message in flight. */ - state inflight:inflight_t({={false, 0, NULL}=}); + // Create a unique ID. + if (asprintf(&self->clientID, "LF_MQTTPublisher_%zu", _lf_MQTTPublisher_count++) < 0) { + lf_print_error_and_exit("MQTTPublisher: Failed to create client ID."); + } + + MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); + pub_connect_options.keepAliveInterval = 20; + pub_connect_options.cleansession = 1; + + // Set up callback functions. + // Second to last argument should be a pointer to a function + // to handle notification of delivery of a message. + // But this reactor isn't sending any messages. + // Second argument is a pointer to context that will be passed to pub_delivered, + // which in this case is a pointer to the inflight state variable. + MQTTClient_setCallbacks(self->client, &self->inflight, pub_connection_lost, NULL, pub_delivered); - /** Client ID. This is automatically generated. */ - state clientID:char*({= NULL =}); + // Connect to the broker. + int rc; // response code. + if ((rc = MQTTClient_connect(self->client, &pub_connect_options)) != MQTTCLIENT_SUCCESS) { + lf_print_error_and_exit("MQTTPublisher: Failed to connect to MQTT broker.\n" + "Perhaps one is not running? Return code: %d", rc); + } - /** The client object. */ - state client:MQTTClient({=NULL=}); + lf_critical_section_exit(); - /** The message object. */ - state mqtt_msg:MQTTClient_message({=MQTTClient_message_initializer=}); + LF_PRINT_LOG("MQTTPublisher: connected to broker."); + =} + + /** + * React to an input by sending a message with the value of the input as the payload. + * If delivery has not yet completed for a previously sent message, then wait for + * it to complete before proceeding (blocking this reaction). + * This copies the message from the input into a buffer, so the input can + * freed upon return from this reaction. + */ + reaction(in) {= + // In case there are multiple instances of this or the subscriber, enter + // a critical section. The Paho MQTT functions are not thread safe. + lf_critical_section_enter(); - /** Connect to the broker. Exit if this fails. */ - reaction(startup){= - // In case there are multiple instances of this or the subscriber, enter - // a critical section. The Paho MQTT functions are not thread safe. - lf_critical_section_enter(); - - // Create a unique ID. - if (asprintf(&self->clientID, "LF_MQTTPublisher_%zu", _lf_MQTTPublisher_count++) < 0) { - lf_print_error_and_exit("MQTTPublisher: Failed to create client ID."); - } - - MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); - pub_connect_options.keepAliveInterval = 20; - pub_connect_options.cleansession = 1; - - // Set up callback functions. - // Second to last argument should be a pointer to a function - // to handle notification of delivery of a message. - // But this reactor isn't sending any messages. - // Second argument is a pointer to context that will be passed to pub_delivered, - // which in this case is a pointer to the inflight state variable. - MQTTClient_setCallbacks(self->client, &self->inflight, pub_connection_lost, NULL, pub_delivered); - - // Connect to the broker. - int rc; // response code. - if ((rc = MQTTClient_connect(self->client, &pub_connect_options)) != MQTTCLIENT_SUCCESS) { - lf_print_error_and_exit("MQTTPublisher: Failed to connect to MQTT broker.\n" - "Perhaps one is not running? Return code: %d", rc); - } - - lf_critical_section_exit(); - - LF_PRINT_LOG("MQTTPublisher: connected to broker."); - =} + if(self->inflight.message_in_flight) { + // Wait for message delivery to be complete. + LF_PRINT_LOG("MQTTPublisher: Waiting for confirmation of publication of previous message"); + int rc = MQTTClient_waitForCompletion( + self->client, self->inflight.delivery_token, self->timeout + ); + if (rc != MQTTCLIENT_SUCCESS) { + lf_print_error("MQTTPublisher: Message delivery failed with error code %d.\n", rc); + lf_print_error(" Message: %s\n", in->value); + lf_print_error(" On topic '%s' for publisher with ClientID: %s\n", self->topic, self->clientID); + } + } + LF_PRINT_LOG("MQTTPublisher: Publishing message: %s", in->value); + LF_PRINT_LOG("MQTTPublisher: on topic '%s' for publisher with ClientID: %s", self->topic, self->clientID); - /** - * React to an input by sending a message with the value of the input as the payload. - * If delivery has not yet completed for a previously sent message, then wait for - * it to complete before proceeding (blocking this reaction). - * This copies the message from the input into a buffer, so the input can - * freed upon return from this reaction. - */ - reaction(in) {= - // In case there are multiple instances of this or the subscriber, enter - // a critical section. The Paho MQTT functions are not thread safe. - lf_critical_section_enter(); - - if(self->inflight.message_in_flight) { - // Wait for message delivery to be complete. - LF_PRINT_LOG("MQTTPublisher: Waiting for confirmation of publication of previous message"); - int rc = MQTTClient_waitForCompletion( - self->client, self->inflight.delivery_token, self->timeout - ); - if (rc != MQTTCLIENT_SUCCESS) { - lf_print_error("MQTTPublisher: Message delivery failed with error code %d.\n", rc); - lf_print_error(" Message: %s\n", in->value); - lf_print_error(" On topic '%s' for publisher with ClientID: %s\n", self->topic, self->clientID); - } - } - LF_PRINT_LOG("MQTTPublisher: Publishing message: %s", in->value); - LF_PRINT_LOG("MQTTPublisher: on topic '%s' for publisher with ClientID: %s", self->topic, self->clientID); - - // Allocate memory for a copy of the message. - // The default length is just the length of the incoming message. - int length = in->length; - // If a timestamp is to be included, the length is bigger. - if (self->include_timestamp) { - // If the input message is not null terminated, then add a null terminator. - if (in->value[in->length - 1] != '\0') length++; - // Allow space (4 bytes) for the magic string "LFts". - length += 4 + sizeof(instant_t); - } + // Allocate memory for a copy of the message. + // The default length is just the length of the incoming message. + int length = in->length; + // If a timestamp is to be included, the length is bigger. + if (self->include_timestamp) { + // If the input message is not null terminated, then add a null terminator. + if (in->value[in->length - 1] != '\0') length++; + // Allow space (4 bytes) for the magic string "LFts". + length += 4 + sizeof(instant_t); + } - self->inflight.message = (char*) malloc(sizeof(char) * length); - memcpy(self->inflight.message, in->value, in->length); - // Append null terminator and timestamp, if appropriate. - if (self->include_timestamp) { - // If the input message is not null terminated, then add a null terminator. - if (in->value[in->length - 1] != '\0') { - self->inflight.message[in->length] = '\0'; - // Add magic string. - memcpy(&self->inflight.message[in->length + 1], "LFts", 4); - } else { - // Add magic string. - memcpy(&self->inflight.message[in->length], "LFts", 4); - } + self->inflight.message = (char*) malloc(sizeof(char) * length); + memcpy(self->inflight.message, in->value, in->length); + // Append null terminator and timestamp, if appropriate. + if (self->include_timestamp) { + // If the input message is not null terminated, then add a null terminator. + if (in->value[in->length - 1] != '\0') { + self->inflight.message[in->length] = '\0'; + // Add magic string. + memcpy(&self->inflight.message[in->length + 1], "LFts", 4); + } else { + // Add magic string. + memcpy(&self->inflight.message[in->length], "LFts", 4); + } - // Append the current timestamp to the message. - instant_t timestamp = lf_time_logical(); - encode_int64(timestamp, - (unsigned char*)(self->inflight.message + length - sizeof(instant_t)) - ); - LF_PRINT_LOG("MQTTPublisher: Timestamp (elapsed) of sending message: " PRINTF_TIME, - timestamp - lf_time_start() - ); - } - self->mqtt_msg.payload = self->inflight.message; - self->mqtt_msg.payloadlen = length; - - // QoS 2 means that the message will be delivered exactly once. - self->mqtt_msg.qos = 2; - - // Retained messages are held by the server and sent to future new subscribers. - // Specify that this message should not be retained. - // It will be sent only to subscribers currently subscribed. - self->mqtt_msg.retained = 0; - - MQTTClient_publishMessage(self->client, self->topic, &self->mqtt_msg, &self->inflight.delivery_token); - self->inflight.message_in_flight = true; - - lf_critical_section_exit(); - - // It is not clear why the following is needed, but the message - // does not go out until the next invocation without it. - MQTTClient_yield(); - =} + // Append the current timestamp to the message. + instant_t timestamp = lf_time_logical(); + encode_int64(timestamp, + (unsigned char*)(self->inflight.message + length - sizeof(instant_t)) + ); + LF_PRINT_LOG("MQTTPublisher: Timestamp (elapsed) of sending message: " PRINTF_TIME, + timestamp - lf_time_start() + ); + } + self->mqtt_msg.payload = self->inflight.message; + self->mqtt_msg.payloadlen = length; + + // QoS 2 means that the message will be delivered exactly once. + self->mqtt_msg.qos = 2; + + // Retained messages are held by the server and sent to future new subscribers. + // Specify that this message should not be retained. + // It will be sent only to subscribers currently subscribed. + self->mqtt_msg.retained = 0; + + MQTTClient_publishMessage(self->client, self->topic, &self->mqtt_msg, &self->inflight.delivery_token); + self->inflight.message_in_flight = true; + + lf_critical_section_exit(); - /** Disconnect the client. */ - reaction(shutdown) {= - LF_PRINT_LOG("MQTTPublisher: Client ID %s disconnecting.", self->clientID); - if (self->clientID) free(self->clientID); - MQTTClient_disconnect(self->client, 10000); - MQTTClient_destroy(&self->client); - =} + // It is not clear why the following is needed, but the message + // does not go out until the next invocation without it. + MQTTClient_yield(); + =} + + /** Disconnect the client. */ + reaction(shutdown) {= + LF_PRINT_LOG("MQTTPublisher: Client ID %s disconnecting.", self->clientID); + if (self->clientID) free(self->clientID); + MQTTClient_disconnect(self->client, 10000); + MQTTClient_destroy(&self->client); + =} } diff --git a/C/src/mqtt/lib/MQTTSubscriber.lf b/C/src/mqtt/lib/MQTTSubscriber.lf index abaf6788..a0f205bd 100644 --- a/C/src/mqtt/lib/MQTTSubscriber.lf +++ b/C/src/mqtt/lib/MQTTSubscriber.lf @@ -47,224 +47,224 @@ target C * @author Edward A. Lee */ reactor MQTTSubscriber ( - address:string("tcp://localhost:1883"), - topic:string("DefaultTopic"), - use_physical_time:bool(true), - offset:time(0) + address:string("tcp://localhost:1883"), + topic:string("DefaultTopic"), + use_physical_time:bool(true), + offset:time(0) ) { - preamble {= - #include "MQTTClient.h" - #include "core/federated/net_util.h" - - // Fix the QoS to indicate that the message will be delivered reliably exactly once. - #define QOS 2 + preamble {= + #include "MQTTClient.h" + #include "core/federated/net_util.h" + + // Fix the QoS to indicate that the message will be delivered reliably exactly once. + #define QOS 2 - // Count of instances of this reactor so that unique client IDs are generated. - static size_t _lf_MQTTSubscriber_count = 0; + // Count of instances of this reactor so that unique client IDs are generated. + static size_t _lf_MQTTSubscriber_count = 0; + + typedef struct MQTTSubscriber_info_t { + void* logical_action; + interval_t offset; + bool use_physical_time; + interval_t latencies; // Sum of all observed latencies. + interval_t max_latency; + size_t count; + } MQTTSubscriber_info_t; + + // Connection options for the client. + // Making this global means that all instances of this reactor have + // the same connection options. + MQTTClient_connectOptions sub_connect_options = MQTTClient_connectOptions_initializer; - typedef struct MQTTSubscriber_info_t { - void* logical_action; - interval_t offset; - bool use_physical_time; - interval_t latencies; // Sum of all observed latencies. - interval_t max_latency; - size_t count; - } MQTTSubscriber_info_t; + // Callback function invoked by MQTT when a message arrives. + int message_arrived( + void *info, + char *topic_name, + int topic_length, // If 0, strlen(topic_name) can be trusted. + MQTTClient_message *message + ) { + // FIXME: This is assuming that the message string + // and topic_name are null terminated. What if they aren't? + // Perhaps force them to be? + LF_PRINT_LOG( + "MQTTSubscriber: Message arrived on topic %s: %s", topic_name, (char*)message->payload + ); + + MQTTSubscriber_info_t* my_info = (MQTTSubscriber_info_t*)info; + + // Enter a critical section so that logical time does not elapse while + // we calculate the delay to the logical time for the message. + lf_critical_section_enter(); + + interval_t delay; + instant_t current_time = lf_time_logical(); + interval_t offset = my_info->offset; + + // Extract the publisher's timestamp from the message, if it is present. + if ( + // Is the string null terminated? + (int)((char*)message->payload)[message->payloadlen - sizeof(instant_t) - 5] == '\0' + // Is the magic string present? + && memcmp("LFts", &message->payload[message->payloadlen - sizeof(instant_t) - 4], 4) == 0 + ) { + my_info->count++; - // Connection options for the client. - // Making this global means that all instances of this reactor have - // the same connection options. - MQTTClient_connectOptions sub_connect_options = MQTTClient_connectOptions_initializer; - - // Callback function invoked by MQTT when a message arrives. - int message_arrived( - void *info, - char *topic_name, - int topic_length, // If 0, strlen(topic_name) can be trusted. - MQTTClient_message *message - ) { - // FIXME: This is assuming that the message string - // and topic_name are null terminated. What if they aren't? - // Perhaps force them to be? - LF_PRINT_LOG( - "MQTTSubscriber: Message arrived on topic %s: %s", topic_name, (char*)message->payload - ); - - MQTTSubscriber_info_t* my_info = (MQTTSubscriber_info_t*)info; - - // Enter a critical section so that logical time does not elapse while - // we calculate the delay to the logical time for the message. - lf_critical_section_enter(); - - interval_t delay; - instant_t current_time = lf_time_logical(); - interval_t offset = my_info->offset; - - // Extract the publisher's timestamp from the message, if it is present. - if ( - // Is the string null terminated? - (int)((char*)message->payload)[message->payloadlen - sizeof(instant_t) - 5] == '\0' - // Is the magic string present? - && memcmp("LFts", &message->payload[message->payloadlen - sizeof(instant_t) - 4], 4) == 0 - ) { - my_info->count++; - - instant_t timestamp = extract_int64( - (unsigned char*)message->payload + message->payloadlen - sizeof(instant_t) - ); - instant_t physical_time = lf_time_physical(); - - interval_t latency = physical_time - timestamp; - my_info->latencies += latency; - - if (latency > my_info->max_latency) { - my_info->max_latency = latency; - } - if (my_info->use_physical_time) { - // Use physical time. - delay = physical_time + offset - current_time; - } else { - // Use logical time. - delay = timestamp + offset - current_time; - } - // Schedule the event. - // We rely on lf_schedule_copy to issue a warning if the delay is negative. - lf_schedule_copy( - ((MQTTSubscriber_info_t*)info)->logical_action, - delay, - (char*)message->payload, - message->payloadlen - sizeof(instant_t) - ); - } else { - if (!my_info->use_physical_time) { - // No timestamp included, so we can't use logical time! - lf_print_warning("MQTTSubscriber: Received message with no timestamp!"); - } - // Use physical time. - instant_t physical_time = lf_time_physical(); - delay = physical_time + offset - current_time; - - // Schedule the event. - // We rely on lf_schedule_copy to issue a warning if the delay is negative. - lf_schedule_copy( - ((MQTTSubscriber_info_t*)info)->logical_action, - delay, - (char*)message->payload, - message->payloadlen - ); - } - - LF_PRINT_LOG( - "MQTTSubscriber: Received message. Timestamp will be " PRINTF_TIME - " ahead of current (elapsed) time, " PRINTF_TIME, delay, current_time - lf_time_start() - ); - - lf_critical_section_exit(); - - // MQTTClient_freeMessage() also frees the memory allocated to the payload, - // which is why we have to copy the message here. - MQTTClient_freeMessage(&message); - MQTTClient_free(topic_name); - - // Return true to indicate that the message has been successfully handled. - return 1; - } + instant_t timestamp = extract_int64( + (unsigned char*)message->payload + message->payloadlen - sizeof(instant_t) + ); + instant_t physical_time = lf_time_physical(); - /** Callback invoked if the connection is lost. */ - void sub_connection_lost(void *info, char *cause) { - lf_print_warning("MQTTSubscriber: Connection lost. Cause: %s", cause); + interval_t latency = physical_time - timestamp; + my_info->latencies += latency; + + if (latency > my_info->max_latency) { + my_info->max_latency = latency; } - =} - - /** - * Output for sending the incoming MQTT message. - * Use type char* rather than string because it is not - * a static string, but rather dynamically allocated memory. - */ - output message:char*; + if (my_info->use_physical_time) { + // Use physical time. + delay = physical_time + offset - current_time; + } else { + // Use logical time. + delay = timestamp + offset - current_time; + } + // Schedule the event. + // We rely on lf_schedule_copy to issue a warning if the delay is negative. + lf_schedule_copy( + ((MQTTSubscriber_info_t*)info)->logical_action, + delay, + (char*)message->payload, + message->payloadlen - sizeof(instant_t) + ); + } else { + if (!my_info->use_physical_time) { + // No timestamp included, so we can't use logical time! + lf_print_warning("MQTTSubscriber: Received message with no timestamp!"); + } + // Use physical time. + instant_t physical_time = lf_time_physical(); + delay = physical_time + offset - current_time; - /** - * Action that is triggered when there is an incoming MQTT message. - * Use a logical action here so that the callback function can - * precisely control timestamp of the received message. - */ - logical action act:char*; + // Schedule the event. + // We rely on lf_schedule_copy to issue a warning if the delay is negative. + lf_schedule_copy( + ((MQTTSubscriber_info_t*)info)->logical_action, + delay, + (char*)message->payload, + message->payloadlen + ); + } + + LF_PRINT_LOG( + "MQTTSubscriber: Received message. Timestamp will be " PRINTF_TIME + " ahead of current (elapsed) time, " PRINTF_TIME, delay, current_time - lf_time_start() + ); + + lf_critical_section_exit(); + + // MQTTClient_freeMessage() also frees the memory allocated to the payload, + // which is why we have to copy the message here. + MQTTClient_freeMessage(&message); + MQTTClient_free(topic_name); + + // Return true to indicate that the message has been successfully handled. + return 1; + } - /** Client ID. This is automatically generated. */ - state clientID:char*({= NULL =}); + /** Callback invoked if the connection is lost. */ + void sub_connection_lost(void *info, char *cause) { + lf_print_warning("MQTTSubscriber: Connection lost. Cause: %s", cause); + } + =} + + /** + * Output for sending the incoming MQTT message. + * Use type char* rather than string because it is not + * a static string, but rather dynamically allocated memory. + */ + output message:char*; - /** State variable storing the MQTT client created for each instance of this reactor. */ - state client:MQTTClient({=NULL=}); - - /** Struct containing the action and offset. */ - state info:MQTTSubscriber_info_t({= {NULL, 0LL, false, 0LL, 0LL, 0} =}) + /** + * Action that is triggered when there is an incoming MQTT message. + * Use a logical action here so that the callback function can + * precisely control timestamp of the received message. + */ + logical action act:char*; + + /** Client ID. This is automatically generated. */ + state clientID:char*({= NULL =}); + + /** State variable storing the MQTT client created for each instance of this reactor. */ + state client:MQTTClient({=NULL=}); - reaction(startup) -> act {= - int rc; // response code. + /** Struct containing the action and offset. */ + state info:MQTTSubscriber_info_t({= {NULL, 0LL, false, 0LL, 0LL, 0} =}) + + reaction(startup) -> act {= + int rc; // response code. - if (asprintf(&self->clientID, "LF_MQTTSubscriber_%zu", _lf_MQTTSubscriber_count++) < 0) { - lf_print_error_and_exit("MQTTSubscriber: Failed to create client ID."); - } + if (asprintf(&self->clientID, "LF_MQTTSubscriber_%zu", _lf_MQTTSubscriber_count++) < 0) { + lf_print_error_and_exit("MQTTSubscriber: Failed to create client ID."); + } - // In case there are multiple instances of this or the subscriber, enter - // a critical section. The Paho MQTT functions are not thread safe. - lf_critical_section_enter(); + // In case there are multiple instances of this or the subscriber, enter + // a critical section. The Paho MQTT functions are not thread safe. + lf_critical_section_enter(); - rc = MQTTClient_create( - &self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL - ); - if (rc != MQTTCLIENT_SUCCESS) { - lf_print_error_and_exit("MQTTSubscriber: Failed to create MQTT client.\n" - "Return code: %d\n", rc); - } + rc = MQTTClient_create( + &self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL + ); + if (rc != MQTTCLIENT_SUCCESS) { + lf_print_error_and_exit("MQTTSubscriber: Failed to create MQTT client.\n" + "Return code: %d\n", rc); + } - sub_connect_options.keepAliveInterval = 20; - sub_connect_options.cleansession = 1; - - self->info.logical_action = act; - self->info.offset = self->offset; - self->info.use_physical_time = self->use_physical_time; - - // Set up callback functions. - // Last argument should be a pointer to a function to - // handle notification of delivery of a sent message. - // But this reactor isn't sending any messages. - MQTTClient_setCallbacks(self->client, &self->info, sub_connection_lost, message_arrived, NULL); - - // Connect to the broker. - rc = MQTTClient_connect(self->client, &sub_connect_options); - if (rc != MQTTCLIENT_SUCCESS) { - lf_print_error_and_exit( - "MQTTSubscriber: Failed to connect to MQTT broker.\n" - "Perhaps one is not running? Return code: %d\n", rc); - } - - MQTTClient_subscribe(self->client, self->topic, QOS); - - lf_critical_section_exit(); - =} + sub_connect_options.keepAliveInterval = 20; + sub_connect_options.cleansession = 1; - reaction(act) -> message {= - // The action contains a token that we can just forward. - // The allocated memory will be freed when the token's reference count hits 0. - // Note that this token will still contain the publisher's timestamp. - lf_set_token(message, act->token); - =} + self->info.logical_action = act; + self->info.offset = self->offset; + self->info.use_physical_time = self->use_physical_time; - reaction(shutdown) {= - if (self->info.count > 0) { - lf_print( - "MQTTSubscriber: Maximum apparent latency measured at receiver (in nsec): " PRINTF_TIME, - self->info.max_latency - ); - lf_print( - "MQTTSubscriber: Average apparent latency measured at receiver (in nsec): " PRINTF_TIME, - self->info.latencies/self->info.count - ); - } - LF_PRINT_LOG("MQTTSubscriber: Client ID %s disconnecting.", self->clientID); - if (self->clientID) free(self->clientID); - MQTTClient_disconnect(self->client, 10000); - MQTTClient_destroy(&self->client); - =} + // Set up callback functions. + // Last argument should be a pointer to a function to + // handle notification of delivery of a sent message. + // But this reactor isn't sending any messages. + MQTTClient_setCallbacks(self->client, &self->info, sub_connection_lost, message_arrived, NULL); + + // Connect to the broker. + rc = MQTTClient_connect(self->client, &sub_connect_options); + if (rc != MQTTCLIENT_SUCCESS) { + lf_print_error_and_exit( + "MQTTSubscriber: Failed to connect to MQTT broker.\n" + "Perhaps one is not running? Return code: %d\n", rc); + } + + MQTTClient_subscribe(self->client, self->topic, QOS); + + lf_critical_section_exit(); + =} + + reaction(act) -> message {= + // The action contains a token that we can just forward. + // The allocated memory will be freed when the token's reference count hits 0. + // Note that this token will still contain the publisher's timestamp. + lf_set_token(message, act->token); + =} + + reaction(shutdown) {= + if (self->info.count > 0) { + lf_print( + "MQTTSubscriber: Maximum apparent latency measured at receiver (in nsec): " PRINTF_TIME, + self->info.max_latency + ); + lf_print( + "MQTTSubscriber: Average apparent latency measured at receiver (in nsec): " PRINTF_TIME, + self->info.latencies/self->info.count + ); + } + LF_PRINT_LOG("MQTTSubscriber: Client ID %s disconnecting.", self->clientID); + if (self->clientID) free(self->clientID); + MQTTClient_disconnect(self->client, 10000); + MQTTClient_destroy(&self->client); + =} } diff --git a/C/src/mqtt/lib/MQTTTestReactors.lf b/C/src/mqtt/lib/MQTTTestReactors.lf index b3c4a56b..0aed0127 100644 --- a/C/src/mqtt/lib/MQTTTestReactors.lf +++ b/C/src/mqtt/lib/MQTTTestReactors.lf @@ -14,27 +14,27 @@ target C * @output message The message. */ reactor MessageGenerator(root:string(""), period:time(1 sec)) { - // Output type char* instead of string is used for dynamically - // allocated character arrays (as opposed to static constant strings). - output message:char*; - state count:int(1); - // Send first message after 1 sec so that the startup reactions - // do not factor into the transport time measurement on the first message. - timer t(1 sec, period); - reaction(t) -> message {= - // With NULL, 0 arguments, snprintf tells us how many bytes are needed. - // Add one for the null terminator. - int length = snprintf(NULL, 0, "%s %d", self->root, self->count) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(message, length); - // Populate the output string and increment the count. - snprintf(message->value, length, "%s %d", self->root, self->count++); - tag_t tag = lf_tag(); - lf_print("MessageGenerator: At (elapsed) tag " PRINTF_TAG ", publish message: %s", - tag.time - lf_time_start(), tag.microstep, - message->value - ); - =} + // Output type char* instead of string is used for dynamically + // allocated character arrays (as opposed to static constant strings). + output message:char*; + state count:int(1); + // Send first message after 1 sec so that the startup reactions + // do not factor into the transport time measurement on the first message. + timer t(1 sec, period); + reaction(t) -> message {= + // With NULL, 0 arguments, snprintf tells us how many bytes are needed. + // Add one for the null terminator. + int length = snprintf(NULL, 0, "%s %d", self->root, self->count) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(message, length); + // Populate the output string and increment the count. + snprintf(message->value, length, "%s %d", self->root, self->count++); + tag_t tag = lf_tag(); + lf_print("MessageGenerator: At (elapsed) tag " PRINTF_TAG ", publish message: %s", + tag.time - lf_time_start(), tag.microstep, + message->value + ); + =} } /** @@ -43,12 +43,12 @@ reactor MessageGenerator(root:string(""), period:time(1 sec)) { * @input message The message. */ reactor PrintMessage { - input message:char*; - reaction(message) {= - tag_t tag = lf_tag(); - lf_print("PrintMessage: At (elapsed) time " PRINTF_TAG ", subscriber receives: %s", - tag.time - lf_time_start(), tag.microstep, - message->value - ); - =} + input message:char*; + reaction(message) {= + tag_t tag = lf_tag(); + lf_print("PrintMessage: At (elapsed) time " PRINTF_TAG ", subscriber receives: %s", + tag.time - lf_time_start(), tag.microstep, + message->value + ); + =} } diff --git a/C/src/patterns/Chain_01_SendReceive.lf b/C/src/patterns/Chain_01_SendReceive.lf index 26ffbd31..6395ad08 100644 --- a/C/src/patterns/Chain_01_SendReceive.lf +++ b/C/src/patterns/Chain_01_SendReceive.lf @@ -9,7 +9,7 @@ target C; import SendOnce, Receive from "lib/SendersAndReceivers.lf"; main reactor { - r1 = new SendOnce(); - r2 = new Receive(); - r1.out -> r2.in; + r1 = new SendOnce(); + r2 = new Receive(); + r1.out -> r2.in; } diff --git a/C/src/patterns/Chain_02_Pipeline.lf b/C/src/patterns/Chain_02_Pipeline.lf index 8b9656ed..97bb3497 100644 --- a/C/src/patterns/Chain_02_Pipeline.lf +++ b/C/src/patterns/Chain_02_Pipeline.lf @@ -15,18 +15,18 @@ * @author Edward A. Lee */ target C { - workers: 4, - timeout: 1 sec + workers: 4, + timeout: 1 sec } import SendCount, Receive from "lib/SendersAndReceivers.lf"; import TakeTime from "lib/TakeTime.lf"; main reactor { - r0 = new SendCount(period = 100 msec); - rp = new[4] TakeTime(approximate_time = 100 msec); - r5 = new Receive(); - r0.out, rp.out -> rp.in, r5.in after 100 msec; + r0 = new SendCount(period = 100 msec); + rp = new[4] TakeTime(approximate_time = 100 msec); + r5 = new Receive(); + r0.out, rp.out -> rp.in, r5.in after 100 msec; } \ No newline at end of file diff --git a/C/src/patterns/FullyConnected_00_Broadcast.lf b/C/src/patterns/FullyConnected_00_Broadcast.lf index ba31fb63..3563bff2 100644 --- a/C/src/patterns/FullyConnected_00_Broadcast.lf +++ b/C/src/patterns/FullyConnected_00_Broadcast.lf @@ -9,33 +9,33 @@ target C; reactor Node( - num_nodes: size_t(4), - bank_index: int(0) + num_nodes: size_t(4), + bank_index: int(0) ) { - input[num_nodes] in: int; - output out: int; - - state received: bool(false); - - reaction (startup) -> out{= - lf_print("Hello from node %d!", self->bank_index); - // broadcast my ID to everyone - lf_set(out, self->bank_index); - =} - - reaction (in) {= - printf("Node %d received messages from ", self->bank_index); - for (int i = 0; i < in_width; i++) { - if (in[i]->is_present) { - self->received = true; - printf("%d, ", in[i]->value); - } - } - printf("\n"); - =} + input[num_nodes] in: int; + output out: int; + + state received: bool(false); + + reaction (startup) -> out{= + lf_print("Hello from node %d!", self->bank_index); + // broadcast my ID to everyone + lf_set(out, self->bank_index); + =} + + reaction (in) {= + printf("Node %d received messages from ", self->bank_index); + for (int i = 0; i < in_width; i++) { + if (in[i]->is_present) { + self->received = true; + printf("%d, ", in[i]->value); + } + } + printf("\n"); + =} } main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - (nodes.out)+ -> nodes.in; + nodes = new[num_nodes] Node(num_nodes=num_nodes); + (nodes.out)+ -> nodes.in; } diff --git a/C/src/patterns/FullyConnected_01_Addressable.lf b/C/src/patterns/FullyConnected_01_Addressable.lf index 3b6837cd..0c524e7d 100644 --- a/C/src/patterns/FullyConnected_01_Addressable.lf +++ b/C/src/patterns/FullyConnected_01_Addressable.lf @@ -13,30 +13,30 @@ target C; reactor Node( - num_nodes: size_t(4), - bank_index: int(0) + num_nodes: size_t(4), + bank_index: int(0) ) { - input[num_nodes] in: int; - output[num_nodes] out: int; - - reaction (startup) -> out{= - lf_print("Hello from node %d!", self->bank_index); - // broadcast my ID to everyone - lf_set(out[(self->bank_index + 1) % self->num_nodes], self->bank_index); - =} - - reaction (in) {= - for (int i = 0; i < in_width; i++) { - if (in[i]->is_present) { - lf_print("Node %d received %d on channel %d.", - self->bank_index, in[i]->value, i - ); - } - } - =} + input[num_nodes] in: int; + output[num_nodes] out: int; + + reaction (startup) -> out{= + lf_print("Hello from node %d!", self->bank_index); + // broadcast my ID to everyone + lf_set(out[(self->bank_index + 1) % self->num_nodes], self->bank_index); + =} + + reaction (in) {= + for (int i = 0; i < in_width; i++) { + if (in[i]->is_present) { + lf_print("Node %d received %d on channel %d.", + self->bank_index, in[i]->value, i + ); + } + } + =} } main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - nodes.out -> interleaved(nodes.in); + nodes = new[num_nodes] Node(num_nodes=num_nodes); + nodes.out -> interleaved(nodes.in); } diff --git a/C/src/patterns/Loop_01_Single.lf b/C/src/patterns/Loop_01_Single.lf index d685f877..27039160 100644 --- a/C/src/patterns/Loop_01_Single.lf +++ b/C/src/patterns/Loop_01_Single.lf @@ -9,12 +9,12 @@ * @author Edward A. Lee */ target C { - timeout: 5 sec + timeout: 5 sec }; import SendPeriodicallyAndReceive from "lib/SendersAndReceivers.lf"; main reactor { - r1 = new SendPeriodicallyAndReceive(); - r1.out -> r1.in; -} \ No newline at end of file + r1 = new SendPeriodicallyAndReceive(); + r1.out -> r1.in; +} diff --git a/C/src/patterns/Loop_02_SingleDelay.lf b/C/src/patterns/Loop_02_SingleDelay.lf index fd3088b1..2be5e599 100644 --- a/C/src/patterns/Loop_02_SingleDelay.lf +++ b/C/src/patterns/Loop_02_SingleDelay.lf @@ -11,14 +11,14 @@ * @author Edward A. Lee */ target C { - timeout: 5 sec + timeout: 5 sec }; import ReceiveAndSendPeriodically from "lib/SendersAndReceivers.lf"; main reactor ( - a_1_1:time(0) + a_1_1:time(0) ) { - r1 = new ReceiveAndSendPeriodically(); - r1.out -> r1.in after a_1_1; -} \ No newline at end of file + r1 = new ReceiveAndSendPeriodically(); + r1.out -> r1.in after a_1_1; +} diff --git a/C/src/patterns/lib/SendersAndReceivers.lf b/C/src/patterns/lib/SendersAndReceivers.lf index 249438f6..f36a7ea6 100644 --- a/C/src/patterns/lib/SendersAndReceivers.lf +++ b/C/src/patterns/lib/SendersAndReceivers.lf @@ -9,10 +9,10 @@ target C; * Send an output (42) at startup. */ reactor SendOnce { - output out:int; - reaction(startup) -> out {= - lf_set(out, 42); - =} + output out:int; + reaction(startup) -> out {= + lf_set(out, 42); + =} } /** @@ -24,18 +24,18 @@ reactor SendOnce { * @param increment The increment between outputs */ reactor SendCount( - offset:time(0), - period:time(1 sec), - start:int(0), - increment:int(1) + offset:time(0), + period:time(1 sec), + start:int(0), + increment:int(1) ) { - state count:int(start); - output out:int; - timer t(offset, period); - reaction(t) -> out {= - lf_set(out, self->count); - self->count += self->increment; - =} + state count:int(start); + output out:int; + timer t(offset, period); + reaction(t) -> out {= + lf_set(out, self->count); + self->count += self->increment; + =} } /** @@ -43,35 +43,35 @@ reactor SendCount( * and the value of the input. */ reactor Receive { - input in:int; - reaction(in) {= - lf_print("At elapsed tag (%lld, %d), received %d.", - lf_time_logical_elapsed(), lf_tag().microstep, - in->value - ); - =} + input in:int; + reaction(in) {= + lf_print("At elapsed tag (%lld, %d), received %d.", + lf_time_logical_elapsed(), lf_tag().microstep, + in->value + ); + =} } reactor ReceiveAndSend { - input in:int; - output out:int; - reaction(in) -> out {= - lf_set(out, in->value); - =} + input in:int; + output out:int; + reaction(in) -> out {= + lf_set(out, in->value); + =} } reactor SendOnceAndReceive { - input in:int; - output out:int; - reaction(startup) -> out {= - lf_set(out, 42); - =} - reaction(in) {= - lf_print("At tag (%lld, %d), received %d.", - lf_time_logical_elapsed(), - lf_tag().microstep, - in->value - ); - =} + input in:int; + output out:int; + reaction(startup) -> out {= + lf_set(out, 42); + =} + reaction(in) {= + lf_print("At tag (%lld, %d), received %d.", + lf_time_logical_elapsed(), + lf_tag().microstep, + in->value + ); + =} } /** @@ -89,7 +89,7 @@ reactor SendOnceAndReceive { * @label Increment the state, send, report received. */ reactor SendPeriodicallyAndReceive extends SendCount, Receive { - // This reactor simply composes SendCount and Receive, in that order. + // This reactor simply composes SendCount and Receive, in that order. } /** @@ -107,7 +107,7 @@ reactor SendPeriodicallyAndReceive extends SendCount, Receive { * @label Increment the state, send, report received. */ reactor ReceiveAndSendPeriodically extends Receive, SendCount { - // This reactor simply composes Receive and SendCount, in that order. + // This reactor simply composes Receive and SendCount, in that order. } /** @@ -116,31 +116,31 @@ reactor ReceiveAndSendPeriodically extends Receive, SendCount { * @label Increment the state, send, report received. */ reactor SendPeriodicallyAndReceiveMultiport ( - offset:time(0), - period:time(1 sec), - start:int(0), - increment:int(1), - width:int(4) + offset:time(0), + period:time(1 sec), + start:int(0), + increment:int(1), + width:int(4) ) { - input[width] in:int; - output out:int; - - timer t(offset, period); - - state count:int(start); - - reaction(t) -> out {= - lf_set(out, self->count); - self->count += self->increment; - =} - reaction(in) {= - lf_print("At tag (%lld, %d), received:", - lf_time_logical_elapsed(), lf_tag().microstep - ); - for (int i = 0; i < self->width; i++) { - lf_print(" On channel %d: %d", i, in[i]->value); - } - =} + input[width] in:int; + output out:int; + + timer t(offset, period); + + state count:int(start); + + reaction(t) -> out {= + lf_set(out, self->count); + self->count += self->increment; + =} + reaction(in) {= + lf_print("At tag (%lld, %d), received:", + lf_time_logical_elapsed(), lf_tag().microstep + ); + for (int i = 0; i < self->width; i++) { + lf_print(" On channel %d: %d", i, in[i]->value); + } + =} } /** @@ -149,57 +149,57 @@ reactor SendPeriodicallyAndReceiveMultiport ( * @label Increment the state, send increment, accept increment. */ reactor LocalRemoteUpdates( - offset:time(0), - period:time(1 sec), - increment:int(1) + offset:time(0), + period:time(1 sec), + increment:int(1) ) { - input in:int; - output out:int; - - timer t(offset, period); - - state count:int(0); - - reaction(t) -> out {= - lf_set(out, self->increment); - self->count += self->increment; - =} - reaction(in) {= - self->count += in->value; - lf_print("At tag (%lld, %d), count is %d", - lf_time_logical_elapsed(), lf_tag().microstep, - self->count - ); - =} + input in:int; + output out:int; + + timer t(offset, period); + + state count:int(0); + + reaction(t) -> out {= + lf_set(out, self->increment); + self->count += self->increment; + =} + reaction(in) {= + self->count += in->value; + lf_print("At tag (%lld, %d), count is %d", + lf_time_logical_elapsed(), lf_tag().microstep, + self->count + ); + =} } // @label Accumulate local/remote increments, locally query. reactor SendAndReceiveWithLocalQuery( - query_offset:time(0), - query_period:time(1 sec) + query_offset:time(0), + query_period:time(1 sec) ) extends LocalRemoteUpdates { - local_query = new SendPeriodicallyAndReceive( - offset = query_offset, - period = query_period - ); - reaction(local_query.out) -> local_query.in {= - lf_set(local_query.in, self->count); - =} + local_query = new SendPeriodicallyAndReceive( + offset = query_offset, + period = query_period + ); + reaction(local_query.out) -> local_query.in {= + lf_set(local_query.in, self->count); + =} } // @label Accumulate local/remote increments, delayed query. reactor SendAndReceiveWithDelayedQuery( - query_offset:time(0), - query_period:time(1 sec), - query_delay:time(10 msec) + query_offset:time(0), + query_period:time(1 sec), + query_delay:time(10 msec) ) extends LocalRemoteUpdates { - local_query = new SendPeriodicallyAndReceive( - offset = query_offset, - period = query_period - ); - logical action a(query_delay); - reaction(local_query.out) -> a {= - lf_schedule(a, 0); - =} - reaction(a) -> local_query.in {= - lf_set(local_query.in, self->count); - =} + local_query = new SendPeriodicallyAndReceive( + offset = query_offset, + period = query_period + ); + logical action a(query_delay); + reaction(local_query.out) -> a {= + lf_schedule(a, 0); + =} + reaction(a) -> local_query.in {= + lf_set(local_query.in, self->count); + =} } diff --git a/C/src/patterns/lib/TakeTime.lf b/C/src/patterns/lib/TakeTime.lf index b08a7b77..5c3f3440 100644 --- a/C/src/patterns/lib/TakeTime.lf +++ b/C/src/patterns/lib/TakeTime.lf @@ -16,29 +16,29 @@ target C; * @output out The computed Fibonacci number. */ reactor TakeTime( - approximate_time:time(100 msec) + approximate_time:time(100 msec) ) { - input in:int; - output out:int; - reaction(in) -> out {= - instant_t start_time = lf_time_physical(); - int f0 = 0; - int f1 = 1; - int count = 0; - while (lf_time_physical() < start_time + self->approximate_time) { - int next_term = f0 + f1; - if (next_term < 0LL) { - // Overflow has occurred. Start over. - count = 0; - f0 = 0; - f1 = 1; - } else { - f0 = f1; - f1 = next_term; - count++; - } - } - lf_set(out, f1); - lf_print("The %d-th Fibonacci number is %d.", count, f0); - =} -} \ No newline at end of file + input in:int; + output out:int; + reaction(in) -> out {= + instant_t start_time = lf_time_physical(); + int f0 = 0; + int f1 = 1; + int count = 0; + while (lf_time_physical() < start_time + self->approximate_time) { + int next_term = f0 + f1; + if (next_term < 0LL) { + // Overflow has occurred. Start over. + count = 0; + f0 = 0; + f1 = 1; + } else { + f0 = f1; + f1 = next_term; + count++; + } + } + lf_set(out, f1); + lf_print("The %d-th Fibonacci number is %d.", count, f0); + =} +} diff --git a/C/src/rhythm/PlayWaveform.lf b/C/src/rhythm/PlayWaveform.lf index a1dbd33d..57750094 100644 --- a/C/src/rhythm/PlayWaveform.lf +++ b/C/src/rhythm/PlayWaveform.lf @@ -28,89 +28,89 @@ * @author Edward A. Lee */ target C { - files: [ - "/lib/c/reactor-c/util/wave_file_reader.c", - "/lib/c/reactor-c/util/wave_file_reader.h", - "/lib/c/reactor-c/util/audio_loop_mac.c", - "/lib/c/reactor-c/util/audio_loop.h", - "/lib/c/reactor-c/util/audio_loop_linux.c", - ], - cmake-include: [ - "/lib/c/reactor-c/util/audio_loop.cmake", - "/lib/c/reactor-c/util/wave_file_reader.cmake" - ] + files: [ + "/lib/c/reactor-c/util/wave_file_reader.c", + "/lib/c/reactor-c/util/wave_file_reader.h", + "/lib/c/reactor-c/util/audio_loop_mac.c", + "/lib/c/reactor-c/util/audio_loop.h", + "/lib/c/reactor-c/util/audio_loop_linux.c", + ], + cmake-include: [ + "/lib/c/reactor-c/util/audio_loop.cmake", + "/lib/c/reactor-c/util/wave_file_reader.cmake" + ] } /** * Produce a note when a `note` input is received. */ reactor PlayWaveform ( - default_waveform_id:int(0) // Silent waveform + default_waveform_id:int(0) // Silent waveform ) { - preamble {= - #include // Defines strlen() - #include "audio_loop.h" - #include "wave_file_reader.h" - - // wav files giving the waveforms. - // These have to also be included in the files target directive. - #define NUM_WAVEFORMS 9 // Number of waveforms. - #define SOUNDS LF_PACKAGE_DIRECTORY LF_FILE_SEPARATOR "src" LF_FILE_SEPARATOR "rhythm" LF_FILE_SEPARATOR "sounds" LF_FILE_SEPARATOR - char* waveform_files[] = { - SOUNDS "Bass-Drum-1.wav", - SOUNDS "Hi-Bongo.wav", - SOUNDS "Claves.wav", - SOUNDS "High-Conga-1.wav", - SOUNDS "Cowbell-1.wav", - SOUNDS "Cuica-1.wav", - SOUNDS "Guiro.wav", - SOUNDS "Ensoniq-ESQ-1-Snare.wav", - SOUNDS "Floor-Tom-1.wav" - }; - - // The waveforms themselves. - lf_waveform_t* waveforms[NUM_WAVEFORMS + 1]; + preamble {= + #include // Defines strlen() + #include "audio_loop.h" + #include "wave_file_reader.h" + + // wav files giving the waveforms. + // These have to also be included in the files target directive. + #define NUM_WAVEFORMS 9 // Number of waveforms. + #define SOUNDS LF_PACKAGE_DIRECTORY LF_FILE_SEPARATOR "src" LF_FILE_SEPARATOR "rhythm" LF_FILE_SEPARATOR "sounds" LF_FILE_SEPARATOR + char* waveform_files[] = { + SOUNDS "Bass-Drum-1.wav", + SOUNDS "Hi-Bongo.wav", + SOUNDS "Claves.wav", + SOUNDS "High-Conga-1.wav", + SOUNDS "Cowbell-1.wav", + SOUNDS "Cuica-1.wav", + SOUNDS "Guiro.wav", + SOUNDS "Ensoniq-ESQ-1-Snare.wav", + SOUNDS "Floor-Tom-1.wav" + }; - lf_waveform_t empty_waveform = { 0 }; - =} + // The waveforms themselves. + lf_waveform_t* waveforms[NUM_WAVEFORMS + 1]; + + lf_waveform_t empty_waveform = { 0 }; + =} - input note:float; - input waveform:int; + input note:float; + input waveform:int; + + /** + * Index of the current waveform. + * -1 means no waveform (just make ticks)). + */ + state waveform_id:int(default_waveform_id); - /** - * Index of the current waveform. - * -1 means no waveform (just make ticks)). - */ - state waveform_id:int(default_waveform_id); - - reaction(startup) {= - - // First waveform is empty. - waveforms[0] = &empty_waveform; - - // Open and read waveform files. - for (int i = 0; i < NUM_WAVEFORMS; i++) { - waveforms[i + 1] = read_wave_file(waveform_files[i]); - } - - // Start an audio loop that will become ready to receive - // amplitude samples of audio data. - lf_start_audio_loop(lf_time_logical()); - =} + reaction(startup) {= - reaction(waveform) {= - self->waveform_id = waveform->value; - =} + // First waveform is empty. + waveforms[0] = &empty_waveform; - reaction(note) {= - if (self->waveform_id < 0 || self->waveform_id > NUM_WAVEFORMS) { - lf_play_audio_waveform(NULL, note->value, lf_time_logical()); - } else { - lf_play_audio_waveform(waveforms[self->waveform_id], note->value, lf_time_logical()); - } - =} + // Open and read waveform files. + for (int i = 0; i < NUM_WAVEFORMS; i++) { + waveforms[i + 1] = read_wave_file(waveform_files[i]); + } - reaction(shutdown) {= - lf_stop_audio_loop(); - =} + // Start an audio loop that will become ready to receive + // amplitude samples of audio data. + lf_start_audio_loop(lf_time_logical()); + =} + + reaction(waveform) {= + self->waveform_id = waveform->value; + =} + + reaction(note) {= + if (self->waveform_id < 0 || self->waveform_id > NUM_WAVEFORMS) { + lf_play_audio_waveform(NULL, note->value, lf_time_logical()); + } else { + lf_play_audio_waveform(waveforms[self->waveform_id], note->value, lf_time_logical()); + } + =} + + reaction(shutdown) {= + lf_stop_audio_loop(); + =} } diff --git a/C/src/rhythm/Rhythm.lf b/C/src/rhythm/Rhythm.lf index 18e9dc3e..23d12610 100644 --- a/C/src/rhythm/Rhythm.lf +++ b/C/src/rhythm/Rhythm.lf @@ -27,52 +27,52 @@ * @see RhythmDistributedNoUI.lf */ target C { - keepalive: true, - files: [ - "/lib/c/reactor-c/util/sensor_simulator.c", - "/lib/c/reactor-c/util/sensor_simulator.h", - ], - cmake-include: [ - "/lib/c/reactor-c/util/sensor_simulator.cmake", - ] + keepalive: true, + files: [ + "/lib/c/reactor-c/util/sensor_simulator.c", + "/lib/c/reactor-c/util/sensor_simulator.h", + ], + cmake-include: [ + "/lib/c/reactor-c/util/sensor_simulator.cmake", + ] } import PlayWaveform from "PlayWaveform.lf" preamble {= - #include - #include // Defines mkdir. - #include "sensor_simulator.h" + #include + #include // Defines mkdir. + #include "sensor_simulator.h" - ///////////////////////////// - // Configuration of the audio. - #ifndef DOWNBEAT - // Beat pattern with 1 note followed by 7 silences, repeated. - #define DOWNBEAT 0x0101 + ///////////////////////////// + // Configuration of the audio. + #ifndef DOWNBEAT + // Beat pattern with 1 note followed by 7 silences, repeated. + #define DOWNBEAT 0x0101 - // Merenque in binary covers two bars (in temporal order): 1001 1010 1010 1111 - // Reverse the order to get the bit sequence: 1111 0101 0101 1001 - #define MERENGUE 0xf559 - // Merengue emphasis in binary (in temporal order): 1001 0010 0010 1000 - // Reverse the order to get the bit sequence: 0001 0100 0100 1001 - #define MERENGUE_EMPHASIS 0x1449 + // Merenque in binary covers two bars (in temporal order): 1001 1010 1010 1111 + // Reverse the order to get the bit sequence: 1111 0101 0101 1001 + #define MERENGUE 0xf559 + // Merengue emphasis in binary (in temporal order): 1001 0010 0010 1000 + // Reverse the order to get the bit sequence: 0001 0100 0100 1001 + #define MERENGUE_EMPHASIS 0x1449 - // Bossa nova: In temporal order: 1001 0010 0010 0110 - // Note: last '1' is questionable. Held over from previous '1'. - // Reverse order: 0110 0100 0100 1001 - #define BOSSA_NOVA 0x6449 - #define BOSSA_NOVA_EMPHASIS 0x2449 + // Bossa nova: In temporal order: 1001 0010 0010 0110 + // Note: last '1' is questionable. Held over from previous '1'. + // Reverse order: 0110 0100 0100 1001 + #define BOSSA_NOVA 0x6449 + #define BOSSA_NOVA_EMPHASIS 0x2449 - // Samba: Temporal order: 0000 1010 1101 1010 - // Reversed: 0101 1011 0101 0000 - // Alt: 0101 1011 1011 1011 - // Rev: 1101 1101 1101 1010 - #define SAMBA 0xddda - #define SAMBA_EMPHASIS 0x99ca - #endif - - extern const char* instructions[]; - extern int instructions_length; + // Samba: Temporal order: 0000 1010 1101 1010 + // Reversed: 0101 1011 0101 0000 + // Alt: 0101 1011 1011 1011 + // Rev: 1101 1101 1101 1010 + #define SAMBA 0xddda + #define SAMBA_EMPHASIS 0x99ca + #endif + + extern const char* instructions[]; + extern int instructions_length; =} /** @@ -89,229 +89,229 @@ preamble {= * @param message_length The length of the message array. */ reactor RhythmSource( - sixteenth: time = 200 msec, - delta: time = 10 msec, - message: {= const char** =} = {= instructions =}, - message_length: int = {= instructions_length =}, - log_to_file: bool = false + sixteenth: time = 200 msec, + delta: time = 10 msec, + message: {= const char** =} = {= instructions =}, + message_length: int = {= instructions_length =}, + log_to_file: bool = false ) { - preamble {= - const char* instructions[] = { - "Basic control:", - " x: quit", - " +: speed up", - " -: slow down", - "Instrument:", - " 0: none", - " 1: bass drum", - " 2: bongo", - " 3: claves", - " 4: conga", - " 5: cowbell", - " 6: cuica", - " 7: guiro", - " 8: snare", - " 9: tom", - "Rhythm:", - " d: down beat", - " m: merengue", - " b: bossa nova", - " s: samba" - }; - int instructions_length = 20; - =} - // To change the rhythm. - input rhythm_change_in: char - // To change the tempo. - input tempo_change_in: interval_t + preamble {= + const char* instructions[] = { + "Basic control:", + " x: quit", + " +: speed up", + " -: slow down", + "Instrument:", + " 0: none", + " 1: bass drum", + " 2: bongo", + " 3: claves", + " 4: conga", + " 5: cowbell", + " 6: cuica", + " 7: guiro", + " 8: snare", + " 9: tom", + "Rhythm:", + " d: down beat", + " m: merengue", + " b: bossa nova", + " s: samba" + }; + int instructions_length = 20; + =} + // To change the rhythm. + input rhythm_change_in: char + // To change the tempo. + input tempo_change_in: interval_t - // To play a note with the given emphasis. - output note: float - // Instrument selection. - output instrument: int - // To change the rhythm. - output rhythm_change: char - // To change the tempo. - output tempo_change: interval_t + // To play a note with the given emphasis. + output note: float + // Instrument selection. + output instrument: int + // To change the rhythm. + output rhythm_change: char + // To change the tempo. + output tempo_change: interval_t - state tick_duration: time = 200 msec - logical action tick + state tick_duration: time = 200 msec + logical action tick - // Count of sixteenth notes. - state count: int = 0 + // Count of sixteenth notes. + state count: int = 0 - // Action to be invoked when a key is pressed. - physical action key: char + // Action to be invoked when a key is pressed. + physical action key: char - // Indicator of when to make a sound. - state rhythm: int = {= DOWNBEAT =} + // Indicator of when to make a sound. + state rhythm: int = {= DOWNBEAT =} - // Indicator of whether to emphasize the sound. - state emphasis: int = {= DOWNBEAT =} + // Indicator of whether to emphasize the sound. + state emphasis: int = {= DOWNBEAT =} - // Currently active rhythm. This becomes active from rhythm on the downbeat. - state active_rhythm: int = {= DOWNBEAT =} + // Currently active rhythm. This becomes active from rhythm on the downbeat. + state active_rhythm: int = {= DOWNBEAT =} - // Currently active emphasis. This becomes active from rhythm on the - // downbeat. - state active_emphasis: int = {= DOWNBEAT =} + // Currently active emphasis. This becomes active from rhythm on the + // downbeat. + state active_emphasis: int = {= DOWNBEAT =} - // Position of the cursor in the terminal window. - state cursor: int = 0 + // Position of the cursor in the terminal window. + state cursor: int = 0 - reaction(startup) -> key, note, tick {= - // Start the sensor simulator, which starts ncurses. - char* log_file_name = NULL; - if (self->log_to_file) { - log_file_name = calloc(40, sizeof(char)); - // FIXME: log directory won't work in Windows (wrong separator). - mkdir("log", 0755); - int fed_id = lf_fed_id(); - if (fed_id < 0) { - sprintf(log_file_name, "log/Rhythm_%lld.log", lf_time_logical()); - } else { - sprintf(log_file_name, "log/Rhythm_%d_%lld.log", fed_id, lf_time_logical()); - } - } - if (start_sensor_simulator( - self->message, self->message_length, 16, log_file_name, LOG_LEVEL_ALL - )) { - lf_print_error_and_exit("ERROR: Failed to start sensor simulator."); - } + reaction(startup) -> key, note, tick {= + // Start the sensor simulator, which starts ncurses. + char* log_file_name = NULL; + if (self->log_to_file) { + log_file_name = calloc(40, sizeof(char)); + // FIXME: log directory won't work in Windows (wrong separator). + mkdir("log", 0755); + int fed_id = lf_fed_id(); + if (fed_id < 0) { + sprintf(log_file_name, "log/Rhythm_%lld.log", lf_time_logical()); + } else { + sprintf(log_file_name, "log/Rhythm_%d_%lld.log", fed_id, lf_time_logical()); + } + } + if (start_sensor_simulator( + self->message, self->message_length, 16, log_file_name, LOG_LEVEL_ALL + )) { + lf_print_error_and_exit("ERROR: Failed to start sensor simulator."); + } - // Register action to trigger on key press. - register_sensor_key('\0', key); + // Register action to trigger on key press. + register_sensor_key('\0', key); - lf_schedule(tick, self->tick_duration); - =} + lf_schedule(tick, self->tick_duration); + =} - /** React to a key press. */ - reaction(key) -> instrument, rhythm_change, tempo_change {= - int numeric; - switch (key->value) { - case '0': - lf_set(instrument, 0); - break; - case 'd': - self->rhythm = DOWNBEAT; - self->emphasis = DOWNBEAT; - lf_set(rhythm_change, 'd'); - lf_print("Changing rhythm to downbeat only."); - break; - case 'm': - self->rhythm = MERENGUE; - self->emphasis = MERENGUE_EMPHASIS; - lf_set(rhythm_change, 'm'); - lf_print("Changing rhythm to merengue."); - break; - case 'b': - self->rhythm = BOSSA_NOVA; - self->emphasis = BOSSA_NOVA_EMPHASIS; - lf_set(rhythm_change, 'b'); - lf_print("Changing rhythm to bossa nova."); - break; - case 's': - self->rhythm = SAMBA; - self->emphasis = SAMBA_EMPHASIS; - lf_set(rhythm_change, 's'); - lf_print("Changing rhythm to samba."); - break; - case 'x': - lf_request_stop(); - break; - case '+': - self->tick_duration -= self->delta; - if (self->tick_duration < self->delta) { - self->tick_duration = self->delta; - } - lf_set(tempo_change, self->delta); - lf_print("Speeding up tempo."); - break; - case '-': - self->tick_duration += self->delta; - lf_set(tempo_change, -self->delta); - lf_print("Slowing down tempo."); - break; - default: - numeric = (int)key->value; - if (numeric >= 49 && numeric <= 57) { - // A digit between 1 and 9. - lf_set(instrument, numeric - 48); - lf_print("Changing instrument to %c.", numeric); - } + /** React to a key press. */ + reaction(key) -> instrument, rhythm_change, tempo_change {= + int numeric; + switch (key->value) { + case '0': + lf_set(instrument, 0); + break; + case 'd': + self->rhythm = DOWNBEAT; + self->emphasis = DOWNBEAT; + lf_set(rhythm_change, 'd'); + lf_print("Changing rhythm to downbeat only."); + break; + case 'm': + self->rhythm = MERENGUE; + self->emphasis = MERENGUE_EMPHASIS; + lf_set(rhythm_change, 'm'); + lf_print("Changing rhythm to merengue."); + break; + case 'b': + self->rhythm = BOSSA_NOVA; + self->emphasis = BOSSA_NOVA_EMPHASIS; + lf_set(rhythm_change, 'b'); + lf_print("Changing rhythm to bossa nova."); + break; + case 's': + self->rhythm = SAMBA; + self->emphasis = SAMBA_EMPHASIS; + lf_set(rhythm_change, 's'); + lf_print("Changing rhythm to samba."); + break; + case 'x': + lf_request_stop(); + break; + case '+': + self->tick_duration -= self->delta; + if (self->tick_duration < self->delta) { + self->tick_duration = self->delta; } - =} - - /** React to a remote rhythm change. */ - reaction(rhythm_change_in) {= - switch (rhythm_change_in->value) { - case 'm': - self->rhythm = MERENGUE; - self->emphasis = MERENGUE_EMPHASIS; - lf_print("REMOTE: Changing rhythm to merengue."); - break; - case 'b': - self->rhythm = BOSSA_NOVA; - self->emphasis = BOSSA_NOVA_EMPHASIS; - lf_print("REMOTE: Changing rhythm to bossa nova."); - break; - case 's': - self->rhythm = SAMBA; - self->emphasis = SAMBA_EMPHASIS; - lf_print("REMOTE: Changing rhythm to samba."); - break; - default: - self->rhythm = DOWNBEAT; - self->emphasis = DOWNBEAT; - lf_print("REMOTE: Changing rhythm to downbeat only."); - break; + lf_set(tempo_change, self->delta); + lf_print("Speeding up tempo."); + break; + case '-': + self->tick_duration += self->delta; + lf_set(tempo_change, -self->delta); + lf_print("Slowing down tempo."); + break; + default: + numeric = (int)key->value; + if (numeric >= 49 && numeric <= 57) { + // A digit between 1 and 9. + lf_set(instrument, numeric - 48); + lf_print("Changing instrument to %c.", numeric); } - =} + } + =} - reaction(tempo_change_in) {= - self->tick_duration -= tempo_change_in->value; - lf_print("REMOTE: Changing the tempo period by %lld ns.", - -tempo_change_in->value - ); - =} + /** React to a remote rhythm change. */ + reaction(rhythm_change_in) {= + switch (rhythm_change_in->value) { + case 'm': + self->rhythm = MERENGUE; + self->emphasis = MERENGUE_EMPHASIS; + lf_print("REMOTE: Changing rhythm to merengue."); + break; + case 'b': + self->rhythm = BOSSA_NOVA; + self->emphasis = BOSSA_NOVA_EMPHASIS; + lf_print("REMOTE: Changing rhythm to bossa nova."); + break; + case 's': + self->rhythm = SAMBA; + self->emphasis = SAMBA_EMPHASIS; + lf_print("REMOTE: Changing rhythm to samba."); + break; + default: + self->rhythm = DOWNBEAT; + self->emphasis = DOWNBEAT; + lf_print("REMOTE: Changing rhythm to downbeat only."); + break; + } + =} - reaction(tick) -> note, tick {= - int beeped = 0; - int position = 0; - position = 1 << self->count; - if (position & self->active_rhythm) { - double emphasis = 0.25; - if (position & self->active_emphasis) { - emphasis = 1.0; - } - lf_set(note, emphasis); - beeped++; - } - if (beeped > 0) { - if (position & self->active_emphasis) { - show_tick("!"); - } else { - show_tick("*"); - } - } else { - show_tick("."); - } - self->count++; - if (self->count == 16) { - self->active_rhythm = self->rhythm; - self->active_emphasis = self->emphasis; - self->count = 0; - } + reaction(tempo_change_in) {= + self->tick_duration -= tempo_change_in->value; + lf_print("REMOTE: Changing the tempo period by %lld ns.", + -tempo_change_in->value + ); + =} + + reaction(tick) -> note, tick {= + int beeped = 0; + int position = 0; + position = 1 << self->count; + if (position & self->active_rhythm) { + double emphasis = 0.25; + if (position & self->active_emphasis) { + emphasis = 1.0; + } + lf_set(note, emphasis); + beeped++; + } + if (beeped > 0) { + if (position & self->active_emphasis) { + show_tick("!"); + } else { + show_tick("*"); + } + } else { + show_tick("."); + } + self->count++; + if (self->count == 16) { + self->active_rhythm = self->rhythm; + self->active_emphasis = self->emphasis; + self->count = 0; + } - lf_schedule(tick, self->tick_duration); - =} + lf_schedule(tick, self->tick_duration); + =} - reaction(shutdown) {= end_sensor_simulator(); =} + reaction(shutdown) {= end_sensor_simulator(); =} } main reactor { - source = new RhythmSource() - play = new PlayWaveform() - source.note -> play.note - source.instrument -> play.waveform + source = new RhythmSource() + play = new PlayWaveform() + source.note -> play.note + source.instrument -> play.waveform } diff --git a/C/src/rhythm/RhythmDistributed.lf b/C/src/rhythm/RhythmDistributed.lf index d590d8a7..4c61adda 100644 --- a/C/src/rhythm/RhythmDistributed.lf +++ b/C/src/rhythm/RhythmDistributed.lf @@ -18,7 +18,7 @@ * Note that you have to have installed the RTI on your path. * See the instructions in the README file in this directory in the LF repo: * - * org.lflang/src/lib/core/federated/RTI + * org.lflang/src/lib/core/federated/RTI * * You can also map these three to distinct machines by specifying an `at` clause * on the lines at the end of this file that instantiate the reactors. @@ -35,25 +35,25 @@ import RhythmSource from "Rhythm.lf" import PlayWaveform from "PlayWaveform.lf" reactor Player { - input tempo_change_in:interval_t; // To accept a tempo change. - input rhythm_change_in:char; // To accept a rhythm change. - output tempo_change:interval_t; // To change the tempo. - output rhythm_change:char; // To change the rhythm. - source = new RhythmSource(); - play = new PlayWaveform(); - source.note -> play.note; - source.instrument -> play.waveform; - source.rhythm_change -> rhythm_change; - source.tempo_change -> tempo_change; - rhythm_change_in -> source.rhythm_change_in; - tempo_change_in -> source.tempo_change_in; + input tempo_change_in:interval_t; // To accept a tempo change. + input rhythm_change_in:char; // To accept a rhythm change. + output tempo_change:interval_t; // To change the tempo. + output rhythm_change:char; // To change the rhythm. + source = new RhythmSource(); + play = new PlayWaveform(); + source.note -> play.note; + source.instrument -> play.waveform; + source.rhythm_change -> rhythm_change; + source.tempo_change -> tempo_change; + rhythm_change_in -> source.rhythm_change_in; + tempo_change_in -> source.tempo_change_in; } federated reactor { - player1 = new Player(); - player2 = new Player(); - player1.rhythm_change -> player2.rhythm_change_in; - player1.tempo_change -> player2.tempo_change_in; - player2.rhythm_change -> player1.rhythm_change_in; - player2.tempo_change -> player1.tempo_change_in; -} \ No newline at end of file + player1 = new Player(); + player2 = new Player(); + player1.rhythm_change -> player2.rhythm_change_in; + player1.tempo_change -> player2.tempo_change_in; + player2.rhythm_change -> player1.rhythm_change_in; + player2.tempo_change -> player1.tempo_change_in; +} diff --git a/C/src/rhythm/RhythmDistributedNoUI.lf b/C/src/rhythm/RhythmDistributedNoUI.lf index 77101e3a..8852ee38 100644 --- a/C/src/rhythm/RhythmDistributedNoUI.lf +++ b/C/src/rhythm/RhythmDistributedNoUI.lf @@ -13,11 +13,11 @@ * To map these programs onto distinct machines, at the end * of this file, change the lines like this: * ``` - * player1 = new RhythmPlayer(); + * player1 = new RhythmPlayer(); * ``` * to something like * ``` - * player1 = new RhythmPlayer() at 10.0.0.42; + * player1 = new RhythmPlayer() at 10.0.0.42; * ``` * where the last part is the IP address or machine name of * a machine on which you want to run the particular component. @@ -29,40 +29,40 @@ * @author Edward A. Lee */ target C { - coordination: decentralized, - timeout: 10 sec, - clock-sync: on, - clock-sync-options: { - local-federates-on: true, - test-offset: 200 msec, - period: 5 msec, - trials: 10, - attenuation: 10 - } + coordination: decentralized, + timeout: 10 sec, + clock-sync: on, + clock-sync-options: { + local-federates-on: true, + test-offset: 200 msec, + period: 5 msec, + trials: 10, + attenuation: 10 + } }; import PlayWaveform from "PlayWaveform.lf" /** * Reactor that outputs a note request at the specified period. */ -reactor BeatSource(period:time(1600 msec)) { - output note:float; // To play a note with the given emphasis. +reactor BeatSource(period:time(1600 msec)) { + output note:float; // To play a note with the given emphasis. + + timer tick(0, period); - timer tick(0, period); - - reaction(tick) -> note {= - lf_set(note, 1.0f); - =} + reaction(tick) -> note {= + lf_set(note, 1.0f); + =} } reactor RhythmPlayer { - source = new BeatSource(); - play = new PlayWaveform(default_waveform_id = 3); - source.note -> play.note; + source = new BeatSource(); + play = new PlayWaveform(default_waveform_id = 3); + source.note -> play.note; } federated reactor { - player1 = new RhythmPlayer(); - player2 = new RhythmPlayer(); + player1 = new RhythmPlayer(); + player2 = new RhythmPlayer(); } diff --git a/C/src/rhythm/SensorSimulator.lf b/C/src/rhythm/SensorSimulator.lf index 33711f54..50ad7a42 100644 --- a/C/src/rhythm/SensorSimulator.lf +++ b/C/src/rhythm/SensorSimulator.lf @@ -3,37 +3,37 @@ * This has no audio output, but just tests the ncurses interface. */ target C { - keepalive: true, - files: [ - "/lib/c/reactor-c/util/sensor_simulator.c", - "/lib/c/reactor-c/util/sensor_simulator.h", - ], - cmake-include: [ - "/lib/c/reactor-c/util/sensor_simulator.cmake", - ] + keepalive: true, + files: [ + "/lib/c/reactor-c/util/sensor_simulator.c", + "/lib/c/reactor-c/util/sensor_simulator.h", + ], + cmake-include: [ + "/lib/c/reactor-c/util/sensor_simulator.cmake", + ] }; main reactor { - preamble {= - #include "sensor_simulator.h" - const char* messages[] = {"Hello", "World"}; - int num_messages = 2; - =} - timer t(0, 1 sec); - timer r(0, 2 sec); - physical action key:char*; - reaction(startup) -> key {= - lf_print("Starting sensor simulator."); - start_sensor_simulator(messages, num_messages, 16, NULL, LOG_LEVEL_INFO); - register_sensor_key('\0', key); + preamble {= + #include "sensor_simulator.h" + const char* messages[] = {"Hello", "World"}; + int num_messages = 2; + =} + timer t(0, 1 sec); + timer r(0, 2 sec); + physical action key:char*; + reaction(startup) -> key {= + lf_print("Starting sensor simulator."); + start_sensor_simulator(messages, num_messages, 16, NULL, LOG_LEVEL_INFO); + register_sensor_key('\0', key); =} - reaction(t) {= - show_tick("*"); - =} - reaction(r) {= - lf_print("Elapsed logical time: %lld.", lf_time_logical_elapsed()); - show_tick("."); - =} - reaction(key) {= - lf_print("You typed '%s' at elapsed time %lld.", key->value, lf_time_logical_elapsed()); - =} + reaction(t) {= + show_tick("*"); + =} + reaction(r) {= + lf_print("Elapsed logical time: %lld.", lf_time_logical_elapsed()); + show_tick("."); + =} + reaction(key) {= + lf_print("You typed '%s' at elapsed time %lld.", key->value, lf_time_logical_elapsed()); + =} } diff --git a/C/src/rhythm/Sound.lf b/C/src/rhythm/Sound.lf index fbdd6447..4cebb4de 100644 --- a/C/src/rhythm/Sound.lf +++ b/C/src/rhythm/Sound.lf @@ -12,20 +12,20 @@ * @see Rhythm.lf */ target C { - keepalive: true, + keepalive: true, } import PlayWaveform from "PlayWaveform.lf" preamble {= - #ifndef MERENGUE - // Merenque in binary covers two bars (in temporal order): 1001 1010 1010 1111 - // Reverse the order to get the bit sequence: 1111 0101 0101 1001 - #define MERENGUE 0xf559 - // Merengue emphasis in binary (in temporal order): 1001 0010 0010 1000 - // Reverse the order to get the bit sequence: 0001 0100 0100 1001 - #define MERENGUE_EMPHASIS 0x1449 - #endif + #ifndef MERENGUE + // Merenque in binary covers two bars (in temporal order): 1001 1010 1010 1111 + // Reverse the order to get the bit sequence: 1111 0101 0101 1001 + #define MERENGUE 0xf559 + // Merengue emphasis in binary (in temporal order): 1001 0010 0010 1000 + // Reverse the order to get the bit sequence: 0001 0100 0100 1001 + #define MERENGUE_EMPHASIS 0x1449 + #endif =} /** @@ -38,41 +38,41 @@ preamble {= * @param emphasis Binary specification of emphasis in reverse order. */ reactor RhythmSource( - sixteenth: time = 200 msec, - rhythm: int = {= MERENGUE =}, - emphasis: int = {= MERENGUE_EMPHASIS =} + sixteenth: time = 200 msec, + rhythm: int = {= MERENGUE =}, + emphasis: int = {= MERENGUE_EMPHASIS =} ) { - output note: float - - logical action tick + output note: float + + logical action tick - // Count of sixteenth notes. - state count: int = 0 + // Count of sixteenth notes. + state count: int = 0 - reaction(startup) -> note, tick {= - lf_schedule(tick, self->sixteenth); - =} + reaction(startup) -> note, tick {= + lf_schedule(tick, self->sixteenth); + =} - reaction(tick) -> note, tick {= - int position = 1 << self->count; - if (position & self->rhythm) { - double emphasis = 0.25; - if (position & self->emphasis) { - emphasis = 1.0; - } - lf_set(note, emphasis); - } - self->count++; - if (self->count == 16) { - self->count = 0; - } + reaction(tick) -> note, tick {= + int position = 1 << self->count; + if (position & self->rhythm) { + double emphasis = 0.25; + if (position & self->emphasis) { + emphasis = 1.0; + } + lf_set(note, emphasis); + } + self->count++; + if (self->count == 16) { + self->count = 0; + } - lf_schedule(tick, self->sixteenth); - =} + lf_schedule(tick, self->sixteenth); + =} } main reactor { - source = new RhythmSource() - play = new PlayWaveform(default_waveform_id = 1) - source.note -> play.note + source = new RhythmSource() + play = new PlayWaveform(default_waveform_id = 1) + source.note -> play.note } diff --git a/C/src/robot/CompositeRobot.lf b/C/src/robot/CompositeRobot.lf index 988fdd57..dd8a91c2 100644 --- a/C/src/robot/CompositeRobot.lf +++ b/C/src/robot/CompositeRobot.lf @@ -11,84 +11,84 @@ target C import PoissonClock from "../lib/PoissonClock.lf" reactor Handler { - input job:int - input pause:bool // true to pause, false to resume - input arm_status:bool // true for done, false for error - input agv_status:int // batter full/low, errors, etc. - input gripper_status:int - - output move_arm:int[3] // assuming vector command - output pause_arm:bool // true to pause, false to resume - output detect:bool // to request vision detection - output move_agv:int[3] // assuming vector command - output pause_agv:bool // true to pause, false to resume - output gripper_open:bool // true to open, false to close - output pause_gripper:bool - - initial mode IDLE { - reaction(job) -> HANDLING {= - lf_print("Received job at %lld ns", lf_time_logical_elapsed()); - lf_set_mode(HANDLING); - =} - } - mode HANDLING { - reaction(arm_status, agv_status, gripper_status) -> IDLE {= - lf_set_mode(IDLE); - =} - reaction(job) {= - lf_print("Job rejected at %lld ns. Busy.", lf_time_logical_elapsed()); - =} - } + input job:int + input pause:bool // true to pause, false to resume + input arm_status:bool // true for done, false for error + input agv_status:int // batter full/low, errors, etc. + input gripper_status:int + + output move_arm:int[3] // assuming vector command + output pause_arm:bool // true to pause, false to resume + output detect:bool // to request vision detection + output move_agv:int[3] // assuming vector command + output pause_agv:bool // true to pause, false to resume + output gripper_open:bool // true to open, false to close + output pause_gripper:bool + + initial mode IDLE { + reaction(job) -> HANDLING {= + lf_print("Received job at %lld ns", lf_time_logical_elapsed()); + lf_set_mode(HANDLING); + =} + } + mode HANDLING { + reaction(arm_status, agv_status, gripper_status) -> IDLE {= + lf_set_mode(IDLE); + =} + reaction(job) {= + lf_print("Job rejected at %lld ns. Busy.", lf_time_logical_elapsed()); + =} + } } reactor Jobs { - output job:int - state job_count:int(0) - - p = new PoissonClock() - reaction(p.event) -> job {= - lf_set(job, self->job_count++); - =} + output job:int + state job_count:int(0) + + p = new PoissonClock() + reaction(p.event) -> job {= + lf_set(job, self->job_count++); + =} } reactor ManualControl { - output pause:bool // true to pause, false to resume + output pause:bool // true to pause, false to resume } reactor Arm { - input move:int[3] - input[2] pause:bool - output status:bool // true for done, false for error + input move:int[3] + input[2] pause:bool + output status:bool // true for done, false for error } reactor Vision { - input detect:bool + input detect:bool } reactor AGV { - input move:int[3] - input[2] pause:bool - output status:int + input move:int[3] + input[2] pause:bool + output status:int } reactor Gripper { - input open:bool // true for open, false for close - input[2] pause:bool - output status:int + input open:bool // true for open, false for close + input[2] pause:bool + output status:int } main reactor { - h = new Handler() - j = new Jobs() - m = new ManualControl() - a = new Arm() - i = new Vision() - v = new AGV() - g = new Gripper() - - j.job -> h.job - m.pause -> h.pause - m.pause, h.pause_arm -> a.pause - m.pause, h.pause_agv -> v.pause - m.pause, h.pause_gripper -> g.pause - h.move_arm -> a.move - a.status -> h.arm_status - h.detect -> i.detect - h.move_agv -> v.move - v.status -> h.agv_status - h.gripper_open -> g.open - g.status -> h.gripper_status -} \ No newline at end of file + h = new Handler() + j = new Jobs() + m = new ManualControl() + a = new Arm() + i = new Vision() + v = new AGV() + g = new Gripper() + + j.job -> h.job + m.pause -> h.pause + m.pause, h.pause_arm -> a.pause + m.pause, h.pause_agv -> v.pause + m.pause, h.pause_gripper -> g.pause + h.move_arm -> a.move + a.status -> h.arm_status + h.detect -> i.detect + h.move_agv -> v.move + v.status -> h.agv_status + h.gripper_open -> g.open + g.status -> h.gripper_status +} diff --git a/C/src/rosace/AircraftSimulator.lf b/C/src/rosace/AircraftSimulator.lf index b1dd462d..35ad161a 100644 --- a/C/src/rosace/AircraftSimulator.lf +++ b/C/src/rosace/AircraftSimulator.lf @@ -30,190 +30,190 @@ */ target C preamble {= - #include - // Trimming parameters - // These may be overridden in the top-level .lf file. - #ifndef Va_eq - #define Va_eq (230.0) // Nominal airspeed? - #define h_eq (10000.0) + #include + // Trimming parameters + // These may be overridden in the top-level .lf file. + #ifndef Va_eq + #define Va_eq (230.0) // Nominal airspeed? + #define h_eq (10000.0) - #define delta_th_eq (1.5868660794926) - #define delta_e_eq (0.012009615652468) - #endif + #define delta_th_eq (1.5868660794926) + #define delta_e_eq (0.012009615652468) + #endif =} reactor AircraftDynamics( - period: time(5 ms), - // Trimming parameters - theta_eq: double(0.026485847681737), // Initial angle - - // Atmosphere parameters - rho0: double(1.225), - g0: double(9.80665), // Acceleration of gravity in m/s - T0_0: double(288.15), - T0_h: double(-0.0065), - Rs: double(287.05), - - // Aircraft parameters - masse: double(57837.5), - I_y: double(3781272.0), - S: double(122.6), - cbar: double(4.29), - CD_0: double(0.016), - CD_alpha: double(2.5), - CD_deltae: double(0.05), - CL_alpha: double(5.5), - CL_deltae: double(0.193), - alpha_0: double(-0.05), - Cm_0: double(0.04), - Cm_alpha: double(-0.83), - Cm_deltae: double(-1.5), - Cm_q: double(-30) + period: time(5 ms), + // Trimming parameters + theta_eq: double(0.026485847681737), // Initial angle + + // Atmosphere parameters + rho0: double(1.225), + g0: double(9.80665), // Acceleration of gravity in m/s + T0_0: double(288.15), + T0_h: double(-0.0065), + Rs: double(287.05), + + // Aircraft parameters + masse: double(57837.5), + I_y: double(3781272.0), + S: double(122.6), + cbar: double(4.29), + CD_0: double(0.016), + CD_alpha: double(2.5), + CD_deltae: double(0.05), + CL_alpha: double(5.5), + CL_deltae: double(0.193), + alpha_0: double(-0.05), + Cm_0: double(0.04), + Cm_alpha: double(-0.83), + Cm_deltae: double(-1.5), + Cm_q: double(-30) ) { - timer t(0, period) // 200 Hz Forward Euler simulation rate - - input T: double // Thrust - input delta_e: double // Elevator control - - output Vz:double // Vertical speed - output Va:double // True airspeed - output h:double // Altitude - output az:double // Vertical acceleration - output q:double // Pitch rate - - state u: double(0.0) - state w: double(0.0) - state q: double(0.0) - state theta: double(0.0) // Angle (0.0 is horizontal) - state h: double(0.0) - - reaction(startup) {= - self->u = Va_eq * cos(self->theta_eq); // Horizontal speed. - self->w = Va_eq * sin(self->theta_eq); // Vertical speed. - self->q = 0.0; - self->theta = self->theta_eq; - self->h = h_eq; - =} - - reaction(t) delta_e, T -> Vz, Va, h, az, q {= - const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) - - double u_dot, w_dot, q_dot, theta_dot, h_dot; - double CD, CL, Cm; - double Xa, Za, Ma; - double alpha, qbar, V, rho; - - rho = self->rho0 * pow(1.0 + self->T0_h / self->T0_0 * self->h,- self->g0 / (self->Rs * self->T0_h) - 1.0); - alpha = atan(self->w / self->u); - V = sqrt(self->u * self->u + self->w * self->w); - qbar = 0.5 * rho * V * V; - CL = self->CL_deltae * delta_e->value + self->CL_alpha * (alpha - self->alpha_0); - CD = self->CD_0 + self->CD_deltae * delta_e->value + self->CD_alpha * (alpha - self->alpha_0) * (alpha - self->alpha_0); - Cm = self->Cm_0 + self->Cm_deltae * delta_e->value + self->Cm_alpha * alpha + 0.5 * self->Cm_q * self->q * self->cbar / V; - Xa = - qbar * self->S * (CD * cos(alpha) - CL * sin(alpha)); - Za = - qbar * self->S * (CD * sin(alpha) + CL * cos(alpha)); - Ma = qbar * self->cbar * self->S * Cm; - - // Output - lf_set(Va, V); - lf_set(Vz, self->w * cos(self->theta) - self->u * sin(self->theta)); - lf_set(q, self->q); - lf_set(az, self->g0 * cos(self->theta) + Za / self->masse); - lf_set(h, self->h); - - // State Equation - u_dot = - self->g0 * sin(self->theta) - self->q * self->w + (Xa + T->value) / self->masse; - w_dot = self->g0 * cos(self->theta) + self->q * self->u + Za / self->masse; - q_dot = Ma / self->I_y; - theta_dot = self->q; - h_dot = self->u * sin(self->theta) - self->w * cos(self->theta); + timer t(0, period) // 200 Hz Forward Euler simulation rate + + input T: double // Thrust + input delta_e: double // Elevator control + + output Vz:double // Vertical speed + output Va:double // True airspeed + output h:double // Altitude + output az:double // Vertical acceleration + output q:double // Pitch rate + + state u: double(0.0) + state w: double(0.0) + state q: double(0.0) + state theta: double(0.0) // Angle (0.0 is horizontal) + state h: double(0.0) + + reaction(startup) {= + self->u = Va_eq * cos(self->theta_eq); // Horizontal speed. + self->w = Va_eq * sin(self->theta_eq); // Vertical speed. + self->q = 0.0; + self->theta = self->theta_eq; + self->h = h_eq; + =} + + reaction(t) delta_e, T -> Vz, Va, h, az, q {= + const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) - // Update State - self->u += dt * u_dot; - self->w += dt * w_dot; - self->q += dt * q_dot; - self->theta += dt * theta_dot; - self->h += dt * h_dot; - =} + double u_dot, w_dot, q_dot, theta_dot, h_dot; + double CD, CL, Cm; + double Xa, Za, Ma; + double alpha, qbar, V, rho; + + rho = self->rho0 * pow(1.0 + self->T0_h / self->T0_0 * self->h,- self->g0 / (self->Rs * self->T0_h) - 1.0); + alpha = atan(self->w / self->u); + V = sqrt(self->u * self->u + self->w * self->w); + qbar = 0.5 * rho * V * V; + CL = self->CL_deltae * delta_e->value + self->CL_alpha * (alpha - self->alpha_0); + CD = self->CD_0 + self->CD_deltae * delta_e->value + self->CD_alpha * (alpha - self->alpha_0) * (alpha - self->alpha_0); + Cm = self->Cm_0 + self->Cm_deltae * delta_e->value + self->Cm_alpha * alpha + 0.5 * self->Cm_q * self->q * self->cbar / V; + Xa = - qbar * self->S * (CD * cos(alpha) - CL * sin(alpha)); + Za = - qbar * self->S * (CD * sin(alpha) + CL * cos(alpha)); + Ma = qbar * self->cbar * self->S * Cm; + + // Output + lf_set(Va, V); + lf_set(Vz, self->w * cos(self->theta) - self->u * sin(self->theta)); + lf_set(q, self->q); + lf_set(az, self->g0 * cos(self->theta) + Za / self->masse); + lf_set(h, self->h); + + // State Equation + u_dot = - self->g0 * sin(self->theta) - self->q * self->w + (Xa + T->value) / self->masse; + w_dot = self->g0 * cos(self->theta) + self->q * self->u + Za / self->masse; + q_dot = Ma / self->I_y; + theta_dot = self->q; + h_dot = self->u * sin(self->theta) - self->w * cos(self->theta); + + // Update State + self->u += dt * u_dot; + self->w += dt * w_dot; + self->q += dt * q_dot; + self->theta += dt * theta_dot; + self->h += dt * h_dot; + =} } reactor Engine( - period: time(5 ms), - scale: double(26350.0), - tau: double(0.75) + period: time(5 ms), + scale: double(26350.0), + tau: double(0.75) ) { - input delta_thc: double // Engine control - output T: double // Thrust - - timer t(0, period) // 200 Hz Forward Euler simulation rate + input delta_thc: double // Engine control + output T: double // Thrust + + timer t(0, period) // 200 Hz Forward Euler simulation rate - state x1: double({= delta_th_eq =}) - - reaction(t) -> T {= - lf_set(T, self->scale * self->x1); - =} - - reaction(t) delta_thc {= - const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) + state x1: double({= delta_th_eq =}) + + reaction(t) -> T {= + lf_set(T, self->scale * self->x1); + =} + + reaction(t) delta_thc {= + const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) - // State Equation - double x1_dot = - self->tau * self->x1 + self->tau * delta_thc->value; - // Update State - self->x1 += dt * x1_dot; - =} + // State Equation + double x1_dot = - self->tau * self->x1 + self->tau * delta_thc->value; + // Update State + self->x1 += dt * x1_dot; + =} } reactor Elevator( - period: time(5 ms), - omega: double(25.0), - xi: double(0.85) + period: time(5 ms), + omega: double(25.0), + xi: double(0.85) ) { - input delta_ec: double // Elevator control - output delta_e: double - - timer t(0, period) // 200 Hz Forward Euler simulation rate + input delta_ec: double // Elevator control + output delta_e: double + + timer t(0, period) // 200 Hz Forward Euler simulation rate - state x1: double({= delta_e_eq =}) - state x2: double(0.0) - - reaction(t) -> delta_e {= - lf_set(delta_e, self->x1); - =} - - reaction(t) delta_ec {= - const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) + state x1: double({= delta_e_eq =}) + state x2: double(0.0) + + reaction(t) -> delta_e {= + lf_set(delta_e, self->x1); + =} + + reaction(t) delta_ec {= + const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) + + // State Equation + double x1_dot = self->x2; + double x2_dot = -self->omega * self->omega * self->x1 + - 2.0 * self->xi * self->omega * self->x2 + + self->omega * self->omega * delta_ec->value; - // State Equation - double x1_dot = self->x2; - double x2_dot = -self->omega * self->omega * self->x1 - - 2.0 * self->xi * self->omega * self->x2 - + self->omega * self->omega * delta_ec->value; - - // Update State - self->x1 += dt * x1_dot; - self->x2 += dt * x2_dot; - =} + // Update State + self->x1 += dt * x1_dot; + self->x2 += dt * x2_dot; + =} } reactor Aircraft( - period: time(5 ms), - theta_eq: double(0.026485847681737) // Initial angle + period: time(5 ms), + theta_eq: double(0.026485847681737) // Initial angle ) { - input delta_thc: double // Engine control - input delta_ec: double // Elevator control - - output Vz:double // Vertical speed - output Va:double // True airspeed - output h:double // Altitude - output az:double // Vertical acceleration - output q:double // Pitch rate - - a = new AircraftDynamics(period = period, theta_eq = theta_eq) - e = new Engine(period = period) - el = new Elevator(period = period) - - a.Vz, a.Va, a.h, a.az, a.q -> Vz, Va, h, az, q - delta_thc -> e.delta_thc - e.T -> a.T - delta_ec -> el.delta_ec - el.delta_e -> a.delta_e + input delta_thc: double // Engine control + input delta_ec: double // Elevator control + + output Vz:double // Vertical speed + output Va:double // True airspeed + output h:double // Altitude + output az:double // Vertical acceleration + output q:double // Pitch rate + + a = new AircraftDynamics(period = period, theta_eq = theta_eq) + e = new Engine(period = period) + el = new Elevator(period = period) + + a.Vz, a.Va, a.h, a.az, a.q -> Vz, Va, h, az, q + delta_thc -> e.delta_thc + e.T -> a.T + delta_ec -> el.delta_ec + el.delta_e -> a.delta_e } diff --git a/C/src/rosace/Rosace.lf b/C/src/rosace/Rosace.lf index b157780c..83ddd7f7 100644 --- a/C/src/rosace/Rosace.lf +++ b/C/src/rosace/Rosace.lf @@ -7,7 +7,7 @@ * * This implementation is based on code from: * - * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. + * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. * * Since that original code bears an LGPL license, so does this program: * @@ -50,9 +50,9 @@ */ target C { - fast: true, - build: "./build_run_plot.sh Rosace", - timeout: 10 min + fast: true, + build: "./build_run_plot.sh Rosace", + timeout: 10 min } import Aircraft from "AircraftSimulator.lf" @@ -60,13 +60,13 @@ import RosaceController from "RosaceController.lf" import PrintToFile from "../lib/PrintToFile.lf" preamble {= - // Shared constants. - // Trimming parameters - #define Va_eq (230.0) // Nominal airspeed? - #define h_eq (10000.0) + // Shared constants. + // Trimming parameters + #define Va_eq (230.0) // Nominal airspeed? + #define h_eq (10000.0) - #define delta_th_eq (1.5868660794926) - #define delta_e_eq (0.012009615652468) + #define delta_th_eq (1.5868660794926) + #define delta_e_eq (0.012009615652468) =} // Documentation @@ -74,34 +74,34 @@ preamble {= reactor Variables {} reactor Command( - period: time(100 ms), - value: double(0.0) + period: time(100 ms), + value: double(0.0) ) { - timer t(0, period) - output c:double - reaction(t) -> c {= - lf_set(c, self->value); - =} + timer t(0, period) + output c:double + reaction(t) -> c {= + lf_set(c, self->value); + =} } main reactor(filter_period: time(10 ms)) { - variables = new Variables() - a = new Aircraft() - c = new RosaceController(filter_period = filter_period) - altitude = new Command(value = 11000) // Altitude command - speed = new Command(value = 0.0) // Delta airspeed from nominal Va_eq (230) + variables = new Variables() + a = new Aircraft() + c = new RosaceController(filter_period = filter_period) + altitude = new Command(value = 11000) // Altitude command + speed = new Command(value = 0.0) // Delta airspeed from nominal Va_eq (230) + + p_h = new PrintToFile(filename = "altitude.data") + p_Va = new PrintToFile(filename = "airspeed.data") - p_h = new PrintToFile(filename = "altitude.data") - p_Va = new PrintToFile(filename = "airspeed.data") - - a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va - altitude.c -> c.c - speed.c -> c.s - - c.delta_ec -> a.delta_ec - c.delta_thc -> a.delta_thc - - // Print connections. - a.h -> p_h.y - a.Va -> p_Va.y + a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va + altitude.c -> c.c + speed.c -> c.s + + c.delta_ec -> a.delta_ec + c.delta_thc -> a.delta_thc + + // Print connections. + a.h -> p_h.y + a.Va -> p_Va.y } diff --git a/C/src/rosace/RosaceController.lf b/C/src/rosace/RosaceController.lf index ad20f0dd..c0262e86 100644 --- a/C/src/rosace/RosaceController.lf +++ b/C/src/rosace/RosaceController.lf @@ -35,239 +35,239 @@ target C preamble {= - // Trimming parameters - // These may be overridden in the top-level .lf file. - #ifndef Va_eq - #define Va_eq (230.0) // Nominal airspeed? - #define h_eq (10000.0) + // Trimming parameters + // These may be overridden in the top-level .lf file. + #ifndef Va_eq + #define Va_eq (230.0) // Nominal airspeed? + #define h_eq (10000.0) - #define delta_th_eq (1.5868660794926) - #define delta_e_eq (0.012009615652468) - #endif + #define delta_th_eq (1.5868660794926) + #define delta_e_eq (0.012009615652468) + #endif =} reactor Filter( - period: time(10 ms), - - a:double[](0.0, 0.0), - b:double[](0.0, 0.0), - - init_x1:double(0.0), - init_x2:double(0.0) + period: time(10 ms), + + a:double[](0.0, 0.0), + b:double[](0.0, 0.0), + + init_x1:double(0.0), + init_x2:double(0.0) ) { - input x:double - output y:double - - state x1: double(0.0) - state x2: double(0.0) + input x:double + output y:double + + state x1: double(0.0) + state x2: double(0.0) - timer t(0, period) - - reaction(startup) {= - self->x1 = self->init_x1; - self->x2 = self->init_x2; - =} - - reaction(t) -> y {= - lf_set(y, self->x2); - =} - - reaction(t) x {= - double x1_tmp = - self->a[0] * self->x2 + self->b[0] * x->value; - double x2_tmp = self->x1 - self->a[1] * self->x2 + self->b[1] * x->value; - // Update - self->x1 = x1_tmp; - self->x2 = x2_tmp; - =} + timer t(0, period) + + reaction(startup) {= + self->x1 = self->init_x1; + self->x2 = self->init_x2; + =} + + reaction(t) -> y {= + lf_set(y, self->x2); + =} + + reaction(t) x {= + double x1_tmp = - self->a[0] * self->x2 + self->b[0] * x->value; + double x2_tmp = self->x1 - self->a[1] * self->x2 + self->b[1] * x->value; + // Update + self->x1 = x1_tmp; + self->x2 = x2_tmp; + =} } reactor Command( - period: time(100 ms), - value: double(0.0) + period: time(100 ms), + value: double(0.0) ) { - timer t(0, period) - output c:double - reaction(t) -> c {= - lf_set(c, self->value); - =} + timer t(0, period) + output c:double + reaction(t) -> c {= + lf_set(c, self->value); + =} } reactor Hold( - period:time(20 ms), - - Kp_h: double(0.1014048), - Ki_h: double(0.0048288), - h_switch: double(50.0), + period:time(20 ms), + + Kp_h: double(0.1014048), + Ki_h: double(0.0048288), + h_switch: double(50.0), - Vz_c: double(-2.5), - Va_c: double(0.0), - h_c: double(11000) + Vz_c: double(-2.5), + Va_c: double(0.0), + h_c: double(11000) ) { - timer t(0, period) - - input s: double // Set point - input x: double // Measurement - - output c: double // Command + timer t(0, period) + + input s: double // Set point + input x: double // Measurement + + output c: double // Command - state integrator: double(532.2730285) - - reaction(t) s, x -> c {= - double y = 0.0; + state integrator: double(532.2730285) + + reaction(t) s, x -> c {= + double y = 0.0; - double Ts_h = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) - - if ((x->value - s->value) < -self->h_switch) { - // Output - y = self->Vz_c; - } else if ((x->value - s->value) > self->h_switch) { - // Output - y = -self->Vz_c; - } else { - // Output - y = self->Kp_h * (x->value - s->value) + self->Ki_h * self->integrator; - // State - self->integrator += Ts_h * (x->value - s->value); - } - - lf_set(c, y); - =} + double Ts_h = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) + + if ((x->value - s->value) < -self->h_switch) { + // Output + y = self->Vz_c; + } else if ((x->value - s->value) > self->h_switch) { + // Output + y = -self->Vz_c; + } else { + // Output + y = self->Kp_h * (x->value - s->value) + self->Ki_h * self->integrator; + // State + self->integrator += Ts_h * (x->value - s->value); + } + + lf_set(c, y); + =} } reactor VzControl( - period:time(20 ms), - - K2_intVz: double(0.000627342822264), - K2_Vz: double(-0.003252836726554), - K2_q: double(0.376071446897134), - K2_az: double(-0.001566907423747) + period:time(20 ms), + + K2_intVz: double(0.000627342822264), + K2_Vz: double(-0.003252836726554), + K2_q: double(0.376071446897134), + K2_az: double(-0.001566907423747) ) { - timer t(0, period) - - input Vzc: double - input azf: double - input Vzf: double - input qf: double - - output delta_ec: double - - state integrator: double(0.0) + timer t(0, period) + + input Vzc: double + input azf: double + input Vzf: double + input qf: double + + output delta_ec: double + + state integrator: double(0.0) - reaction(t) Vzc, azf, Vzf, qf -> delta_ec {= - - double Ts_K2 = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) - - // Output - double y = self->K2_intVz * self->integrator - + self->K2_Vz * Vzf->value - + self->K2_q * qf->value - + self->K2_az * azf->value + delta_e_eq; - // State - self->integrator += Ts_K2 * (Vzc->value - Vzf->value); - - lf_set(delta_ec, y); - =} + reaction(t) Vzc, azf, Vzf, qf -> delta_ec {= + + double Ts_K2 = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) + + // Output + double y = self->K2_intVz * self->integrator + + self->K2_Vz * Vzf->value + + self->K2_q * qf->value + + self->K2_az * azf->value + delta_e_eq; + // State + self->integrator += Ts_K2 * (Vzc->value - Vzf->value); + + lf_set(delta_ec, y); + =} } reactor VaControl( - period: time(20 ms), - K1_intVa: double(0.049802610664357), - K1_Va: double(-0.486813084356079), - K1_Vz: double(-0.077603095495388), - K1_q: double(21.692383376322041) + period: time(20 ms), + K1_intVa: double(0.049802610664357), + K1_Va: double(-0.486813084356079), + K1_Vz: double(-0.077603095495388), + K1_q: double(21.692383376322041) ) { - timer t(0, period) - - input Vzf: double - input Vaf: double - input Vac: double - input qf: double - - output delta_thc: double - - state integrator: double(0.0) - - reaction(t) Vzf, Vaf, Vac, qf -> delta_thc {= - double Ts_K1 = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) - - // Output - double y = self->K1_intVa * self->integrator - + self->K1_Va * (Vaf->value - Va_eq) - + self->K1_Vz * Vzf->value + self->K1_q * qf->value + delta_th_eq; - - // State - self->integrator += Ts_K1 * (Vac->value - Vaf->value + Va_eq); - - lf_set(delta_thc, y); + timer t(0, period) + + input Vzf: double + input Vaf: double + input Vac: double + input qf: double + + output delta_thc: double + + state integrator: double(0.0) + + reaction(t) Vzf, Vaf, Vac, qf -> delta_thc {= + double Ts_K1 = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) + + // Output + double y = self->K1_intVa * self->integrator + + self->K1_Va * (Vaf->value - Va_eq) + + self->K1_Vz * Vzf->value + self->K1_q * qf->value + delta_th_eq; - =} + // State + self->integrator += Ts_K1 * (Vac->value - Vaf->value + Va_eq); + + lf_set(delta_thc, y); + + =} } reactor RosaceController( - filter_period: time(10 ms) + filter_period: time(10 ms) ) { - // Sensor inputs from aircraft - input Vz:double // Vertical speed - input Va:double // True airspeed - input h:double // Altitude measurement - input az:double // Vertical acceleration - input q:double // Pitch rate - - // Command inputs - input c:double // Altitude command - input s:double // Speed command - - output delta_thc: double // Engine control - output delta_ec: double // Elevator control - - h_c = new Hold() - - h_f = new Filter( - period = 100 ms, - init_x1 = {= h_eq * (1.0 - 1.477888930110354 /*a[1]*/ - 0.049596808318647 /*b1*/) =}, - init_x2 = {= h_eq =}, - a = (0.586756156020839, -1.477888930110354), - b = (0.049596808318647, 0.059270417591839) - ) - az_f = new Filter( - period = filter_period, - init_x1 = 0.0, - init_x2 = 0.0, - a = (0.169118914523145, -0.518588903229759), - b = (0.229019233988375, 0.421510777305010) - ) - Vz_f = new Filter( - period = filter_period, - init_x1 = 0.0, - init_x2 = 0.0, - a = (0.914975803093201, -1.911199519984605), - b = (0.001860178914816, 0.001916104193780) - ) - q_f = new Filter( - period = filter_period, - init_x1 = 0.0, - init_x2 = 0.0, - a = (0.586756156020839, -1.477888930110354), - b = (0.049596808318647, 0.059270417591839) - ) - Va_f = new Filter( - period = filter_period, - init_x1 = {= Va_eq * (1.0 - 1.911199519984605 /*a[1]*/ - 0.001916104193780 /*b[1]*/) =}, - init_x2 = {= Va_eq =}, - a = (0.914975803093201, -1.911199519984605), - b = (0.001860178914816, 0.001916104193780) - ) - - Vz_ct = new VzControl() - Va_ct = new VaControl() - - h, az, Vz, q, Va -> h_f.x, az_f.x, Vz_f.x, q_f.x, Va_f.x - c -> h_c.s - h_f.y -> h_c.x - - Vz_f.y, az_f.y, h_c.c, q_f.y -> Vz_ct.Vzf, Vz_ct.azf, Vz_ct.Vzc, Vz_ct.qf - Vz_ct.delta_ec -> delta_ec - - Va_f.y, Vz_f.y, s, q_f.y -> Va_ct.Vaf, Va_ct.Vzf, Va_ct.Vac, Va_ct.qf - Va_ct.delta_thc -> delta_thc + // Sensor inputs from aircraft + input Vz:double // Vertical speed + input Va:double // True airspeed + input h:double // Altitude measurement + input az:double // Vertical acceleration + input q:double // Pitch rate + + // Command inputs + input c:double // Altitude command + input s:double // Speed command + + output delta_thc: double // Engine control + output delta_ec: double // Elevator control + + h_c = new Hold() + + h_f = new Filter( + period = 100 ms, + init_x1 = {= h_eq * (1.0 - 1.477888930110354 /*a[1]*/ - 0.049596808318647 /*b1*/) =}, + init_x2 = {= h_eq =}, + a = (0.586756156020839, -1.477888930110354), + b = (0.049596808318647, 0.059270417591839) + ) + az_f = new Filter( + period = filter_period, + init_x1 = 0.0, + init_x2 = 0.0, + a = (0.169118914523145, -0.518588903229759), + b = (0.229019233988375, 0.421510777305010) + ) + Vz_f = new Filter( + period = filter_period, + init_x1 = 0.0, + init_x2 = 0.0, + a = (0.914975803093201, -1.911199519984605), + b = (0.001860178914816, 0.001916104193780) + ) + q_f = new Filter( + period = filter_period, + init_x1 = 0.0, + init_x2 = 0.0, + a = (0.586756156020839, -1.477888930110354), + b = (0.049596808318647, 0.059270417591839) + ) + Va_f = new Filter( + period = filter_period, + init_x1 = {= Va_eq * (1.0 - 1.911199519984605 /*a[1]*/ - 0.001916104193780 /*b[1]*/) =}, + init_x2 = {= Va_eq =}, + a = (0.914975803093201, -1.911199519984605), + b = (0.001860178914816, 0.001916104193780) + ) + + Vz_ct = new VzControl() + Va_ct = new VaControl() + + h, az, Vz, q, Va -> h_f.x, az_f.x, Vz_f.x, q_f.x, Va_f.x + c -> h_c.s + h_f.y -> h_c.x + + Vz_f.y, az_f.y, h_c.c, q_f.y -> Vz_ct.Vzf, Vz_ct.azf, Vz_ct.Vzc, Vz_ct.qf + Vz_ct.delta_ec -> delta_ec + + Va_f.y, Vz_f.y, s, q_f.y -> Va_ct.Vaf, Va_ct.Vzf, Va_ct.Vac, Va_ct.qf + Va_ct.delta_thc -> delta_thc } diff --git a/C/src/rosace/RosaceWithUI.lf b/C/src/rosace/RosaceWithUI.lf index 93babda5..0d54b2f2 100644 --- a/C/src/rosace/RosaceWithUI.lf +++ b/C/src/rosace/RosaceWithUI.lf @@ -7,7 +7,7 @@ * * This implementation is based on code from: * - * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. + * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. * * Since that original code bears an LGPL license, so does this program: * @@ -50,8 +50,8 @@ */ target C { - keepalive: true, - timeout: 10 min + keepalive: true, + timeout: 10 min } import Aircraft from "AircraftSimulator.lf" @@ -60,68 +60,68 @@ import RosaceController from "RosaceController.lf" import ServerUI from "../browser-ui/BrowserUI.lf" preamble {= - // Shared constants. - // Trimming parameters - #define Va_eq (230.0) // Nominal airspeed? - #define h_eq (10000.0) + // Shared constants. + // Trimming parameters + #define Va_eq (230.0) // Nominal airspeed? + #define h_eq (10000.0) - #define delta_th_eq (1.5868660794926) - #define delta_e_eq (0.012009615652468) - - #include // Define strtol() + #define delta_th_eq (1.5868660794926) + #define delta_e_eq (0.012009615652468) + + #include // Define strtol() =} reactor UserInterface( - period: time = 100 ms, - altitude_target: double = 10000, - airspeed_delta: double = 0.0 // From nominal Va_eq (230) + period: time = 100 ms, + altitude_target: double = 10000, + airspeed_delta: double = 0.0 // From nominal Va_eq (230) ) { - timer command(0, period) // Frequency with which to issue commands. - output altitude:double - output airspeed:double + timer command(0, period) // Frequency with which to issue commands. + output altitude:double + output airspeed:double - input Va:double - input h:double - - reaction(command) -> altitude, airspeed {= - lf_set(altitude, self->altitude_target); - lf_set(airspeed, self->airspeed_delta); - =} - - s = new ServerUI() - - reaction(s.request) Va, h -> s.response {= - char* response; - if(strncmp("/data", s.request->value, 5) == 0) { - // Construct JSON response. - asprintf(&response, "{\"Va\": %f, \"h\": %f}", Va->value, h->value); - } else if (strncmp("/?altitude=", s.request->value, 11) == 0) { - printf("-------- '%s'\n", s.request->value); - long result = strtol(s.request->value + 11, NULL, 10); - // Send back the request as a response. - asprintf(&response, "%ld", result); - self->altitude_target = (int)result; - } else { - printf("ERROR '%s'\n", s.request->value); - asprintf(&response, "Unrecognized request: %s", s.request->value); - } - lf_set_array(s.response, response, strlen(response) + 1); - =} + input Va:double + input h:double + + reaction(command) -> altitude, airspeed {= + lf_set(altitude, self->altitude_target); + lf_set(airspeed, self->airspeed_delta); + =} + + s = new ServerUI() + + reaction(s.request) Va, h -> s.response {= + char* response; + if(strncmp("/data", s.request->value, 5) == 0) { + // Construct JSON response. + asprintf(&response, "{\"Va\": %f, \"h\": %f}", Va->value, h->value); + } else if (strncmp("/?altitude=", s.request->value, 11) == 0) { + printf("-------- '%s'\n", s.request->value); + long result = strtol(s.request->value + 11, NULL, 10); + // Send back the request as a response. + asprintf(&response, "%ld", result); + self->altitude_target = (int)result; + } else { + printf("ERROR '%s'\n", s.request->value); + asprintf(&response, "Unrecognized request: %s", s.request->value); + } + lf_set_array(s.response, response, strlen(response) + 1); + =} } main reactor(filter_period: time = 10 ms) { - a = new Aircraft() - c = new RosaceController(filter_period = filter_period) - ui = new UserInterface() - - a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va - ui.altitude -> c.c - ui.airspeed -> c.s - - c.delta_ec -> a.delta_ec - c.delta_thc -> a.delta_thc - - // Print connections. - a.h -> ui.h - a.Va -> ui.Va + a = new Aircraft() + c = new RosaceController(filter_period = filter_period) + ui = new UserInterface() + + a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va + ui.altitude -> c.c + ui.airspeed -> c.s + + c.delta_ec -> a.delta_ec + c.delta_thc -> a.delta_thc + + // Print connections. + a.h -> ui.h + a.Va -> ui.Va } diff --git a/C/src/sdv/ParkingAssist.lf b/C/src/sdv/ParkingAssist.lf index 2df0e7f0..e139db06 100644 --- a/C/src/sdv/ParkingAssist.lf +++ b/C/src/sdv/ParkingAssist.lf @@ -52,15 +52,15 @@ reactor Sensors( initial_front_distance: int = 100 // In pixels ) { preamble {= - #include // Defines abs() - #include // Defines lround() + #include // Defines abs() + #include // Defines lround() =} - output speed: int // In pixels/second + output speed: int // In pixels/second output front_distance: int // In pixels state velocity: double = 0 // In pixels/second state position: double = 0 // In pixels - state gear: int = 1 // 1 for forward, -1 for reverse. + state gear: int = 1 // 1 for forward, -1 for reverse. timer t(0, sample_interval) @@ -73,53 +73,53 @@ reactor Sensors( * does not have the expected form. */ method json_to_int( - json: {= const char* =}, - key: {= const char* =}, - result: int* + json: {= const char* =}, + key: {= const char* =}, + result: int* ): int {= - char* key_position = strstr(json, key); - if (key_position == NULL) return -1; - // Check that the next character is closing quotation mark. - if (strncmp((key_position + strlen(key)), "\"", 1) != 0) return -3; - // Find the opening quotation mark for the value string. - char* start = strchr((key_position + strlen(key) + 1), '"'); - if (start == NULL) return -3; - *result = atoi(start + 1); - return 0; + char* key_position = strstr(json, key); + if (key_position == NULL) return -1; + // Check that the next character is closing quotation mark. + if (strncmp((key_position + strlen(key)), "\"", 1) != 0) return -3; + // Find the opening quotation mark for the value string. + char* start = strchr((key_position + strlen(key) + 1), '"'); + if (start == NULL) return -3; + *result = atoi(start + 1); + return 0; =} reaction(s.received) {= - char* json = s.received->value->message; - lf_print("Received control: %s", json); - int accelerator; - if (json_to_int(json, "accelerator", &accelerator) == 0) { - // FIXME: Setting the velocity to be a simple linear - // function of the accelerator position. Pretty naive - // vehicle model. - self->velocity = accelerator * 0.1; - } - int reset_value; - if (json_to_int(json, "reset", &reset_value) == 0) { - self->velocity = 0.0; - self->position = 0.0; - self->gear = 1; - } - int gear; - if (json_to_int(json, "gear", &gear) == 0) { - self->gear = gear; - } + char* json = s.received->value->message; + lf_print("Received control: %s", json); + int accelerator; + if (json_to_int(json, "accelerator", &accelerator) == 0) { + // FIXME: Setting the velocity to be a simple linear + // function of the accelerator position. Pretty naive + // vehicle model. + self->velocity = accelerator * 0.1; + } + int reset_value; + if (json_to_int(json, "reset", &reset_value) == 0) { + self->velocity = 0.0; + self->position = 0.0; + self->gear = 1; + } + int gear; + if (json_to_int(json, "gear", &gear) == 0) { + self->gear = gear; + } =} reaction(t) -> speed, front_distance {= - lf_set(speed, abs((int)lround(self->velocity))); + lf_set(speed, abs((int)lround(self->velocity))); - // Update position. - // Careful with rounding. - self->position += self->gear * self->velocity * (self->sample_interval / MSEC(1)) / 1000.0; + // Update position. + // Careful with rounding. + self->position += self->gear * self->velocity * (self->sample_interval / MSEC(1)) / 1000.0; - // lf_print("Position: %d\n", (int)lround(self->position)); + // lf_print("Position: %d\n", (int)lround(self->position)); - lf_set(front_distance, self->initial_front_distance - (int)lround(self->position)); + lf_set(front_distance, self->initial_front_distance - (int)lround(self->position)); =} } @@ -132,20 +132,20 @@ reactor Dashboard { s = new WebSocketServer(hostport = 241, max_clients = 1) // Allow only one client reaction(speed, front_distance) -> s.send {= - // Ignore the inputs if we are not connected. - if (self->connection.connected) { - // Construct payload. - char* message; - asprintf(&message, "{\"front_distance\": %d, \"speed\": %d}", front_distance->value, speed->value); - - // Construct struct to send. - web_socket_message_t* response = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); - response->length = strlen(message); // Do not include the null terminator. - response->wsi = self->connection.wsi; - response->message = message; - - lf_set(s.send, response); - } + // Ignore the inputs if we are not connected. + if (self->connection.connected) { + // Construct payload. + char* message; + asprintf(&message, "{\"front_distance\": %d, \"speed\": %d}", front_distance->value, speed->value); + + // Construct struct to send. + web_socket_message_t* response = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); + response->length = strlen(message); // Do not include the null terminator. + response->wsi = self->connection.wsi; + response->message = message; + + lf_set(s.send, response); + } =} // Make sure disconnections occur after messages are sent. @@ -159,39 +159,39 @@ reactor SoundAlert { p = new PlayWaveform(default_waveform_id = 3) reaction(front_distance) -> ding, p.waveform {= - instant_t previous_interval = self->ding_interval; - // Change the period of the sound. - if (front_distance->value > 75) { - // Go silent. - self->ding_interval = MSEC(0); - lf_set(p.waveform, 0); - } else if (front_distance->value > 50) { - self->ding_interval = MSEC(2000); - lf_set(p.waveform, 3); - } else if (front_distance->value > 25) { - self->ding_interval = MSEC(1000); - lf_set(p.waveform, 3); - } else if (front_distance->value > 15) { - self->ding_interval = MSEC(500); - lf_set(p.waveform, 9); - } else if (front_distance->value > 5) { - self->ding_interval = MSEC(200); - lf_set(p.waveform, 9); - } else { - self->ding_interval = MSEC(100); - lf_set(p.waveform, 8); - } - // If no sound is playing, start it playing. - if (self->ding_interval > MSEC(0) && previous_interval == MSEC(0)) { - lf_schedule(ding, 0); - } + instant_t previous_interval = self->ding_interval; + // Change the period of the sound. + if (front_distance->value > 75) { + // Go silent. + self->ding_interval = MSEC(0); + lf_set(p.waveform, 0); + } else if (front_distance->value > 50) { + self->ding_interval = MSEC(2000); + lf_set(p.waveform, 3); + } else if (front_distance->value > 25) { + self->ding_interval = MSEC(1000); + lf_set(p.waveform, 3); + } else if (front_distance->value > 15) { + self->ding_interval = MSEC(500); + lf_set(p.waveform, 9); + } else if (front_distance->value > 5) { + self->ding_interval = MSEC(200); + lf_set(p.waveform, 9); + } else { + self->ding_interval = MSEC(100); + lf_set(p.waveform, 8); + } + // If no sound is playing, start it playing. + if (self->ding_interval > MSEC(0) && previous_interval == MSEC(0)) { + lf_schedule(ding, 0); + } =} reaction(ding) -> p.note, ding {= - if (self->ding_interval > MSEC(0)) { - lf_set(p.note, 1); - lf_schedule(ding, self->ding_interval); - } + if (self->ding_interval > MSEC(0)) { + lf_set(p.note, 1); + lf_schedule(ding, self->ding_interval); + } =} } diff --git a/C/src/simulation/MemoryHierarchy.lf b/C/src/simulation/MemoryHierarchy.lf index 012c4ddb..5789cb1c 100644 --- a/C/src/simulation/MemoryHierarchy.lf +++ b/C/src/simulation/MemoryHierarchy.lf @@ -16,20 +16,20 @@ * @author Edward A. Lee */ target C { - timeout: 10 us + timeout: 10 us } import Random from "../lib/Random.lf" import PoissonClock from "../lib/PoissonClock.lf" import RandomDelay from "../lib/RandomDelay.lf" main reactor { - client = new Client(seed = 0) - cache = new Cache(seed = 0, miss_probability = 0.3) - memory = new Memory(seed = 0, average_delay = 100 ns) - client.request -> cache.request - cache.response ->client.response - cache.miss -> memory.request - memory.response -> cache.fill + client = new Client(seed = 0) + cache = new Cache(seed = 0, miss_probability = 0.3) + memory = new Memory(seed = 0, average_delay = 100 ns) + client.request -> cache.request + cache.response ->client.response + cache.miss -> memory.request + memory.response -> cache.fill } /** @@ -44,21 +44,21 @@ main reactor { * interpret this to be an error and print an error message. */ reactor Client(seed:int(0), lambda:double(10000000.0)) { - input response:time - output request:time - p = new PoissonClock(seed = seed, lambda = lambda) - reaction(p.event) -> request {= - lf_set(request, lf_time_logical_elapsed()); - =} - reaction(response) {= - if (response->value > 0) { - interval_t delay = lf_time_logical_elapsed() - response->value; - lf_print("Response in " PRINTF_TIME "ns", delay); - } else { - interval_t delay = lf_time_logical_elapsed() + response->value; - lf_print("ERROR in " PRINTF_TIME "ns", delay); - } - =} + input response:time + output request:time + p = new PoissonClock(seed = seed, lambda = lambda) + reaction(p.event) -> request {= + lf_set(request, lf_time_logical_elapsed()); + =} + reaction(response) {= + if (response->value > 0) { + interval_t delay = lf_time_logical_elapsed() - response->value; + lf_print("Response in " PRINTF_TIME "ns", delay); + } else { + interval_t delay = lf_time_logical_elapsed() + response->value; + lf_print("ERROR in " PRINTF_TIME "ns", delay); + } + =} } /** @@ -77,47 +77,47 @@ reactor Client(seed:int(0), lambda:double(10000000.0)) { * and will have the negative of the request value. */ reactor Cache( - miss_probability:double(0.1), - hit_delay:time(10 ns) + miss_probability:double(0.1), + hit_delay:time(10 ns) ) extends Random { - input request:time - output response:time - output miss:time - input fill:time - state pending:bool(false) - logical action a:time - reaction(a) -> response {= - lf_set(response, a->value); - =} - reaction(request) -> a, response, miss {= - bool hit = random() >= (int)(self->miss_probability * RAND_MAX); - if (self->pending && !hit) { - lf_set(response, -request->value); - } else if (hit) { - lf_schedule_copy(a, self->hit_delay, &request->value, 1); - } else { - lf_print("** Cache miss"); - self->pending = true; - lf_set(miss, request->value); - } - =} - reaction(fill) -> response {= - lf_set(response, fill->value); - self->pending = false; - =} + input request:time + output response:time + output miss:time + input fill:time + state pending:bool(false) + logical action a:time + reaction(a) -> response {= + lf_set(response, a->value); + =} + reaction(request) -> a, response, miss {= + bool hit = random() >= (int)(self->miss_probability * RAND_MAX); + if (self->pending && !hit) { + lf_set(response, -request->value); + } else if (hit) { + lf_schedule_copy(a, self->hit_delay, &request->value, 1); + } else { + lf_print("** Cache miss"); + self->pending = true; + lf_set(miss, request->value); + } + =} + reaction(fill) -> response {= + lf_set(response, fill->value); + self->pending = false; + =} } /** * Simulate a memory that responds after a random time. */ reactor Memory(seed:int(0), average_delay:time(100 ns)) { - input request:time - output response:time - delay = new RandomDelay(average = average_delay) - reaction(request) -> delay.in {= - lf_set(delay.in, request->value); - =} - reaction(delay.out) -> response {= - lf_set(response, delay.out->value); - =} + input request:time + output response:time + delay = new RandomDelay(average = average_delay) + reaction(request) -> delay.in {= + lf_set(delay.in, request->value); + =} + reaction(delay.out) -> response {= + lf_set(response, delay.out->value); + =} } diff --git a/C/src/simulation/PoissonProcess.lf b/C/src/simulation/PoissonProcess.lf index 7c4dcbfe..4f0cb572 100644 --- a/C/src/simulation/PoissonProcess.lf +++ b/C/src/simulation/PoissonProcess.lf @@ -11,28 +11,28 @@ * @author Edward A. Lee */ target C { - timeout: 5 sec + timeout: 5 sec } import PoissonClock from "../lib/PoissonClock.lf" import RandomDelay from "../lib/RandomDelay.lf" main reactor { - non_repeatable = new PoissonClock() - repeatable = new PoissonClock(seed = 1) - delay = new RandomDelay() - reaction(non_repeatable.event, repeatable.event) {= - double seconds = ((double)lf_time_logical_elapsed())/SEC(1); - if (non_repeatable.event->is_present) { - lf_print("non_repeatable event at time %f seconds", seconds); - } - if (repeatable.event->is_present) { - lf_print("repeatable event at time %f seconds", seconds); - } - =} - reaction(startup, delay.out) -> delay.in {= - lf_set(delay.in, lf_time_logical_elapsed()); - if(delay.out->is_present) { - double seconds = ((double)lf_time_logical_elapsed())/SEC(1); - lf_print("-- using RandomDelay, event at time %f seconds", seconds); - } - =} -} \ No newline at end of file + non_repeatable = new PoissonClock() + repeatable = new PoissonClock(seed = 1) + delay = new RandomDelay() + reaction(non_repeatable.event, repeatable.event) {= + double seconds = ((double)lf_time_logical_elapsed())/SEC(1); + if (non_repeatable.event->is_present) { + lf_print("non_repeatable event at time %f seconds", seconds); + } + if (repeatable.event->is_present) { + lf_print("repeatable event at time %f seconds", seconds); + } + =} + reaction(startup, delay.out) -> delay.in {= + lf_set(delay.in, lf_time_logical_elapsed()); + if(delay.out->is_present) { + double seconds = ((double)lf_time_logical_elapsed())/SEC(1); + lf_print("-- using RandomDelay, event at time %f seconds", seconds); + } + =} +} diff --git a/CCpp/src/DoorLock/DoorLock.lf b/CCpp/src/DoorLock/DoorLock.lf index f99f6bbf..46995099 100644 --- a/CCpp/src/DoorLock/DoorLock.lf +++ b/CCpp/src/DoorLock/DoorLock.lf @@ -15,126 +15,126 @@ */ target CCpp { - keepalive: true, + keepalive: true, } import UserInteraction from "lib/UserInteraction.lf" import PropagationDelaySim from "lib/PropagationDelaySim.lf" import AuthSim from "lib/AuthSim.lf" preamble {= - #include "types.h" - #include - #include + #include "types.h" + #include + #include =} reactor DoorLockController { - input door:OpenEvent - input button:LockCommand - input fob:LockCommand - input mobile:LockCommand + input door:OpenEvent + input button:LockCommand + input fob:LockCommand + input mobile:LockCommand + + output actuate:LockCommand - output actuate:LockCommand - - initial mode ClosedLocked { - reaction(reset) {= - lf_print("*** Door is closed and has been locked."); - =} - reaction(button, fob, mobile) -> ClosedUnlocked, actuate {= - if ( - (button->is_present && button->value == UNLOCK) - || (fob->is_present && fob->value == UNLOCK) - || (mobile->is_present && mobile->value == UNLOCK) - ) { - lf_set_mode(ClosedUnlocked); - lf_set(actuate, UNLOCK); - } else { - lf_print("Door is already locked."); - } - =} - reaction(door) {= - lf_print("Door is closed and locked."); - =} - } - mode ClosedUnlocked { - reaction(reset) {= - lf_print("*** Door has been closed and is unlocked."); - =} - reaction(button, fob, mobile) -> ClosedLocked, actuate {= - if ( - (button->is_present && button->value == LOCK) - || (fob->is_present && fob->value == LOCK) - || (mobile->is_present && mobile->value == LOCK) - ) { - lf_set_mode(ClosedLocked); - lf_set(actuate, LOCK); - } else { - lf_print("Door is already unlocked."); - } - =} - reaction(door) -> Open {= - if (door->value == OPEN) lf_set_mode(Open); - else lf_print("Door is already closed."); - =} - } - mode Open { - reaction(reset) {= - lf_print("*** Door has been opened."); - =} - reaction(door) -> ClosedUnlocked {= - if (door->value == CLOSE) lf_set_mode(ClosedUnlocked); - else lf_print("Door is already open."); - =} - reaction(button, fob, mobile) {= - lf_print("Door is open."); - =} - } + initial mode ClosedLocked { + reaction(reset) {= + lf_print("*** Door is closed and has been locked."); + =} + reaction(button, fob, mobile) -> ClosedUnlocked, actuate {= + if ( + (button->is_present && button->value == UNLOCK) + || (fob->is_present && fob->value == UNLOCK) + || (mobile->is_present && mobile->value == UNLOCK) + ) { + lf_set_mode(ClosedUnlocked); + lf_set(actuate, UNLOCK); + } else { + lf_print("Door is already locked."); + } + =} + reaction(door) {= + lf_print("Door is closed and locked."); + =} + } + mode ClosedUnlocked { + reaction(reset) {= + lf_print("*** Door has been closed and is unlocked."); + =} + reaction(button, fob, mobile) -> ClosedLocked, actuate {= + if ( + (button->is_present && button->value == LOCK) + || (fob->is_present && fob->value == LOCK) + || (mobile->is_present && mobile->value == LOCK) + ) { + lf_set_mode(ClosedLocked); + lf_set(actuate, LOCK); + } else { + lf_print("Door is already unlocked."); + } + =} + reaction(door) -> Open {= + if (door->value == OPEN) lf_set_mode(Open); + else lf_print("Door is already closed."); + =} + } + mode Open { + reaction(reset) {= + lf_print("*** Door has been opened."); + =} + reaction(door) -> ClosedUnlocked {= + if (door->value == CLOSE) lf_set_mode(ClosedUnlocked); + else lf_print("Door is already open."); + =} + reaction(button, fob, mobile) {= + lf_print("Door is open."); + =} + } } reactor DoorLockActuator(tolerance:time(1000 msec)) { - input in:LockCommand - - reaction(in) {= - interval_t lag = (lf_time_physical() - lf_time_logical()); - std::string command_string = (in->value == LOCK) ? "LOCK" : "UNLOCK"; + input in:LockCommand - std::cout << lf_time_physical_elapsed() << ": Command " << command_string - << " executed successfully. Time lag: " << lag << " ns." - << std::endl; - =} deadline(tolerance){= - interval_t lag = (lf_time_physical() - lf_time_logical()); - std::string command_string = (in->value == LOCK) ? "LOCK" : "UNLOCK"; + reaction(in) {= + interval_t lag = (lf_time_physical() - lf_time_logical()); + std::string command_string = (in->value == LOCK) ? "LOCK" : "UNLOCK"; - std::cout << lf_time_physical_elapsed() << ": Deadline violated!! " << command_string - << " executed " << lag - self->tolerance << " nsecs later then expected." << std::endl; - =} + std::cout << lf_time_physical_elapsed() << ": Command " << command_string + << " executed successfully. Time lag: " << lag << " ns." + << std::endl; + =} deadline(tolerance){= + interval_t lag = (lf_time_physical() - lf_time_logical()); + std::string command_string = (in->value == LOCK) ? "LOCK" : "UNLOCK"; + + std::cout << lf_time_physical_elapsed() << ": Deadline violated!! " << command_string + << " executed " << lag - self->tolerance << " nsecs later then expected." << std::endl; + =} } reactor DoorLockSystem { - input door:OpenEvent - input button:LockCommand - input fob:LockCommand - input mobile:LockCommand + input door:OpenEvent + input button:LockCommand + input fob:LockCommand + input mobile:LockCommand - dlc = new DoorLockController() - a = new DoorLockActuator() - door -> dlc.door - button -> dlc.button - fob -> dlc.fob - mobile -> dlc.mobile - dlc.actuate -> a.in + dlc = new DoorLockController() + a = new DoorLockActuator() + door -> dlc.door + button -> dlc.button + fob -> dlc.fob + mobile -> dlc.mobile + dlc.actuate -> a.in } main reactor { - ui = new UserInteraction() - dlc = new DoorLockSystem() - radio = new PropagationDelaySim() - auth_delay = new PropagationDelaySim(min_delay = 200 ms) - auth = new AuthSim() - - ui.door -> dlc.door - ui.button -> dlc.button - ui.fob -> radio.in - radio.out -> dlc.fob - ui.mobile -> auth_delay.in - auth_delay.out -> auth.in - auth.out -> dlc.mobile + ui = new UserInteraction() + dlc = new DoorLockSystem() + radio = new PropagationDelaySim() + auth_delay = new PropagationDelaySim(min_delay = 200 ms) + auth = new AuthSim() + + ui.door -> dlc.door + ui.button -> dlc.button + ui.fob -> radio.in + radio.out -> dlc.fob + ui.mobile -> auth_delay.in + auth_delay.out -> auth.in + auth.out -> dlc.mobile } diff --git a/CCpp/src/DoorLock/lib/AuthSim.lf b/CCpp/src/DoorLock/lib/AuthSim.lf index ed45d3d3..45b651f8 100644 --- a/CCpp/src/DoorLock/lib/AuthSim.lf +++ b/CCpp/src/DoorLock/lib/AuthSim.lf @@ -9,16 +9,16 @@ target CCpp reactor AuthSim { - input in:LockCommand - output out:LockCommand + input in:LockCommand + output out:LockCommand - state count:int(0) - - reaction(in) -> out {= - if (++self->count % 5 == 0) { - lf_print("!!! Authentication rejected."); - } else { - lf_set(out, in->value); - } - =} + state count:int(0) + + reaction(in) -> out {= + if (++self->count % 5 == 0) { + lf_print("!!! Authentication rejected."); + } else { + lf_set(out, in->value); + } + =} } diff --git a/CCpp/src/DoorLock/lib/PropagationDelaySim.lf b/CCpp/src/DoorLock/lib/PropagationDelaySim.lf index 9c26a392..65417170 100644 --- a/CCpp/src/DoorLock/lib/PropagationDelaySim.lf +++ b/CCpp/src/DoorLock/lib/PropagationDelaySim.lf @@ -10,18 +10,18 @@ target CCpp preamble {= - #include /* srand, rand */ + #include /* srand, rand */ =} reactor PropagationDelaySim(min_delay:time(100 msec)) { - input in:LockCommand - output out:LockCommand + input in:LockCommand + output out:LockCommand - reaction(startup) {= - // Seed the random number generator. - srand(lf_time_logical()); - =} - reaction(in) -> out {= - lf_nanosleep((rand()%10+1) * self->min_delay); - lf_set(out, in->value); - =} + reaction(startup) {= + // Seed the random number generator. + srand(lf_time_logical()); + =} + reaction(in) -> out {= + lf_nanosleep((rand()%10+1) * self->min_delay); + lf_set(out, in->value); + =} } diff --git a/CCpp/src/DoorLock/lib/UserInteraction.lf b/CCpp/src/DoorLock/lib/UserInteraction.lf index 440b31c5..0b7267c6 100644 --- a/CCpp/src/DoorLock/lib/UserInteraction.lf +++ b/CCpp/src/DoorLock/lib/UserInteraction.lf @@ -11,83 +11,83 @@ */ target C { - keepalive: true, - files: [ - "../include/types.h", - ], + keepalive: true, + files: [ + "../include/types.h", + ], } preamble {= - #include "types.h" - - void* read_input(void* user_response) { - lf_print("****************************************************\n" - "Press the following keys for the following actions:\n" - " o - OPEN the door\n" - " c - CLOSE the door\n" - " l - LOCK using door button\n" - " u - UNLOCK using door button\n" - " f - LOCK using wireless key fob\n" - " r - UNLOCK using wireless key fob\n" - " m - LOCK using mobile device\n" - " n - UNLOCK using mobile device\n" - ); - int c; - while(1) { - while((c = getchar()) != '\n') { - lf_schedule_copy(user_response, 0, &c, 1); - if (c == EOF) break; - } + #include "types.h" + + void* read_input(void* user_response) { + lf_print("****************************************************\n" + "Press the following keys for the following actions:\n" + " o - OPEN the door\n" + " c - CLOSE the door\n" + " l - LOCK using door button\n" + " u - UNLOCK using door button\n" + " f - LOCK using wireless key fob\n" + " r - UNLOCK using wireless key fob\n" + " m - LOCK using mobile device\n" + " n - UNLOCK using mobile device\n" + ); + int c; + while(1) { + while((c = getchar()) != '\n') { + lf_schedule_copy(user_response, 0, &c, 1); + if (c == EOF) break; + } - if (c == EOF) break; - } - return NULL; + if (c == EOF) break; } + return NULL; + } =} - + reactor UserInteraction { - output door:OpenEvent; - output button:LockCommand; - output fob:LockCommand; - output mobile:LockCommand; - - physical action key:int; - - reaction(startup) -> key {= - // start new thread - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, key); - =} + output door:OpenEvent; + output button:LockCommand; + output fob:LockCommand; + output mobile:LockCommand; + + physical action key:int; + + reaction(startup) -> key {= + // start new thread + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, key); + =} - reaction(key) -> door, button, fob, mobile {= - // lf_print("You typed '%c' at elapsed time %lld.", key->value, lf_time_logical_elapsed()); - - switch(key->value) { - case 'o': - lf_set(door, OPEN); - break; - case 'c': - lf_set(door, CLOSE); - break; - case 'l': - lf_set(button, LOCK); - break; - case 'u': - lf_set(button, UNLOCK); - break; - case 'f': - lf_set(fob, LOCK); - break; - case 'r': - lf_set(fob, UNLOCK); - break; - case 'm': - lf_set(mobile, LOCK); - break; - case 'n': - lf_set(mobile, UNLOCK); - break; - default: - lf_print("Unrecognized key '%c'", key->value); - } - =} + reaction(key) -> door, button, fob, mobile {= + // lf_print("You typed '%c' at elapsed time %lld.", key->value, lf_time_logical_elapsed()); + + switch(key->value) { + case 'o': + lf_set(door, OPEN); + break; + case 'c': + lf_set(door, CLOSE); + break; + case 'l': + lf_set(button, LOCK); + break; + case 'u': + lf_set(button, UNLOCK); + break; + case 'f': + lf_set(fob, LOCK); + break; + case 'r': + lf_set(fob, UNLOCK); + break; + case 'm': + lf_set(mobile, LOCK); + break; + case 'n': + lf_set(mobile, UNLOCK); + break; + default: + lf_print("Unrecognized key '%c'", key->value); + } + =} } diff --git a/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf b/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf index 3275b989..2c3f32e7 100644 --- a/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf +++ b/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf @@ -4,7 +4,7 @@ import Sender from "Sender.lf" import Receiver from "Receiver.lf" federated reactor { - sender = new Sender(); - receiver = new Receiver(); - sender.out -> receiver.in serializer "ROS2"; -} \ No newline at end of file + sender = new Sender(); + receiver = new Receiver(); + sender.out -> receiver.in serializer "ROS2"; +} diff --git a/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf b/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf index 0c59305d..881b6a82 100644 --- a/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf +++ b/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf @@ -1,31 +1,31 @@ // src/Receiver.lf target CCpp { - cmake-include: "include/composition.cmake" + cmake-include: "include/composition.cmake" }; preamble {= - #include "subscriber_member_function.h" + #include "subscriber_member_function.h" =} reactor Receiver { - // Instantiate the subscriber node as a sate variable - state subscriber_node : std::shared_ptr; - input in:std::shared_ptr; + // Instantiate the subscriber node as a sate variable + state subscriber_node : std::shared_ptr; + input in:std::shared_ptr; - reaction(startup) {= - // Initialize rclcpp - rclcpp::init(0, NULL); - // Instantiate the ROS node - self->subscriber_node = std::make_shared(); - =} + reaction(startup) {= + // Initialize rclcpp + rclcpp::init(0, NULL); + // Instantiate the ROS node + self->subscriber_node = std::make_shared(); + =} - reaction(in) {= + reaction(in) {= - lf_print("[LF receiver] Received %s", in->value->data.c_str()); - self->subscriber_node->topic_callback(in->value); - =} + lf_print("[LF receiver] Received %s", in->value->data.c_str()); + self->subscriber_node->topic_callback(in->value); + =} - reaction(shutdown) {= - rclcpp::shutdown(); - =} -} \ No newline at end of file + reaction(shutdown) {= + rclcpp::shutdown(); + =} +} diff --git a/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf b/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf index 1c6558ee..bd4910e0 100644 --- a/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf +++ b/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf @@ -1,34 +1,34 @@ // src/Sender.lf target CCpp { - cmake-include: "include/composition.cmake" + cmake-include: "include/composition.cmake" }; preamble {= - #include "publisher_member_function.h" + #include "publisher_member_function.h" =} reactor Sender { - // Instantiate the publisher node as a sate variable - state publisher_node : std::shared_ptr; - output out:std::shared_ptr; - timer t(0, 1 sec); - state count:int(0); + // Instantiate the publisher node as a sate variable + state publisher_node : std::shared_ptr; + output out:std::shared_ptr; + timer t(0, 1 sec); + state count:int(0); - reaction(startup) {= - // Initialize rclcpp - rclcpp::init(0, NULL); - // Instantiate the ROS node - self->publisher_node = std::make_shared(); - =} + reaction(startup) {= + // Initialize rclcpp + rclcpp::init(0, NULL); + // Instantiate the ROS node + self->publisher_node = std::make_shared(); + =} - reaction(t) -> out {= - lf_print("[LF sender] Send message"); - auto message = std::make_shared(); - message->data = "Hello, world! " + std::to_string(self->count++); - lf_set(out, message); - =} + reaction(t) -> out {= + lf_print("[LF sender] Send message"); + auto message = std::make_shared(); + message->data = "Hello, world! " + std::to_string(self->count++); + lf_set(out, message); + =} - reaction(shutdown) {= - rclcpp::shutdown(); - =} -} \ No newline at end of file + reaction(shutdown) {= + rclcpp::shutdown(); + =} +} diff --git a/CCpp/src/ROS/ROSBuiltInSerialization.lf b/CCpp/src/ROS/ROSBuiltInSerialization.lf index 2dc00a9c..c01fc36b 100644 --- a/CCpp/src/ROS/ROSBuiltInSerialization.lf +++ b/CCpp/src/ROS/ROSBuiltInSerialization.lf @@ -13,7 +13,7 @@ * * Then you can use lfc to compile this program: * - * lfc ROSBuiltInSerialization.lf + * lfc ROSBuiltInSerialization.lf * * And launch the federated program in the `bin` folder: * @@ -22,47 +22,47 @@ * @author Soroush Bateni */ target CCpp { - cmake: true, // Only CMake is supported - cmake-include: "include/CMakeListsExtension.txt", + cmake: true, // Only CMake is supported + cmake-include: "include/CMakeListsExtension.txt", }; preamble {= - #include "std_msgs/msg/int32.hpp" - + #include "std_msgs/msg/int32.hpp" + =} reactor Sender { - output out:std_msgs::msg::Int32; - - // state serialized_msg_pcl:rclcpp::SerializedMessage({=0u=}); - state count:int(0); - - timer t (0, 1 sec); + output out:std_msgs::msg::Int32; + + // state serialized_msg_pcl:rclcpp::SerializedMessage({=0u=}); + state count:int(0); + + timer t (0, 1 sec); + + reaction (t) -> out {= + std_msgs::msg::Int32 ros_message; + ros_message.data = self->count++; + lf_set(out, ros_message); - reaction (t) -> out {= - std_msgs::msg::Int32 ros_message; - ros_message.data = self->count++; - lf_set(out, ros_message); - - =} + =} } reactor Receiver { - input in:std_msgs::msg::Int32; - - reaction (in) {= - // Print the ROS2 message data - lf_print( - "Serialized integer after deserialization: %d", - in->value.data - ); - =} + input in:std_msgs::msg::Int32; + + reaction (in) {= + // Print the ROS2 message data + lf_print( + "Serialized integer after deserialization: %d", + in->value.data + ); + =} } federated reactor { - sender = new Sender(); - receiver = new Receiver(); - - sender.out -> receiver.in serializer "ros2"; + sender = new Sender(); + receiver = new Receiver(); + + sender.out -> receiver.in serializer "ros2"; } diff --git a/CCpp/src/ROS/ROSSerialization.lf b/CCpp/src/ROS/ROSSerialization.lf index 2c353dd7..8765e155 100644 --- a/CCpp/src/ROS/ROSSerialization.lf +++ b/CCpp/src/ROS/ROSSerialization.lf @@ -11,85 +11,85 @@ * the built-in ROS 2 serialization feature that is provided in LF. */ target CCpp { - coordination: decentralized, - timeout: 10 sec, - cmake-include: "include/CMakeListsExtension.txt" + coordination: decentralized, + timeout: 10 sec, + cmake-include: "include/CMakeListsExtension.txt" }; preamble {= - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/int32.hpp" - #include "rcutils/allocator.h" - #include "rclcpp/rclcpp.hpp" - #include "rclcpp/serialization.hpp" - - - #include "rclcpp/serialized_message.hpp" - - #include "rosidl_typesupport_cpp/message_type_support.hpp" + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/int32.hpp" + #include "rcutils/allocator.h" + #include "rclcpp/rclcpp.hpp" + #include "rclcpp/serialization.hpp" + + + #include "rclcpp/serialized_message.hpp" + + #include "rosidl_typesupport_cpp/message_type_support.hpp" =} reactor Clock(offset:time(0), period:time(1 sec)) { - output y:uint8_t*; + output y:uint8_t*; + + timer t(0, period); + state count:int(0); + state serialized_msg:rclcpp::SerializedMessage({=0u=}); + reaction(t) -> y {= + + (self->count)++; + + // From https://github.com/ros2/demos/blob/master/demo_nodes_cpp/src/topics/talker_serialized_message.cpp - timer t(0, period); - state count:int(0); - state serialized_msg:rclcpp::SerializedMessage({=0u=}); - reaction(t) -> y {= - - (self->count)++; - - // From https://github.com/ros2/demos/blob/master/demo_nodes_cpp/src/topics/talker_serialized_message.cpp - - auto msg = std::make_shared(); - msg->data = self->count; - - auto message_header_length = 8u; - auto message_payload_length = 10u; - self->serialized_msg.reserve(message_header_length + message_payload_length); - - static rclcpp::Serialization serializer_obj; - serializer_obj.serialize_message(msg.get(), &self->serialized_msg); - - SET_NEW_ARRAY(y, self->serialized_msg.size()); - y->value = self->serialized_msg.get_rcl_serialized_message().buffer; - =} + auto msg = std::make_shared(); + msg->data = self->count; + + auto message_header_length = 8u; + auto message_payload_length = 10u; + self->serialized_msg.reserve(message_header_length + message_payload_length); + + static rclcpp::Serialization serializer_obj; + serializer_obj.serialize_message(msg.get(), &self->serialized_msg); + + SET_NEW_ARRAY(y, self->serialized_msg.size()); + y->value = self->serialized_msg.get_rcl_serialized_message().buffer; + =} } reactor Destination { - input x:uint8_t*; + input x:uint8_t*; + + state s:int(1); + reaction(x) {= + auto message = std::make_unique( rcl_serialized_message_t{ + .buffer = (uint8_t*)x->token->value, + .buffer_length = x->token->length, + .buffer_capacity = x->token->length, + .allocator = rcl_get_default_allocator() + }); - state s:int(1); - reaction(x) {= - auto message = std::make_unique( rcl_serialized_message_t{ - .buffer = (uint8_t*)x->token->value, - .buffer_length = x->token->length, - .buffer_capacity = x->token->length, - .allocator = rcl_get_default_allocator() - }); - - // Check the ref count == 1 - // lf_print("%d", x->token->ref_count); - // assert(x->token->ref_count == 1); // Might be optimized away (see validate in cpp target) - - //rclcpp::SerializedMessage* msg = new rclcpp::SerializedMessage(x->token->length, rcl_get_default_allocator()); - auto msg = std::make_unique(std::move(*message.get())); - x->token->value = NULL; // Manually move it + // Check the ref count == 1 + // lf_print("%d", x->token->ref_count); + // assert(x->token->ref_count == 1); // Might be optimized away (see validate in cpp target) + + //rclcpp::SerializedMessage* msg = new rclcpp::SerializedMessage(x->token->length, rcl_get_default_allocator()); + auto msg = std::make_unique(std::move(*message.get())); + x->token->value = NULL; // Manually move it - // In order to deserialize the message we have to manually create a ROS2 - // message in which we want to convert the serialized data. - std_msgs::msg::Int32 int_msg; - auto serializer_obj = rclcpp::Serialization(); - serializer_obj.deserialize_message(msg.get(), &int_msg); - - lf_print("Received %d.", int_msg.data); - if (int_msg.data != self->s) { - lf_print_warning("Expected %d and got %d.", self->s, int_msg.data); - } - self->s++; - =} + // In order to deserialize the message we have to manually create a ROS2 + // message in which we want to convert the serialized data. + std_msgs::msg::Int32 int_msg; + auto serializer_obj = rclcpp::Serialization(); + serializer_obj.deserialize_message(msg.get(), &int_msg); + + lf_print("Received %d.", int_msg.data); + if (int_msg.data != self->s) { + lf_print_warning("Expected %d and got %d.", self->s, int_msg.data); + } + self->s++; + =} } federated reactor (period:time(1 sec)) { - c = new Clock(period = period); - d = new Destination(); - c.y -> d.x; + c = new Clock(period = period); + d = new Destination(); + c.y -> d.x; } diff --git a/Cpp/AlarmClock/src/AlarmClock.lf b/Cpp/AlarmClock/src/AlarmClock.lf index 338b219f..36b1d97f 100644 --- a/Cpp/AlarmClock/src/AlarmClock.lf +++ b/Cpp/AlarmClock/src/AlarmClock.lf @@ -13,8 +13,8 @@ */ target Cpp{ - cmake-include: "AlarmClock.cmake", - keepalive: true + cmake-include: "AlarmClock.cmake", + keepalive: true }; import Network from "./Network.lf"; @@ -24,17 +24,17 @@ import Clock from "./Clock.lf"; #import Clock.lf; main reactor AlarmClock { - clock = new Clock(); - network = new Network(); + clock = new Clock(); + network = new Network(); - // additon of a new event - network.event -> clock.event; - network.delete_index -> clock.cancel_by_index; - clock.event_dump -> network.updated_events; + // additon of a new event + network.event -> clock.event; + network.delete_index -> clock.cancel_by_index; + clock.event_dump -> network.updated_events; - reaction (startup) {= - std::cout << "Starting Lingua Franca AlarmClock" << std::endl; - =} + reaction (startup) {= + std::cout << "Starting Lingua Franca AlarmClock" << std::endl; + =} } diff --git a/Cpp/AlarmClock/src/Clock.lf b/Cpp/AlarmClock/src/Clock.lf index 30e3c641..753a1964 100644 --- a/Cpp/AlarmClock/src/Clock.lf +++ b/Cpp/AlarmClock/src/Clock.lf @@ -10,197 +10,197 @@ target Cpp{ - cmake-include: "AlarmClock.cmake", - keepalive: true + cmake-include: "AlarmClock.cmake", + keepalive: true }; public preamble {= - #include "shared_header.hpp" + #include "shared_header.hpp" =} reactor Trigger { - private preamble {= - auto convert_to_relative = [](long time_stamp){ - const auto t = std::chrono::system_clock::now(); - std::chrono::seconds desired_time = std::chrono::seconds(time_stamp); - std::chrono::seconds current_time = - std::chrono::duration_cast(t.time_since_epoch()); - std::chrono::seconds delta_t = desired_time - current_time; - return delta_t; - }; - =} - - input input_event: {=Event=}; - input input_interrupt: long; - logical action interrupt; - logical action triggered_event: {=std::string=}; - state ignore_flag: bool; - - //the input_event will scheduled - reaction (input_event) -> triggered_event {= - if(input_event.is_present()) { - auto extracted = input_event.get().get(); - auto delta_t = convert_to_relative(extracted->time_stamp_); - triggered_event.schedule(extracted->message_, delta_t); - } - =} - - reaction (input_interrupt) -> interrupt {= - if(input_interrupt.is_present()){ - auto delta_t = convert_to_relative(*(input_interrupt.get().get())); - interrupt.schedule(delta_t); - } - =} - - // reaction which will be triggered when a event is due - reaction(triggered_event) {= - auto select_random_file = []{ - std::vector files; - for (const auto& file : std::filesystem::directory_iterator(kMusicDir)) { - files.push_back(file.path().filename()); - } - - return files[rand() % files.size()]; - }; - - // takes a random audio file and playes it with mpg321 - if(triggered_event.is_present() and not ignore_flag){ - std::cout << "Triggering Event: " << *(triggered_event.get().get()) << std::endl; - std::string command = std::string(kPlayerCommand) + " " + std::string(kMusicDir) + select_random_file(); - if( system(command.c_str()) != 0 ){ - std::runtime_error("mpc finished with non zero return value"); - } - } - ignore_flag = false; - =} - - reaction (interrupt) {= - ignore_flag = true; - =} + private preamble {= + auto convert_to_relative = [](long time_stamp){ + const auto t = std::chrono::system_clock::now(); + std::chrono::seconds desired_time = std::chrono::seconds(time_stamp); + std::chrono::seconds current_time = + std::chrono::duration_cast(t.time_since_epoch()); + std::chrono::seconds delta_t = desired_time - current_time; + return delta_t; + }; + =} + + input input_event: {=Event=}; + input input_interrupt: long; + logical action interrupt; + logical action triggered_event: {=std::string=}; + state ignore_flag: bool; + + //the input_event will scheduled + reaction (input_event) -> triggered_event {= + if(input_event.is_present()) { + auto extracted = input_event.get().get(); + auto delta_t = convert_to_relative(extracted->time_stamp_); + triggered_event.schedule(extracted->message_, delta_t); + } + =} + + reaction (input_interrupt) -> interrupt {= + if(input_interrupt.is_present()){ + auto delta_t = convert_to_relative(*(input_interrupt.get().get())); + interrupt.schedule(delta_t); + } + =} + + // reaction which will be triggered when a event is due + reaction(triggered_event) {= + auto select_random_file = []{ + std::vector files; + for (const auto& file : std::filesystem::directory_iterator(kMusicDir)) { + files.push_back(file.path().filename()); + } + + return files[rand() % files.size()]; + }; + + // takes a random audio file and playes it with mpg321 + if(triggered_event.is_present() and not ignore_flag){ + std::cout << "Triggering Event: " << *(triggered_event.get().get()) << std::endl; + std::string command = std::string(kPlayerCommand) + " " + std::string(kMusicDir) + select_random_file(); + if( system(command.c_str()) != 0 ){ + std::runtime_error("mpc finished with non zero return value"); + } + } + ignore_flag = false; + =} + + reaction (interrupt) {= + ignore_flag = true; + =} } reactor Clock { - // function which is used to check if a given event has already past - private preamble {= - auto time_over(const Event& event) noexcept -> bool { - const auto p1 = std::chrono::system_clock::now(); - auto current_time = std::chrono::duration_cast(p1.time_since_epoch()).count(); - return current_time > event.time_stamp_; - } - =} - - // trigger reactor which handles the execution of the scheduled reaction - trigger = new Trigger(); - // this event will be scheduled and added to persistent storage - input event: Event; - input cancel_by_index: std::size_t; - // list of events - output event_dump: {= std::vector =}; - // timer which triggers clear and save - timer maintance(10 sec, 30 sec); - - // persistant storage - state events: std::vector(); - - // reaction that appends new events which will be scheduled - // the newtwork reactor is updated - reaction (event) -> trigger.input_event, event_dump {= - if (event.is_present() and not time_over(*event.get())){ - trigger.input_event.set(*event.get()); - events.push_back(*event.get()); - event_dump.set(events); - } - =} - - // initiation ... reading file to create state - reaction (startup) -> trigger.input_event, event_dump {= - // if the calender file doesn't exists it's created - if (not std::filesystem::exists(kFile)){ - std::ofstream{kFile}; - } - - std::ifstream file; - file.open(kFile); - - std::string line; - if(not file.is_open()) { - throw std::runtime_error("Cannot open event file!"); - } - - // iterating through the file every line corresponds to one events (csv) - while (file) { - std::getline(file, line); - if (line.empty()) { - break; - } - - Event serialized_event {}; - - // an event has the shape message;time_stamp\newline - std::size_t colon_pos = line.find(";"); - serialized_event.message_ = line.substr(0, colon_pos); - serialized_event.time_stamp_ = static_cast( - std::stoi(line.substr(colon_pos + 1, line.size() - 2)) - ); - - // if the given event is not already in the past it gets schedules by lingua franca - if(not time_over(serialized_event)){ - trigger.input_event.set(serialized_event); - events.push_back(serialized_event); - } - } - - file.close(); + // function which is used to check if a given event has already past + private preamble {= + auto time_over(const Event& event) noexcept -> bool { + const auto p1 = std::chrono::system_clock::now(); + auto current_time = std::chrono::duration_cast(p1.time_since_epoch()).count(); + return current_time > event.time_stamp_; + } + =} + + // trigger reactor which handles the execution of the scheduled reaction + trigger = new Trigger(); + // this event will be scheduled and added to persistent storage + input event: Event; + input cancel_by_index: std::size_t; + // list of events + output event_dump: {= std::vector =}; + // timer which triggers clear and save + timer maintance(10 sec, 30 sec); + + // persistant storage + state events: std::vector(); + + // reaction that appends new events which will be scheduled + // the newtwork reactor is updated + reaction (event) -> trigger.input_event, event_dump {= + if (event.is_present() and not time_over(*event.get())){ + trigger.input_event.set(*event.get()); + events.push_back(*event.get()); + event_dump.set(events); + } + =} + + // initiation ... reading file to create state + reaction (startup) -> trigger.input_event, event_dump {= + // if the calender file doesn't exists it's created + if (not std::filesystem::exists(kFile)){ + std::ofstream{kFile}; + } + + std::ifstream file; + file.open(kFile); + + std::string line; + if(not file.is_open()) { + throw std::runtime_error("Cannot open event file!"); + } + + // iterating through the file every line corresponds to one events (csv) + while (file) { + std::getline(file, line); + if (line.empty()) { + break; + } + + Event serialized_event {}; + + // an event has the shape message;time_stamp\newline + std::size_t colon_pos = line.find(";"); + serialized_event.message_ = line.substr(0, colon_pos); + serialized_event.time_stamp_ = static_cast( + std::stoi(line.substr(colon_pos + 1, line.size() - 2)) + ); + + // if the given event is not already in the past it gets schedules by lingua franca + if(not time_over(serialized_event)){ + trigger.input_event.set(serialized_event); + events.push_back(serialized_event); + } + } + + file.close(); + event_dump.set(events); + =} + + // state needs to be saved to file + reaction (shutdown, maintance) -> event_dump {= + remove_events(); + save(); + event_dump.set(events); + =} + + reaction (cancel_by_index) -> trigger.input_interrupt, event_dump {= + if(cancel_by_index.is_present()) { + std::size_t index = *(cancel_by_index.get().get()); + + if( index < events.size()){ + auto tag = events.at(index).time_stamp_; + trigger.input_interrupt.set(tag); + events.erase(events.begin() + index); event_dump.set(events); - =} - - // state needs to be saved to file - reaction (shutdown, maintance) -> event_dump {= - remove_events(); - save(); - event_dump.set(events); - =} - - reaction (cancel_by_index) -> trigger.input_interrupt, event_dump {= - if(cancel_by_index.is_present()) { - std::size_t index = *(cancel_by_index.get().get()); - - if( index < events.size()){ - auto tag = events.at(index).time_stamp_; - trigger.input_interrupt.set(tag); - events.erase(events.begin() + index); - event_dump.set(events); - } - - } - =} - - method remove_events() {= - // list of element which can be removed in the next iteration - std::vector removed_indices; - std::size_t index = 0; - for(const Event& event: events) { - if (time_over(event)){ - removed_indices.push_back(index); - } - index++; - } - - std::size_t removed_counter = 0; - for (std::size_t i : removed_indices) { - events.erase(events.begin() + i - removed_counter); - removed_counter++; - } - =} - - method save() {= - std::ofstream file(kFile, std::ios::trunc); - - for (const Event& e : events ) { - file << e.message_ + ";" + std::to_string(e.time_stamp_) + "\n"; - } - - file.close(); - =} + } + + } + =} + + method remove_events() {= + // list of element which can be removed in the next iteration + std::vector removed_indices; + std::size_t index = 0; + for(const Event& event: events) { + if (time_over(event)){ + removed_indices.push_back(index); + } + index++; + } + + std::size_t removed_counter = 0; + for (std::size_t i : removed_indices) { + events.erase(events.begin() + i - removed_counter); + removed_counter++; + } + =} + + method save() {= + std::ofstream file(kFile, std::ios::trunc); + + for (const Event& e : events ) { + file << e.message_ + ";" + std::to_string(e.time_stamp_) + "\n"; + } + + file.close(); + =} } diff --git a/Cpp/AlarmClock/src/Network.lf b/Cpp/AlarmClock/src/Network.lf index 63092207..38ee1599 100644 --- a/Cpp/AlarmClock/src/Network.lf +++ b/Cpp/AlarmClock/src/Network.lf @@ -13,213 +13,213 @@ */ target Cpp{ - cmake-include: "AlarmClock.cmake", - keepalive: true + cmake-include: "AlarmClock.cmake", + keepalive: true }; public preamble {= - #include "shared_header.hpp" + #include "shared_header.hpp" =} reactor Network { - private preamble {= - #include - #include - #include - #include - #include - =} - - // physical event which is triggered by receiving a request - physical action new_event: Event; - physical action delete_request: std::size_t; - - // variables for the receive thread - state thread: std::thread; // receive thread - state events: std::vector; // copy - - input updated_events: std::vector; - output event: Event; // event which will be added to the clock - output delete_index: std::size_t; - - // this reaction transforms a physical action into a logical reaction - reaction (new_event) -> event {= - if(new_event.is_present()){ - event.set(new_event.get()); + private preamble {= + #include + #include + #include + #include + #include + =} + + // physical event which is triggered by receiving a request + physical action new_event: Event; + physical action delete_request: std::size_t; + + // variables for the receive thread + state thread: std::thread; // receive thread + state events: std::vector; // copy + + input updated_events: std::vector; + output event: Event; // event which will be added to the clock + output delete_index: std::size_t; + + // this reaction transforms a physical action into a logical reaction + reaction (new_event) -> event {= + if(new_event.is_present()){ + event.set(new_event.get()); + } + =} + + reaction (delete_request) -> delete_index {= + if(delete_request.is_present()){ + delete_index.set(delete_request.get()); + } + =} + + // main starts receive thread + reaction (startup) -> delete_request, new_event{= + thread = std::thread([&] { + crow::SimpleApp app; + + // returns json of all the upcoming events + CROW_ROUTE(app, "/list") ([&]{ + // function converts unix timestamp to human readable datetime string + auto unix_to_human_readable = [](unsigned int time_stamp){ + using Clock = std::chrono::high_resolution_clock; + using TimePoint = std::chrono::time_point; + const Clock::duration duration_time_stamp = std::chrono::seconds(time_stamp); + const TimePoint chrono_time_point(duration_time_stamp); + std::time_t end_time = std::chrono::system_clock::to_time_t(chrono_time_point); + std::string return_string(std::ctime(&end_time)); + return return_string.substr(0, return_string.size() - 2); + }; + + crow::json::wvalue response; + for (const Event& event : events ){ + crow::json::wvalue json_event; + json_event["date"] = std::move(unix_to_human_readable(event.time_stamp_)); + json_event["message"] = event.message_; + + response[std::to_string(event.time_stamp_)] = std::move(json_event); + }; + return crow::response(response); + }); + + // adds new event by unix time stamp + CROW_ROUTE(app, "/add_event_timestamp").methods("POST"_method) + ([&new_event](const crow::request& req){ + auto json_body = crow::json::load(req.body); + if (!json_body) { + return crow::response(400); } - =} - reaction (delete_request) -> delete_index {= - if(delete_request.is_present()){ - delete_index.set(delete_request.get()); + // maybe add extra input validation + Event serialized_event { + json_body["message"].s(), + static_cast(json_body["time_stamp"].u()) + }; + + // triggers physical action + new_event.schedule(serialized_event, 0ms); + crow::json::wvalue response; + response["success"] = true; + return crow::response(response); + }); + + // adds new event by relativ times + CROW_ROUTE(app, "/add_event_relative").methods("POST"_method) + ([&new_event](const crow::request& req){ + auto relativ_time = 0l; + auto json_body = crow::json::load(req.body); + if (!json_body) { + return crow::response(400); } - =} - - // main starts receive thread - reaction (startup) -> delete_request, new_event{= - thread = std::thread([&] { - crow::SimpleApp app; - - // returns json of all the upcoming events - CROW_ROUTE(app, "/list") ([&]{ - // function converts unix timestamp to human readable datetime string - auto unix_to_human_readable = [](unsigned int time_stamp){ - using Clock = std::chrono::high_resolution_clock; - using TimePoint = std::chrono::time_point; - const Clock::duration duration_time_stamp = std::chrono::seconds(time_stamp); - const TimePoint chrono_time_point(duration_time_stamp); - std::time_t end_time = std::chrono::system_clock::to_time_t(chrono_time_point); - std::string return_string(std::ctime(&end_time)); - return return_string.substr(0, return_string.size() - 2); - }; - - crow::json::wvalue response; - for (const Event& event : events ){ - crow::json::wvalue json_event; - json_event["date"] = std::move(unix_to_human_readable(event.time_stamp_)); - json_event["message"] = event.message_; - - response[std::to_string(event.time_stamp_)] = std::move(json_event); - }; - return crow::response(response); - }); - - // adds new event by unix time stamp - CROW_ROUTE(app, "/add_event_timestamp").methods("POST"_method) - ([&new_event](const crow::request& req){ - auto json_body = crow::json::load(req.body); - if (!json_body) { - return crow::response(400); - } - - // maybe add extra input validation - Event serialized_event { - json_body["message"].s(), - static_cast(json_body["time_stamp"].u()) - }; - - // triggers physical action - new_event.schedule(serialized_event, 0ms); - crow::json::wvalue response; - response["success"] = true; - return crow::response(response); - }); - - // adds new event by relativ times - CROW_ROUTE(app, "/add_event_relative").methods("POST"_method) - ([&new_event](const crow::request& req){ - auto relativ_time = 0l; - auto json_body = crow::json::load(req.body); - if (!json_body) { - return crow::response(400); - } - - // calculates relative time in seconds - if(json_body.has("day")){ - relativ_time += 24 * 60 * 60 * json_body["day"].i(); - } - if(json_body.has("hour")){ - relativ_time += 60 * 60 * json_body["hour"].i(); - } - if(json_body.has("minute")){ - relativ_time += 60 * json_body["minute"].i(); - } - if(json_body.has("second")){ - relativ_time += json_body["second"].i(); - } - - const auto now = std::chrono::system_clock::now(); - auto current_time = std::chrono::duration_cast(now.time_since_epoch()).count(); - - std::cout << "current_time: " << current_time << " offset:" << relativ_time << std::endl; - Event serialized_event { - json_body["message"].s(), - current_time + relativ_time - }; - - // triggers physical action - new_event.schedule(serialized_event, 0ms); - crow::json::wvalue response; - response["success"] = true; - return crow::response(response); - }); - - // will set the timer in the text 24 hours - CROW_ROUTE(app, "/add_event_time").methods("POST"_method) - ([&new_event](const crow::request& req){ - // just % doesn't work because it is the remainder operator - // and does not behave like modulo for negative numbers - auto mod = [](int a, int b) { - int r = a % b; - return r < 0 ? r + b : r; - }; - - auto relativ_time = 0l; - auto json_body = crow::json::load(req.body); - if (!json_body) { - return crow::response(400); - } - - // use std::chrono::hh_mm_ss when C++20 is available - time_t time_struct = time(NULL); - struct tm *formatted_time = localtime(&time_struct); - - // calculating time differences and turning them into seconds for the time_stamp - if(json_body.has("hour")){ - relativ_time += 3600 * mod(json_body["hour"].i() - formatted_time->tm_hour - 1, 24); - } - if(json_body.has("minute")){ - relativ_time += 60 * mod(json_body["minute"].i() - formatted_time->tm_min - 1, 60); - } - if(json_body.has("second")){ - relativ_time += mod(json_body["second"].u() - formatted_time->tm_sec - 1, 60); - } - - Event serialized_event { - json_body["message"].s(), - relativ_time + time_struct - }; - - // triggers physical action - new_event.schedule(serialized_event, 0ms); - crow::json::wvalue response; - response["success"] = true; - return crow::response(response); - }); - - // request stopping playing music - // just used pidof to kill the process - CROW_ROUTE(app, "/stop") ([]{ - int status = system((std::string(kKillCommand) + " $(" + std::string(kPidofCommand) + " mpg321)").c_str()); - crow::json::wvalue response; - response["success"] = status; - return crow::response(response); - }); - - CROW_ROUTE(app, "/remove").methods("POST"_method) - ([&delete_request](const crow::request& req){ - auto json_body = crow::json::load(req.body); - if (!json_body) { - return crow::response(400); - } - - std::size_t index = json_body["index"].u(); - delete_request.schedule(index, 0s); - - crow::json::wvalue response; - response["success"] = true; - return crow::response(response); - }); - - // start the http server - app.port(kPort).multithreaded().run(); - }); - =} - reaction (updated_events) {= - events = std::move(*updated_events.get()); - =} - - reaction ( shutdown ) {= - thread.join(); - =} + + // calculates relative time in seconds + if(json_body.has("day")){ + relativ_time += 24 * 60 * 60 * json_body["day"].i(); + } + if(json_body.has("hour")){ + relativ_time += 60 * 60 * json_body["hour"].i(); + } + if(json_body.has("minute")){ + relativ_time += 60 * json_body["minute"].i(); + } + if(json_body.has("second")){ + relativ_time += json_body["second"].i(); + } + + const auto now = std::chrono::system_clock::now(); + auto current_time = std::chrono::duration_cast(now.time_since_epoch()).count(); + + std::cout << "current_time: " << current_time << " offset:" << relativ_time << std::endl; + Event serialized_event { + json_body["message"].s(), + current_time + relativ_time + }; + + // triggers physical action + new_event.schedule(serialized_event, 0ms); + crow::json::wvalue response; + response["success"] = true; + return crow::response(response); + }); + + // will set the timer in the text 24 hours + CROW_ROUTE(app, "/add_event_time").methods("POST"_method) + ([&new_event](const crow::request& req){ + // just % doesn't work because it is the remainder operator + // and does not behave like modulo for negative numbers + auto mod = [](int a, int b) { + int r = a % b; + return r < 0 ? r + b : r; + }; + + auto relativ_time = 0l; + auto json_body = crow::json::load(req.body); + if (!json_body) { + return crow::response(400); + } + + // use std::chrono::hh_mm_ss when C++20 is available + time_t time_struct = time(NULL); + struct tm *formatted_time = localtime(&time_struct); + + // calculating time differences and turning them into seconds for the time_stamp + if(json_body.has("hour")){ + relativ_time += 3600 * mod(json_body["hour"].i() - formatted_time->tm_hour - 1, 24); + } + if(json_body.has("minute")){ + relativ_time += 60 * mod(json_body["minute"].i() - formatted_time->tm_min - 1, 60); + } + if(json_body.has("second")){ + relativ_time += mod(json_body["second"].u() - formatted_time->tm_sec - 1, 60); + } + + Event serialized_event { + json_body["message"].s(), + relativ_time + time_struct + }; + + // triggers physical action + new_event.schedule(serialized_event, 0ms); + crow::json::wvalue response; + response["success"] = true; + return crow::response(response); + }); + + // request stopping playing music + // just used pidof to kill the process + CROW_ROUTE(app, "/stop") ([]{ + int status = system((std::string(kKillCommand) + " $(" + std::string(kPidofCommand) + " mpg321)").c_str()); + crow::json::wvalue response; + response["success"] = status; + return crow::response(response); + }); + + CROW_ROUTE(app, "/remove").methods("POST"_method) + ([&delete_request](const crow::request& req){ + auto json_body = crow::json::load(req.body); + if (!json_body) { + return crow::response(400); + } + + std::size_t index = json_body["index"].u(); + delete_request.schedule(index, 0s); + + crow::json::wvalue response; + response["success"] = true; + return crow::response(response); + }); + + // start the http server + app.port(kPort).multithreaded().run(); + }); + =} + reaction (updated_events) {= + events = std::move(*updated_events.get()); + =} + + reaction ( shutdown ) {= + thread.join(); + =} } diff --git a/Cpp/CarBrake/src/CarBrake.lf b/Cpp/CarBrake/src/CarBrake.lf index 2ee51359..ac857777 100644 --- a/Cpp/CarBrake/src/CarBrake.lf +++ b/Cpp/CarBrake/src/CarBrake.lf @@ -13,93 +13,93 @@ * error message if a deadline is violated. */ target Cpp { - cmake-include: "threads.cmake" + cmake-include: "threads.cmake" }; reactor Camera { - timer t(20msecs, 20msecs) - output frame: void + timer t(20msecs, 20msecs) + output frame: void - reaction (t) -> frame {= - frame.set(); // send a "frame" - =} + reaction (t) -> frame {= + frame.set(); // send a "frame" + =} } reactor BrakingAssistant { - input frame: void; - output trigger_brake: void; - - state counter: int(0); + input frame: void; + output trigger_brake: void; + + state counter: int(0); - reaction(frame) -> trigger_brake {= - // processing takes some time - std::this_thread::sleep_for(10ms); - - if (counter % 10 == 0) { - std::cout << "[automatic] Send the brake signal - " << get_physical_time() << std::endl; - trigger_brake.set(); - } - counter++; - =} + reaction(frame) -> trigger_brake {= + // processing takes some time + std::this_thread::sleep_for(10ms); + + if (counter % 10 == 0) { + std::cout << "[automatic] Send the brake signal - " << get_physical_time() << std::endl; + trigger_brake.set(); + } + counter++; + =} } reactor BrakePedal { - physical action pedal; - output trigger_brake: void; + physical action pedal; + output trigger_brake: void; - state thread: std::thread; + state thread: std::thread; - reaction(startup) -> pedal {= - this->thread = std::thread([&] () { - // press the brake pedal roughly every second - while (true) { - std::this_thread::sleep_for(1005ms); - std::cout << "[manual] Pressing the break pedal - " << get_physical_time() << std::endl; - pedal.schedule(0ms); - } - }); - =} + reaction(startup) -> pedal {= + this->thread = std::thread([&] () { + // press the brake pedal roughly every second + while (true) { + std::this_thread::sleep_for(1005ms); + std::cout << "[manual] Pressing the break pedal - " << get_physical_time() << std::endl; + pedal.schedule(0ms); + } + }); + =} - reaction(pedal) -> trigger_brake {= - std::cout << "[manual] Send the brake signal - " << get_physical_time() << std::endl; - trigger_brake.set(); - =} + reaction(pedal) -> trigger_brake {= + std::cout << "[manual] Send the brake signal - " << get_physical_time() << std::endl; + trigger_brake.set(); + =} - reaction(shutdown) {= - thread.join(); - =} + reaction(shutdown) {= + thread.join(); + =} } reactor Brake { - public preamble {= - #include - =} + public preamble {= + #include + =} - input brake_assistant: void; - input brake_pedal: void; - - reaction(brake_pedal) {= - std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; - std::cout << "[system] source: manual" << std::endl; - =} deadline (3msecs) {= - std::cout << "\033[1;31m[error]\033[0m Deadline on manual braking violated - " << get_physical_time() << std::endl; - =} - - reaction(brake_assistant) {= - std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; - std::cout << "[system] source: assistant" << std::endl; - =} deadline (15msecs) {= - std::cout << "\033[1;31m[error]\033[0m Deadline on automatic braking violated - " << get_physical_time() << std::endl; - =} + input brake_assistant: void; + input brake_pedal: void; + + reaction(brake_pedal) {= + std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; + std::cout << "[system] source: manual" << std::endl; + =} deadline (3msecs) {= + std::cout << "\033[1;31m[error]\033[0m Deadline on manual braking violated - " << get_physical_time() << std::endl; + =} + + reaction(brake_assistant) {= + std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; + std::cout << "[system] source: assistant" << std::endl; + =} deadline (15msecs) {= + std::cout << "\033[1;31m[error]\033[0m Deadline on automatic braking violated - " << get_physical_time() << std::endl; + =} } main reactor { - camera = new Camera(); - assistant = new BrakingAssistant(); - pedal = new BrakePedal(); - brake = new Brake(); - - camera.frame -> assistant.frame; - assistant.trigger_brake -> brake.brake_assistant; - pedal.trigger_brake -> brake.brake_pedal; + camera = new Camera(); + assistant = new BrakingAssistant(); + pedal = new BrakePedal(); + brake = new Brake(); + + camera.frame -> assistant.frame; + assistant.trigger_brake -> brake.brake_assistant; + pedal.trigger_brake -> brake.brake_pedal; } diff --git a/Cpp/CarBrake/src/CarBrake2.lf b/Cpp/CarBrake/src/CarBrake2.lf index 23166787..56b7841a 100644 --- a/Cpp/CarBrake/src/CarBrake2.lf +++ b/Cpp/CarBrake/src/CarBrake2.lf @@ -4,108 +4,108 @@ * on the ability to meet deadlines in the response to brake pedal actions. */ target Cpp { - cmake-include: "threads.cmake" + cmake-include: "threads.cmake" }; reactor Camera { - timer t(20msecs, 20msecs) - output frame: void + timer t(20msecs, 20msecs) + output frame: void - reaction (t) -> frame {= - frame.set(); // send a "frame" - =} + reaction (t) -> frame {= + frame.set(); // send a "frame" + =} } reactor BrakingAssistant { - input frame: void; - output trigger_brake: void; - - state counter: int(0); - - reaction(frame) -> trigger_brake {= - // processing takes some time - std::this_thread::sleep_for(10ms); - - if (counter % 10 == 0) { - std::cout << "[automatic] Send the brake signal - " << get_physical_time() << std::endl; - trigger_brake.set(); - } - counter++; - =} + input frame: void; + output trigger_brake: void; + + state counter: int(0); + + reaction(frame) -> trigger_brake {= + // processing takes some time + std::this_thread::sleep_for(10ms); + + if (counter % 10 == 0) { + std::cout << "[automatic] Send the brake signal - " << get_physical_time() << std::endl; + trigger_brake.set(); + } + counter++; + =} } reactor BrakePedal { - physical action pedal; - output trigger_brake: void; - - state thread: std::thread; - - reaction(startup) -> pedal {= - this->thread = std::thread([&] () { - // press the brake pedal roughly every second - - while (true) { - std::this_thread::sleep_for(1005ms); - std::cout << "[manual] Pressing the break pedal - " << get_physical_time() << std::endl; - pedal.schedule(0ms); - } - }); - =} - - reaction(pedal) -> trigger_brake {= - std::cout << "[manual] Send the brake signal - " << get_physical_time() << std::endl; - trigger_brake.set(); - =} - - reaction(shutdown) {= - thread.join(); - =} + physical action pedal; + output trigger_brake: void; + + state thread: std::thread; + + reaction(startup) -> pedal {= + this->thread = std::thread([&] () { + // press the brake pedal roughly every second + + while (true) { + std::this_thread::sleep_for(1005ms); + std::cout << "[manual] Pressing the break pedal - " << get_physical_time() << std::endl; + pedal.schedule(0ms); + } + }); + =} + + reaction(pedal) -> trigger_brake {= + std::cout << "[manual] Send the brake signal - " << get_physical_time() << std::endl; + trigger_brake.set(); + =} + + reaction(shutdown) {= + thread.join(); + =} } reactor Brake { - public preamble {= - #include - =} - - input brake_assistant: void; - input brake_pedal: void; - - reaction(brake_pedal) {= - std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; - std::cout << "[system] source: manual" << std::endl; - =} deadline (3msecs) {= - std::cout << "\033[1;31m[error]\033[0m Deadline on manual braking violated - " << get_physical_time() << std::endl; - =} - - reaction(brake_assistant) {= - std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; - std::cout << "[system] source: assistant" << std::endl; - =} deadline (15msecs) {= - std::cout << "\033[1;31m[error]\033[0m Deadline on automatic braking violated - " << get_physical_time() << std::endl; - =} + public preamble {= + #include + =} + + input brake_assistant: void; + input brake_pedal: void; + + reaction(brake_pedal) {= + std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; + std::cout << "[system] source: manual" << std::endl; + =} deadline (3msecs) {= + std::cout << "\033[1;31m[error]\033[0m Deadline on manual braking violated - " << get_physical_time() << std::endl; + =} + + reaction(brake_assistant) {= + std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; + std::cout << "[system] source: assistant" << std::endl; + =} deadline (15msecs) {= + std::cout << "\033[1;31m[error]\033[0m Deadline on automatic braking violated - " << get_physical_time() << std::endl; + =} } reactor Braking { - input brake_assistant: void; - pedal = new BrakePedal(); - brake = new Brake(); + input brake_assistant: void; + pedal = new BrakePedal(); + brake = new Brake(); - pedal.trigger_brake -> brake.brake_pedal; - brake_assistant -> brake.brake_assistant; + pedal.trigger_brake -> brake.brake_pedal; + brake_assistant -> brake.brake_assistant; } reactor Vision { - output trigger_brake: void; - camera = new Camera(); - assistant = new BrakingAssistant(); + output trigger_brake: void; + camera = new Camera(); + assistant = new BrakingAssistant(); - camera.frame -> assistant.frame; - assistant.trigger_brake -> trigger_brake; + camera.frame -> assistant.frame; + assistant.trigger_brake -> trigger_brake; } main reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking(); + vision = new Vision(); - vision.trigger_brake ~> braking.brake_assistant; + vision.trigger_brake ~> braking.brake_assistant; } diff --git a/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf b/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf index d273f82c..52e9b46e 100644 --- a/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf +++ b/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf @@ -11,28 +11,28 @@ target Cpp { } reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) { - input[num_nodes] in: size_t - output out: size_t + input[num_nodes] in: size_t + output out: size_t - reaction (startup) -> out{= - std::cout << "Hello from node " << bank_index << "!\n"; - // broadcast my ID to everyone - out.set(bank_index); - =} + reaction (startup) -> out{= + std::cout << "Hello from node " << bank_index << "!\n"; + // broadcast my ID to everyone + out.set(bank_index); + =} - reaction (in) {= - std::cout << "Node " << bank_index << " received messages from "; - for (auto& port : in) { - if (port.is_present()) { - std::cout << *port.get() << ", "; - } - } - std::cout << '\n'; - =} + reaction (in) {= + std::cout << "Node " << bank_index << " received messages from "; + for (auto& port : in) { + if (port.is_present()) { + std::cout << *port.get() << ", "; + } + } + std::cout << '\n'; + =} } main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - (nodes.out)+ -> nodes.in; + nodes = new[num_nodes] Node(num_nodes=num_nodes); + (nodes.out)+ -> nodes.in; } diff --git a/Cpp/Patterns/src/FullyConnected_01_Addressable.lf b/Cpp/Patterns/src/FullyConnected_01_Addressable.lf index 70b9537b..23752214 100644 --- a/Cpp/Patterns/src/FullyConnected_01_Addressable.lf +++ b/Cpp/Patterns/src/FullyConnected_01_Addressable.lf @@ -13,28 +13,28 @@ target Cpp { } reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) { - input[num_nodes] in: size_t - output[num_nodes] out: size_t + input[num_nodes] in: size_t + output[num_nodes] out: size_t - reaction (startup) -> out{= - std::cout << "Hello from node " << bank_index << "!\n"; - // send my ID only to my right neighbour - out[(bank_index + 1) % num_nodes].set(bank_index); - =} + reaction (startup) -> out{= + std::cout << "Hello from node " << bank_index << "!\n"; + // send my ID only to my right neighbour + out[(bank_index + 1) % num_nodes].set(bank_index); + =} - reaction (in) {= - std::cout << "Node " << bank_index << " received messages from "; - for (auto& port : in) { - if (port.is_present()) { - std::cout << *port.get() << ", "; - } - } - std::cout << '\n'; - =} + reaction (in) {= + std::cout << "Node " << bank_index << " received messages from "; + for (auto& port : in) { + if (port.is_present()) { + std::cout << *port.get() << ", "; + } + } + std::cout << '\n'; + =} } main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - nodes.out -> interleaved(nodes.in); + nodes = new[num_nodes] Node(num_nodes=num_nodes); + nodes.out -> interleaved(nodes.in); } diff --git a/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf b/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf index 42bee9c9..16e0dfb2 100644 --- a/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf +++ b/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf @@ -7,69 +7,69 @@ target Cpp { }; public preamble {= - struct Pos { - size_t col; - size_t row; - }; + struct Pos { + size_t col; + size_t row; + }; - std::ostream& operator<<(std::ostream& os, const Pos& pos); + std::ostream& operator<<(std::ostream& os, const Pos& pos); =} private preamble {= - std::ostream& operator<<(std::ostream& os, const Pos& pos) { - os << '(' << pos.col << ',' << pos.row << ')'; - return os; - } + std::ostream& operator<<(std::ostream& os, const Pos& pos) { + os << '(' << pos.col << ',' << pos.row << ')'; + return os; + } =} reactor Node(bank_index: size_t(0), row_index: size_t(0), num_rows: size_t(4), num_cols:size_t(4)) { - input[num_cols] fromRow: Pos - input[num_rows] fromCol: Pos + input[num_cols] fromRow: Pos + input[num_rows] fromCol: Pos - output toRowAndCol: Pos + output toRowAndCol: Pos - state pos: Pos{bank_index, row_index} + state pos: Pos{bank_index, row_index} - reaction (startup) -> toRowAndCol {= - std::cout << "Hello from " << pos << '\n'; - // send my position to everyone else in my row and column - toRowAndCol.set(pos); - =} + reaction (startup) -> toRowAndCol {= + std::cout << "Hello from " << pos << '\n'; + // send my position to everyone else in my row and column + toRowAndCol.set(pos); + =} - reaction (fromRow) {= - std::cout << pos << " received row messages from: "; - for (auto& port : fromRow) { - if (port.is_present()) { - std::cout << *port.get() << ", "; - } - } - std::cout << '\n'; - =} + reaction (fromRow) {= + std::cout << pos << " received row messages from: "; + for (auto& port : fromRow) { + if (port.is_present()) { + std::cout << *port.get() << ", "; + } + } + std::cout << '\n'; + =} - reaction (fromCol) {= - std::cout << pos << " received col messages from: "; - for (auto& port : fromCol) { - if (port.is_present()) { - std::cout << *port.get() << ", "; - } - } - std::cout << '\n'; - =} + reaction (fromCol) {= + std::cout << pos << " received col messages from: "; + for (auto& port : fromCol) { + if (port.is_present()) { + std::cout << *port.get() << ", "; + } + } + std::cout << '\n'; + =} } reactor Row(bank_index: size_t(0), num_rows:size_t(4), num_cols:size_t(4)) { - nodes = new[num_cols] Node(row_index=bank_index, num_rows=num_rows, num_cols=num_cols) + nodes = new[num_cols] Node(row_index=bank_index, num_rows=num_rows, num_cols=num_cols) - input[{=num_rows * num_cols=}] fromCol: Pos - output[num_cols] toCol: Pos + input[{=num_rows * num_cols=}] fromCol: Pos + output[num_cols] toCol: Pos - (nodes.toRowAndCol)+ -> nodes.fromRow - nodes.toRowAndCol -> toCol - fromCol -> interleaved(nodes.fromCol) + (nodes.toRowAndCol)+ -> nodes.fromRow + nodes.toRowAndCol -> toCol + fromCol -> interleaved(nodes.fromCol) } main reactor (num_rows:size_t(4), num_cols:size_t(4)) { - rows = new[num_rows] Row(num_rows=num_rows, num_cols=num_cols) - (rows.toCol)+ -> rows.fromCol; + rows = new[num_rows] Row(num_rows=num_rows, num_cols=num_cols) + (rows.toCol)+ -> rows.fromCol; } diff --git a/Cpp/ROS2/src/MinimalPublisher.lf b/Cpp/ROS2/src/MinimalPublisher.lf index 27c13ab8..8206eea7 100644 --- a/Cpp/ROS2/src/MinimalPublisher.lf +++ b/Cpp/ROS2/src/MinimalPublisher.lf @@ -8,34 +8,34 @@ * ensure regular message publication. */ target Cpp { - ros2: true, - ros2-dependencies: ["std_msgs"], + ros2: true, + ros2-dependencies: ["std_msgs"], } public preamble {= - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/string.hpp" + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/string.hpp" =} main reactor { - private preamble {= - // FIXME: forward declaration to make the node visible - extern rclcpp::Node* lf_node; - =} + private preamble {= + // FIXME: forward declaration to make the node visible + extern rclcpp::Node* lf_node; + =} - state publisher: {= rclcpp::Publisher::SharedPtr =} - state count: unsigned(0) + state publisher: {= rclcpp::Publisher::SharedPtr =} + state count: unsigned(0) - timer t(0, 500 ms) + timer t(0, 500 ms) - reaction(startup) {= - publisher = lf_node->create_publisher("topic", 10); - =} + reaction(startup) {= + publisher = lf_node->create_publisher("topic", 10); + =} - reaction(t) {= - auto message = std_msgs::msg::String(); - message.data = "Hello, world! " + std::to_string(count++); - reactor::log::Info() << "Publishing: " << message.data; - publisher->publish(message); - =} + reaction(t) {= + auto message = std_msgs::msg::String(); + message.data = "Hello, world! " + std::to_string(count++); + reactor::log::Info() << "Publishing: " << message.data; + publisher->publish(message); + =} } diff --git a/Cpp/ROS2/src/MinimalSubscriber.lf b/Cpp/ROS2/src/MinimalSubscriber.lf index e08e9aed..899bc7c5 100644 --- a/Cpp/ROS2/src/MinimalSubscriber.lf +++ b/Cpp/ROS2/src/MinimalSubscriber.lf @@ -8,35 +8,35 @@ * topic in a reactive manner. */ target Cpp { - ros2: true, - keepalive: true, - ros2-dependencies: ["std_msgs"], + ros2: true, + keepalive: true, + ros2-dependencies: ["std_msgs"], } public preamble {= - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/string.hpp" + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/string.hpp" =} main reactor { - private preamble {= - // FIXME: forward declaration to make the node visible - extern rclcpp::Node* lf_node; - =} + private preamble {= + // FIXME: forward declaration to make the node visible + extern rclcpp::Node* lf_node; + =} - state subscription: {= rclcpp::Subscription::SharedPtr =} - state count: unsigned(0) + state subscription: {= rclcpp::Subscription::SharedPtr =} + state count: unsigned(0) - physical action message: std::string; + physical action message: std::string; - reaction(startup) -> message {= - subscription = lf_node->create_subscription( - "topic", 10, [&message](const std_msgs::msg::String::SharedPtr msg) { message.schedule(msg->data); } ); - // FIXME: Why can't we use a reference type in the lambda argument? - // const std_msgs::msg::String::SharedPtr& msg - =} + reaction(startup) -> message {= + subscription = lf_node->create_subscription( + "topic", 10, [&message](const std_msgs::msg::String::SharedPtr msg) { message.schedule(msg->data); } ); + // FIXME: Why can't we use a reference type in the lambda argument? + // const std_msgs::msg::String::SharedPtr& msg + =} - reaction(message) {= - reactor::log::Info() << "I heard: " << *message.get(); - =} + reaction(message) {= + reactor::log::Info() << "I heard: " << *message.get(); + =} } diff --git a/Cpp/ReflexGame/src/ReflexGame.lf b/Cpp/ReflexGame/src/ReflexGame.lf index 8eb3a1ea..35d13d69 100644 --- a/Cpp/ReflexGame/src/ReflexGame.lf +++ b/Cpp/ReflexGame/src/ReflexGame.lf @@ -8,8 +8,8 @@ * @author Marten Lohstroh */ target Cpp { - keepalive: true, - cmake-include: "ReflexGame.cmake" + keepalive: true, + cmake-include: "ReflexGame.cmake" }; /** @@ -20,40 +20,40 @@ target Cpp { * @param max_time The maximum time between outputs. */ reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { - private preamble {= - // Generate a random additional delay over the minimum. - // Assume millisecond precision is enough. - reactor::Duration additional_time(reactor::Duration min_time, reactor::Duration max_time) { - int interval_in_msec = (max_time - min_time) / std::chrono::milliseconds(1); - return (std::rand() % interval_in_msec) * std::chrono::milliseconds(1); - } - =} - input another: void; - output out: void; - logical action prompt(min_time); - state count: int(0); - - reaction(startup) -> prompt {= - std::cout << "***********************************************" << std::endl; - std::cout << "Watch for the prompt, then hit Return or Enter." << std::endl; - std::cout << "Type Control-D (EOF) to quit." << std::endl << std::endl; + private preamble {= + // Generate a random additional delay over the minimum. + // Assume millisecond precision is enough. + reactor::Duration additional_time(reactor::Duration min_time, reactor::Duration max_time) { + int interval_in_msec = (max_time - min_time) / std::chrono::milliseconds(1); + return (std::rand() % interval_in_msec) * std::chrono::milliseconds(1); + } + =} + input another: void; + output out: void; + logical action prompt(min_time); + state count: int(0); + + reaction(startup) -> prompt {= + std::cout << "***********************************************" << std::endl; + std::cout << "Watch for the prompt, then hit Return or Enter." << std::endl; + std::cout << "Type Control-D (EOF) to quit." << std::endl << std::endl; - // TODO: Manual inclusion of header necessary? - // Set a seed for random number generation based on the current time. - std::srand(std::time(nullptr)); + // TODO: Manual inclusion of header necessary? + // Set a seed for random number generation based on the current time. + std::srand(std::time(nullptr)); - // Schedule the first event. - prompt.schedule(additional_time(0ms, max_time - min_time)); - =} - reaction(prompt) -> out {= - count++; - std::cout << count << ". Hit Return or Enter!" << std::endl << std::flush; - out.set(); - =} - reaction(another) -> prompt {= - // Schedule the next event. - prompt.schedule(additional_time(0ms, max_time - min_time)); - =} + // Schedule the first event. + prompt.schedule(additional_time(0ms, max_time - min_time)); + =} + reaction(prompt) -> out {= + count++; + std::cout << count << ". Hit Return or Enter!" << std::endl << std::flush; + out.set(); + =} + reaction(another) -> prompt {= + // Schedule the next event. + prompt.schedule(additional_time(0ms, max_time - min_time)); + =} } /** @@ -63,75 +63,75 @@ reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { * of this event and then report the response time. */ reactor GetUserInput { - public preamble {= - #include - =} + public preamble {= + #include + =} - physical action user_response: char; - state prompt_time: {= reactor::TimePoint =} ({= reactor::TimePoint::min() =}); - state total_time: time(0); - state count: int(0); - state thread: {= std::thread =}; + physical action user_response: char; + state prompt_time: {= reactor::TimePoint =} ({= reactor::TimePoint::min() =}); + state total_time: time(0); + state count: int(0); + state thread: {= std::thread =}; - input prompt: void; - output another: void; + input prompt: void; + output another: void; - reaction(startup) -> user_response {= - // Start the thread that listens for Enter or Return. - thread = std::thread([&] () { - int c; - while(1) { - while((c = getchar()) != '\n') { - if (c == EOF) break; - } - user_response.schedule(c, 0ms); - if (c == EOF) break; - } - }); - =} + reaction(startup) -> user_response {= + // Start the thread that listens for Enter or Return. + thread = std::thread([&] () { + int c; + while(1) { + while((c = getchar()) != '\n') { + if (c == EOF) break; + } + user_response.schedule(c, 0ms); + if (c == EOF) break; + } + }); + =} - reaction(prompt) {= - prompt_time = get_physical_time(); - =} + reaction(prompt) {= + prompt_time = get_physical_time(); + =} - reaction(user_response) -> another {= - auto c = user_response.get(); - if (*c == EOF) { - environment()->sync_shutdown(); - return; - } - // If the prompt_time is 0, then the user is cheating and - // hitting return before being prompted. - if (prompt_time == reactor::TimePoint::min()) { - std::cout << "YOU CHEATED!" << std::endl; - environment()->sync_shutdown(); - } else { - reactor::TimePoint logical = get_logical_time(); - std::chrono::duration elapsed = (logical - prompt_time); - auto time_in_ms = std::chrono::duration_cast(elapsed); - std::cout << "Response time in milliseconds: " << time_in_ms << std::endl; - count++; - total_time += time_in_ms; - // Reset the prompt_time to indicate that there is no new prompt. - prompt_time = reactor::TimePoint::min(); - // Trigger another prompt. - another.set(); - } - =} + reaction(user_response) -> another {= + auto c = user_response.get(); + if (*c == EOF) { + environment()->sync_shutdown(); + return; + } + // If the prompt_time is 0, then the user is cheating and + // hitting return before being prompted. + if (prompt_time == reactor::TimePoint::min()) { + std::cout << "YOU CHEATED!" << std::endl; + environment()->sync_shutdown(); + } else { + reactor::TimePoint logical = get_logical_time(); + std::chrono::duration elapsed = (logical - prompt_time); + auto time_in_ms = std::chrono::duration_cast(elapsed); + std::cout << "Response time in milliseconds: " << time_in_ms << std::endl; + count++; + total_time += time_in_ms; + // Reset the prompt_time to indicate that there is no new prompt. + prompt_time = reactor::TimePoint::min(); + // Trigger another prompt. + another.set(); + } + =} - reaction(shutdown) {= - thread.join(); - if (count > 0) { - std::cout << std::endl << "**** Average response time: " << std::chrono::duration_cast(total_time/count) << std::endl; - } else { - std::cout << std::endl << "**** No attempts." << std::endl; - } - =} + reaction(shutdown) {= + thread.join(); + if (count > 0) { + std::cout << std::endl << "**** Average response time: " << std::chrono::duration_cast(total_time/count) << std::endl; + } else { + std::cout << std::endl << "**** No attempts." << std::endl; + } + =} } main reactor ReflexGame { - p = new RandomSource(); - g = new GetUserInput(); - p.out -> g.prompt; - g.another -> p.another; + p = new RandomSource(); + g = new GetUserInput(); + p.out -> g.prompt; + g.another -> p.another; } diff --git a/Cpp/RequestResponse/src/Add.lf b/Cpp/RequestResponse/src/Add.lf index 4cea7eab..2cd17aac 100644 --- a/Cpp/RequestResponse/src/Add.lf +++ b/Cpp/RequestResponse/src/Add.lf @@ -15,29 +15,29 @@ import AddService from "AddService.lf" reactor Client { - timer t(0, 100ms) - output add_request: Request> - input add_response: Response + timer t(0, 100ms) + output add_request: Request> + input add_response: Response - state counter: int(0) + state counter: int(0) - reaction(t) -> add_request {= - auto req = Request(std::make_pair(counter, 42)); - add_request.set(req); - reactor::log::Info() << "Client asks what " << counter << " + 42 is"; - counter++; - =} + reaction(t) -> add_request {= + auto req = Request(std::make_pair(counter, 42)); + add_request.set(req); + reactor::log::Info() << "Client asks what " << counter << " + 42 is"; + counter++; + =} - reaction(add_response) {= - reactor::log::Info() << "It is " << add_response.get()->data(); - =} + reaction(add_response) {= + reactor::log::Info() << "It is " << add_response.get()->data(); + =} } main reactor { - adder = new AddService() - client = new Client() + adder = new AddService() + client = new Client() - client.add_request -> adder.request - adder.response -> client.add_response -} \ No newline at end of file + client.add_request -> adder.request + adder.response -> client.add_response +} diff --git a/Cpp/RequestResponse/src/AddService.lf b/Cpp/RequestResponse/src/AddService.lf index 36f40adc..c7d4ee99 100644 --- a/Cpp/RequestResponse/src/AddService.lf +++ b/Cpp/RequestResponse/src/AddService.lf @@ -1,23 +1,23 @@ target Cpp public preamble {= - #include "request_response.hh" + #include "request_response.hh" =} reactor AddService { - input request: Request> - output response: Response + input request: Request> + output response: Response - logical action request_queue: Request> + logical action request_queue: Request> - reaction(request) -> request_queue {= - auto delay = std::chrono::milliseconds(rand() % 400 + 100); - request_queue.schedule(request.get(), delay); - =} + reaction(request) -> request_queue {= + auto delay = std::chrono::milliseconds(rand() % 400 + 100); + request_queue.schedule(request.get(), delay); + =} - reaction(request_queue) -> response {= - auto req = request_queue.get(); - int sum = req->data().first + req->data().second; - response.set(req->make_response(sum)); - =} + reaction(request_queue) -> response {= + auto req = request_queue.get(); + int sum = req->data().first + req->data().second; + response.set(req->make_response(sum)); + =} } diff --git a/Cpp/RequestResponse/src/AddWithContext.lf b/Cpp/RequestResponse/src/AddWithContext.lf index 964e0caa..ccbc2eae 100644 --- a/Cpp/RequestResponse/src/AddWithContext.lf +++ b/Cpp/RequestResponse/src/AddWithContext.lf @@ -5,40 +5,40 @@ import ContextManager from "ContextManager.lf" reactor Client { - timer t(0, 100ms) - output add_request: Request> - input add_response: Response - - state counter: int(0) - - add_cm = new ContextManager<{=Request>=}, {=Response=}, {=std::function=}>() - - reaction(t) -> add_cm.request_in {= - auto req = Request(std::make_pair(counter, 42)); - int c = counter; // This is a weird corner case in C++ where the clojure below - // cannot capture counter by value. Copying it to a local variable helps... - auto callback = [c](int sum) { - reactor::log::Info() << "Result: " << c << " + 42 = " << sum; - }; - add_cm.request_in.set(std::make_pair(req, callback)); - reactor::log::Info() << "Client asks what " << counter << " + 42 is"; - counter++; - =} - - reaction(add_cm.response_out) {= - auto const& resp = add_cm.response_out.get()->first; - auto const& callback = add_cm.response_out.get()->second; - callback(resp.data()); - =} - - add_cm.request_out -> add_request - add_response -> add_cm.response_in + timer t(0, 100ms) + output add_request: Request> + input add_response: Response + + state counter: int(0) + + add_cm = new ContextManager<{=Request>=}, {=Response=}, {=std::function=}>() + + reaction(t) -> add_cm.request_in {= + auto req = Request(std::make_pair(counter, 42)); + int c = counter; // This is a weird corner case in C++ where the clojure below + // cannot capture counter by value. Copying it to a local variable helps... + auto callback = [c](int sum) { + reactor::log::Info() << "Result: " << c << " + 42 = " << sum; + }; + add_cm.request_in.set(std::make_pair(req, callback)); + reactor::log::Info() << "Client asks what " << counter << " + 42 is"; + counter++; + =} + + reaction(add_cm.response_out) {= + auto const& resp = add_cm.response_out.get()->first; + auto const& callback = add_cm.response_out.get()->second; + callback(resp.data()); + =} + + add_cm.request_out -> add_request + add_response -> add_cm.response_in } main reactor { - adder = new AddService() - client = new Client() + adder = new AddService() + client = new Client() - client.add_request -> adder.request - adder.response -> client.add_response -} \ No newline at end of file + client.add_request -> adder.request + adder.response -> client.add_response +} diff --git a/Cpp/RequestResponse/src/ContextManager.lf b/Cpp/RequestResponse/src/ContextManager.lf index c85a2519..d3bfdd96 100644 --- a/Cpp/RequestResponse/src/ContextManager.lf +++ b/Cpp/RequestResponse/src/ContextManager.lf @@ -1,28 +1,28 @@ target Cpp public preamble {= - #include "request_response.hh" + #include "request_response.hh" =} reactor ContextManager { - input request_in: std::pair - output request_out: Req + input request_in: std::pair + output request_out: Req - input response_in: Resp - output response_out: std::pair + input response_in: Resp + output response_out: std::pair - state context_buffer: {= std::map =} + state context_buffer: {= std::map =} - reaction (request_in) -> request_out {= - const auto& req = request_in.get()->first; - const auto& ctx = request_in.get()->second; - context_buffer[req.uid()] = ctx; - request_out.set(req); - =} + reaction (request_in) -> request_out {= + const auto& req = request_in.get()->first; + const auto& ctx = request_in.get()->second; + context_buffer[req.uid()] = ctx; + request_out.set(req); + =} - reaction (response_in) -> response_out {= - const auto& resp = *response_in.get(); - const auto& ctx = context_buffer[resp.uid()]; - response_out.set(std::make_pair(resp, ctx)); - =} -} \ No newline at end of file + reaction (response_in) -> response_out {= + const auto& resp = *response_in.get(); + const auto& ctx = context_buffer[resp.uid()]; + response_out.set(std::make_pair(resp, ctx)); + =} +} diff --git a/Cpp/RequestResponse/src/MAC.lf b/Cpp/RequestResponse/src/MAC.lf index ff628d99..ce3db56c 100644 --- a/Cpp/RequestResponse/src/MAC.lf +++ b/Cpp/RequestResponse/src/MAC.lf @@ -5,94 +5,94 @@ import MultiplyService from "MultiplyService.lf" import ContextManager from "ContextManager.lf" public preamble {= - struct MACData { - int factor1; - int factor2; - int summand; - }; + struct MACData { + int factor1; + int factor2; + int summand; + }; =} reactor MACService { - input request: Request - output response: Response - - output mul_request: Request> - input mul_response: Response - - output add_request: Request> - input add_response: Response - - add_cm = new ContextManager<{=Request>=}, {=Response=}, {=Request=}>() - mul_cm = new ContextManager<{=Request>=}, {=Response=}, {=Request=}>() - - reaction(request) -> mul_cm.request_in {= - auto& original_request = *request.get(); - auto& data = original_request.data(); - auto multiply = std::make_pair(data.factor1, data.factor2); - mul_cm.request_in.set(std::make_pair(Request(multiply), original_request)); - =} - - reaction(mul_cm.response_out) -> add_cm.request_in {= - auto mul_result = mul_cm.response_out.get()->first.data(); - auto& original_request = mul_cm.response_out.get()->second; - auto add = std::make_pair(mul_result, original_request.data().summand); - add_cm.request_in.set(std::make_pair(Request(add), original_request)); - =} - - reaction(add_cm.response_out) -> response {= - auto result = add_cm.response_out.get()->first.data(); - auto& original_request = add_cm.response_out.get()->second; - response.set(original_request.make_response(result)); - =} - - mul_cm.request_out -> mul_request - add_cm.request_out -> add_request - mul_response -> mul_cm.response_in - add_response -> add_cm.response_in + input request: Request + output response: Response + + output mul_request: Request> + input mul_response: Response + + output add_request: Request> + input add_response: Response + + add_cm = new ContextManager<{=Request>=}, {=Response=}, {=Request=}>() + mul_cm = new ContextManager<{=Request>=}, {=Response=}, {=Request=}>() + + reaction(request) -> mul_cm.request_in {= + auto& original_request = *request.get(); + auto& data = original_request.data(); + auto multiply = std::make_pair(data.factor1, data.factor2); + mul_cm.request_in.set(std::make_pair(Request(multiply), original_request)); + =} + + reaction(mul_cm.response_out) -> add_cm.request_in {= + auto mul_result = mul_cm.response_out.get()->first.data(); + auto& original_request = mul_cm.response_out.get()->second; + auto add = std::make_pair(mul_result, original_request.data().summand); + add_cm.request_in.set(std::make_pair(Request(add), original_request)); + =} + + reaction(add_cm.response_out) -> response {= + auto result = add_cm.response_out.get()->first.data(); + auto& original_request = add_cm.response_out.get()->second; + response.set(original_request.make_response(result)); + =} + + mul_cm.request_out -> mul_request + add_cm.request_out -> add_request + mul_response -> mul_cm.response_in + add_response -> add_cm.response_in } reactor Client { - timer t(0, 100ms) - output mac_request: Request - input mac_response: Response - - state counter: int(0) - - mac_cm = new ContextManager<{=Request=}, {=Response=}, MACData>() - - reaction(t) -> mac_cm.request_in {= - auto data = MACData{counter, counter + 1, counter + 2}; - auto req = Request(data); - mac_cm.request_in.set(std::make_pair(req, data)); - reactor::log::Info() << "Client asks what " << data.factor1 << " * " << data.factor2 - << " + " << data.summand << " is"; - counter++; - =} - - reaction(mac_cm.response_out) {= - auto const result = mac_cm.response_out.get()->first.data(); - auto const& data = mac_cm.response_out.get()->second; - reactor::log::Info() << "Result: " << data.factor1 << " * " << data.factor2 - << " + " << data.summand << " = " << result; - =} - - mac_cm.request_out -> mac_request - mac_response -> mac_cm.response_in + timer t(0, 100ms) + output mac_request: Request + input mac_response: Response + + state counter: int(0) + + mac_cm = new ContextManager<{=Request=}, {=Response=}, MACData>() + + reaction(t) -> mac_cm.request_in {= + auto data = MACData{counter, counter + 1, counter + 2}; + auto req = Request(data); + mac_cm.request_in.set(std::make_pair(req, data)); + reactor::log::Info() << "Client asks what " << data.factor1 << " * " << data.factor2 + << " + " << data.summand << " is"; + counter++; + =} + + reaction(mac_cm.response_out) {= + auto const result = mac_cm.response_out.get()->first.data(); + auto const& data = mac_cm.response_out.get()->second; + reactor::log::Info() << "Result: " << data.factor1 << " * " << data.factor2 + << " + " << data.summand << " = " << result; + =} + + mac_cm.request_out -> mac_request + mac_response -> mac_cm.response_in } main reactor { - client = new Client() - mac = new MACService() - adder = new AddService() - multiplier = new MultiplyService() + client = new Client() + mac = new MACService() + adder = new AddService() + multiplier = new MultiplyService() - client.mac_request -> mac.request - mac.response -> client.mac_response + client.mac_request -> mac.request + mac.response -> client.mac_response - mac.mul_request -> multiplier.request - multiplier.response -> mac.mul_response + mac.mul_request -> multiplier.request + multiplier.response -> mac.mul_response - mac.add_request -> adder.request - adder.response -> mac.add_response -} \ No newline at end of file + mac.add_request -> adder.request + adder.response -> mac.add_response +} diff --git a/Cpp/RequestResponse/src/MultiplyService.lf b/Cpp/RequestResponse/src/MultiplyService.lf index 82efa160..0c5c8913 100644 --- a/Cpp/RequestResponse/src/MultiplyService.lf +++ b/Cpp/RequestResponse/src/MultiplyService.lf @@ -1,23 +1,23 @@ target Cpp public preamble {= - #include "request_response.hh" + #include "request_response.hh" =} reactor MultiplyService { - input request: Request> - output response: Response + input request: Request> + output response: Response - logical action request_queue: Request> + logical action request_queue: Request> - reaction(request) -> request_queue {= - auto delay = std::chrono::milliseconds(rand() % 400 + 100); - request_queue.schedule(request.get(), delay); - =} + reaction(request) -> request_queue {= + auto delay = std::chrono::milliseconds(rand() % 400 + 100); + request_queue.schedule(request.get(), delay); + =} - reaction(request_queue) -> response {= - auto req = request_queue.get(); - int res = req->data().first * req->data().second; - response.set(req->make_response(res)); - =} + reaction(request_queue) -> response {= + auto req = request_queue.get(); + int res = req->data().first * req->data().second; + response.set(req->make_response(res)); + =} } diff --git a/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf b/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf index 6f1c45f5..795c7956 100644 --- a/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf +++ b/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf @@ -8,25 +8,25 @@ */ target Python { - docker: true, - files: ["../utils.py"] + 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" + 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" =} /** @@ -34,167 +34,167 @@ preamble {= * 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(lf.time.logical_elapsed(), get_microstep()) - log_entry = (lf.time.physical_elapsed(), 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() - =} + /* 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(lf.time.logical_elapsed(), get_microstep()) + log_entry = (lf.time.physical_elapsed(), 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; + fob = new DoubleUnlockKeyFob(); + twin = new DoubleUnlockKeyFob(); + fob.send_lock_action -> twin.get_lock_action; + twin.send_lock_action -> fob.get_lock_action; } diff --git a/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf b/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf index c9ace130..a0527167 100644 --- a/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf +++ b/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf @@ -8,56 +8,56 @@ */ target Python { - docker: true, - files: ["../utils.py"] + 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 = lf.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 = lf.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) - =} + 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 = lf.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 = lf.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; -} \ No newline at end of file + 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; +} diff --git a/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf b/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf index 9658df98..e7afef5e 100644 --- a/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf +++ b/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf @@ -8,14 +8,14 @@ */ target Python { - docker: true, - files: ["../utils.py"] + docker: true, + files: ["../utils.py"] }; preamble {= - import curses - import threading - from utils import Logger, Window + import curses + import threading + from utils import Logger, Window =} /** @@ -23,105 +23,105 @@ preamble {= * and sends and receives lock state to and from other key fobs. */ reactor KeyFob { - /* logger / window related state variables */ - state logger({=None=}); - state window({=None=}); - - /* KeyFob related state variables */ - state locked({=False=}); - state listener({=None=}); - - /* I/O ports and actions */ - input get_lock_state; - output send_lock_state; - physical action press_lock; - physical action press_unlock; - - preamble {= - def lock_state_str(self, locked): - return "Locked" if locked else "Unlocked" - - def print_lock_state(self): - self.window.change_line(1, f"Lock Status: {self.lock_state_str(self.locked)}") - - def print_log(self): - if self.logger.log_size() > 0: - for i, line in enumerate(self.logger.get_log()): - self.window.change_line(2 + i, line) - - def format_log_message(self, line): - elapsed_ptime, tag, remote, locked = line - return (f"At (tag: ({'{:,}'.format(tag.time)} ns, {tag.microstep}), " - f"lag: {'{:,}'.format(elapsed_ptime - tag.time)} ns), " - f"{'[Remote]' if remote else '[Local]'} Updated lock state to: {self.lock_state_str(locked)}") - - # log structure: (elapsed_physical_time:int, tag:int, remote:bool, locked:bool) - def append_log(self, remote, locked): - elapsed_tag = Tag(lf.time.logical_elapsed(), get_microstep()) - log_entry = (lf.time.physical_elapsed(), elapsed_tag, remote, locked) - 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() - =} - - reaction(startup) -> press_lock, press_unlock {= - # Set up the logger and the curses window - self.window = Window() - self.logger = Logger() - self.window.change_line(0, "Press 'l' to lock, 'u' to unlock, 'q' to quit") - 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) -> send_lock_state {= - self.append_log(remote=False, locked=True) - self.locked = True - self.print_lock_state() - self.print_log() - self.window.refresh() - send_lock_state.set(True) - =} - - reaction(press_unlock) -> send_lock_state {= - self.append_log(remote=False, locked=False) - self.locked = False - self.print_lock_state() - self.print_log() - self.window.refresh() - send_lock_state.set(False) - =} - - reaction(get_lock_state) {= - self.append_log(remote=True, locked=get_lock_state.value) - self.locked = get_lock_state.value - self.print_lock_state() - self.print_log() - self.window.refresh() - =} - - reaction(shutdown) {= - self.listener.join() - curses.endwin() - =} + /* logger / window related state variables */ + state logger({=None=}); + state window({=None=}); + + /* KeyFob related state variables */ + state locked({=False=}); + state listener({=None=}); + + /* I/O ports and actions */ + input get_lock_state; + output send_lock_state; + physical action press_lock; + physical action press_unlock; + + preamble {= + def lock_state_str(self, locked): + return "Locked" if locked else "Unlocked" + + def print_lock_state(self): + self.window.change_line(1, f"Lock Status: {self.lock_state_str(self.locked)}") + + def print_log(self): + if self.logger.log_size() > 0: + for i, line in enumerate(self.logger.get_log()): + self.window.change_line(2 + i, line) + + def format_log_message(self, line): + elapsed_ptime, tag, remote, locked = line + return (f"At (tag: ({'{:,}'.format(tag.time)} ns, {tag.microstep}), " + f"lag: {'{:,}'.format(elapsed_ptime - tag.time)} ns), " + f"{'[Remote]' if remote else '[Local]'} Updated lock state to: {self.lock_state_str(locked)}") + + # log structure: (elapsed_physical_time:int, tag:int, remote:bool, locked:bool) + def append_log(self, remote, locked): + elapsed_tag = Tag(lf.time.logical_elapsed(), get_microstep()) + log_entry = (lf.time.physical_elapsed(), elapsed_tag, remote, locked) + 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() + =} + + reaction(startup) -> press_lock, press_unlock {= + # Set up the logger and the curses window + self.window = Window() + self.logger = Logger() + self.window.change_line(0, "Press 'l' to lock, 'u' to unlock, 'q' to quit") + 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) -> send_lock_state {= + self.append_log(remote=False, locked=True) + self.locked = True + self.print_lock_state() + self.print_log() + self.window.refresh() + send_lock_state.set(True) + =} + + reaction(press_unlock) -> send_lock_state {= + self.append_log(remote=False, locked=False) + self.locked = False + self.print_lock_state() + self.print_log() + self.window.refresh() + send_lock_state.set(False) + =} + + reaction(get_lock_state) {= + self.append_log(remote=True, locked=get_lock_state.value) + self.locked = get_lock_state.value + self.print_lock_state() + self.print_log() + self.window.refresh() + =} + + reaction(shutdown) {= + self.listener.join() + curses.endwin() + =} } federated reactor { - fob = new KeyFob(); - twin = new KeyFob(); - fob.send_lock_state -> twin.get_lock_state; - twin.send_lock_state -> fob.get_lock_state; -} \ No newline at end of file + fob = new KeyFob(); + twin = new KeyFob(); + fob.send_lock_state -> twin.get_lock_state; + twin.send_lock_state -> fob.get_lock_state; +} diff --git a/Python/src/Piano/Piano.lf b/Python/src/Piano/Piano.lf index c849b7ba..f4ac1e74 100644 --- a/Python/src/Piano/Piano.lf +++ b/Python/src/Piano/Piano.lf @@ -10,9 +10,9 @@ * until the user decides to stop. */ target Python { - files: [gui.py, keys.png, soundfont.sf2], - threading: true, - keepalive: true + files: [gui.py, keys.png, soundfont.sf2], + threading: true, + keepalive: true }; @@ -20,33 +20,33 @@ target Python { * Receives key presses from the pygame piano process */ reactor GetUserInput { - preamble {= - import threading - def listen_for_input(self, user_response): - while 1: - try: - c = self.user_input.recv() - except EOFError: - request_stop() - return - # Each time a key press is received, schedule a user_response event - user_response.schedule(0, c) - =} - physical action user_response; - input user_input_pipe_init; - output user_input; - state user_input({=None=}) # multiprocessing.connection.PipeConnection - - reaction(user_input_pipe_init) -> user_response {= - # starts a thread to receive key presses from the pygame process - self.user_input = user_input_pipe_init.value - t = self.threading.Thread(target=self.listen_for_input, args=(user_response, )) - t.start() - =} - - reaction(user_response) -> user_input {= - user_input.set(user_response.value) - =} + preamble {= + import threading + def listen_for_input(self, user_response): + while 1: + try: + c = self.user_input.recv() + except EOFError: + request_stop() + return + # Each time a key press is received, schedule a user_response event + user_response.schedule(0, c) + =} + physical action user_response; + input user_input_pipe_init; + output user_input; + state user_input({=None=}) # multiprocessing.connection.PipeConnection + + reaction(user_input_pipe_init) -> user_response {= + # starts a thread to receive key presses from the pygame process + self.user_input = user_input_pipe_init.value + t = self.threading.Thread(target=self.listen_for_input, args=(user_response, )) + t.start() + =} + + reaction(user_response) -> user_input {= + user_input.set(user_response.value) + =} } @@ -54,24 +54,24 @@ reactor GetUserInput { * Sends graphics updates to the pygame piano process */ reactor UpdateGraphics { - input note; - input update_graphics_pipe_init; - state update_graphics({=None=}); # multiprocessing.connection.PipeConnection - state pressed_keys({=set()=}) - - reaction(update_graphics_pipe_init) {= - self.update_graphics = update_graphics_pipe_init.value - =} - - reaction(note) {= - key_down, note_t = note.value - if key_down and note_t not in self.pressed_keys: - self.pressed_keys.add(note_t) - self.update_graphics.send(self.pressed_keys) - if not key_down and note_t in self.pressed_keys: - self.pressed_keys.remove(note_t) - self.update_graphics.send(self.pressed_keys) - =} + input note; + input update_graphics_pipe_init; + state update_graphics({=None=}); # multiprocessing.connection.PipeConnection + state pressed_keys({=set()=}) + + reaction(update_graphics_pipe_init) {= + self.update_graphics = update_graphics_pipe_init.value + =} + + reaction(note) {= + key_down, note_t = note.value + if key_down and note_t not in self.pressed_keys: + self.pressed_keys.add(note_t) + self.update_graphics.send(self.pressed_keys) + if not key_down and note_t in self.pressed_keys: + self.pressed_keys.remove(note_t) + self.update_graphics.send(self.pressed_keys) + =} } @@ -79,162 +79,162 @@ reactor UpdateGraphics { * Plays sound using fluidsynth upon receiving signal from TranslateKeyToNote */ reactor PlaySound { - state lowest(4); # the octave of the lowest "C" on the piano. - state channel(8); - state Note; - state fluidsynth; - input note; - input play_sound_init; - - reaction(play_sound_init) {= - self.fluidsynth, self.Note = play_sound_init.value - =} - - reaction(note) {= - # upon receiving a note, play or stop the note depending on if its a key down or key up. - key_down, note_t = note.value - if key_down: - self.fluidsynth.play_Note(self.Note(note_t[0], self.lowest + note_t[1]), self.channel, 100) - else: - self.fluidsynth.stop_Note(self.Note(note_t[0], self.lowest + note_t[1]), self.channel) - =} + state lowest(4); # the octave of the lowest "C" on the piano. + state channel(8); + state Note; + state fluidsynth; + input note; + input play_sound_init; + + reaction(play_sound_init) {= + self.fluidsynth, self.Note = play_sound_init.value + =} + + reaction(note) {= + # upon receiving a note, play or stop the note depending on if its a key down or key up. + key_down, note_t = note.value + if key_down: + self.fluidsynth.play_Note(self.Note(note_t[0], self.lowest + note_t[1]), self.channel, 100) + else: + self.fluidsynth.stop_Note(self.Note(note_t[0], self.lowest + note_t[1]), self.channel) + =} } /* * Translates key presses to piano keys and triggers the initialization of StartGui */ reactor TranslateKeyToNote { - preamble {= - piano_keys = { - "z": ("C", 0), - "s": ("C#", 0), - "x": ("D", 0), - "d": ("D#", 0), - "c": ("E", 0), - "v": ("F", 0), - "g": ("F#", 0), - "b": ("G", 0), - "h": ("G#", 0), - "n": ("A", 0), - "j": ("A#", 0), - "m": ("B", 0), - "w": ("C", 1), - "3": ("C#", 1), - "e": ("D", 1), - "4": ("D#", 1), - "r": ("E", 1), - "t": ("F", 1), - "6": ("F#", 1), - "y": ("G", 1), - "7": ("G#", 1), - "u": ("A", 1), - "8": ("A#", 1), - "i": ("B", 1) - } - =} - - input user_input; - input translate_init; - output note; - output gui_init; - - reaction(translate_init) -> gui_init {= - gui_init.set(self.piano_keys) - =} - - reaction(user_input) -> note {= - key_down, c = user_input.value - if c in self.piano_keys: - note.set((key_down, self.piano_keys[c])) - =} + preamble {= + piano_keys = { + "z": ("C", 0), + "s": ("C#", 0), + "x": ("D", 0), + "d": ("D#", 0), + "c": ("E", 0), + "v": ("F", 0), + "g": ("F#", 0), + "b": ("G", 0), + "h": ("G#", 0), + "n": ("A", 0), + "j": ("A#", 0), + "m": ("B", 0), + "w": ("C", 1), + "3": ("C#", 1), + "e": ("D", 1), + "4": ("D#", 1), + "r": ("E", 1), + "t": ("F", 1), + "6": ("F#", 1), + "y": ("G", 1), + "7": ("G#", 1), + "u": ("A", 1), + "8": ("A#", 1), + "i": ("B", 1) + } + =} + + input user_input; + input translate_init; + output note; + output gui_init; + + reaction(translate_init) -> gui_init {= + gui_init.set(self.piano_keys) + =} + + reaction(user_input) -> note {= + key_down, c = user_input.value + if c in self.piano_keys: + note.set((key_down, self.piano_keys[c])) + =} } reactor StartFluidSynth { - preamble {= - import sys - import os - - try: - from mingus.containers.note import Note - except: - print("Import Error: Failed to import 'mingus'. Try 'pip3 install mingus'") - request_stop() - sys.exit(1) - - try: - from mingus.midi import fluidsynth - except: - if sys.platform == "darwin": - print("Import Error: fluidsynth is missing. Try 'brew install fluidsynth'") - elif sys.platform == "linux" or sys.platform == "linux2": - print("Import Error: fluidsynth is missing. Try 'sudo apt-get install -y fluidsynth'") - else: - print("Import Error: fluidsynth is missing. ") - request_stop() - sys.exit(1) - =} - state soundfont({=self.os.path.join(self.os.path.dirname(__file__), "soundfont.sf2")=}) - output translate_init; - output play_sound_init; + preamble {= + import sys + import os - reaction(startup) -> play_sound_init, translate_init {= - if not self.os.path.exists(self.soundfont): - print("Error: Soundfont file does not exist.") - print("Try downloading a soundfont file from here (this is the soundfont used for testing the demo): ") - print("http://www.schristiancollins.com/generaluser.php") - print("Alternatively, pick and download a soundfont from here:") - print("https://github.com/FluidSynth/fluidsynth/wiki/SoundFont") - print("Rename the soundfont to \"soundfont.sf2\" and put it under the same directory as Piano.lf.") - request_stop() - return + try: + from mingus.containers.note import Note + except: + print("Import Error: Failed to import 'mingus'. Try 'pip3 install mingus'") + request_stop() + sys.exit(1) + + try: + from mingus.midi import fluidsynth + except: + if sys.platform == "darwin": + print("Import Error: fluidsynth is missing. Try 'brew install fluidsynth'") + elif sys.platform == "linux" or sys.platform == "linux2": + print("Import Error: fluidsynth is missing. Try 'sudo apt-get install -y fluidsynth'") + else: + print("Import Error: fluidsynth is missing. ") + request_stop() + sys.exit(1) + =} + state soundfont({=self.os.path.join(self.os.path.dirname(__file__), "soundfont.sf2")=}) + output translate_init; + output play_sound_init; + + reaction(startup) -> play_sound_init, translate_init {= + if not self.os.path.exists(self.soundfont): + print("Error: Soundfont file does not exist.") + print("Try downloading a soundfont file from here (this is the soundfont used for testing the demo): ") + print("http://www.schristiancollins.com/generaluser.php") + print("Alternatively, pick and download a soundfont from here:") + print("https://github.com/FluidSynth/fluidsynth/wiki/SoundFont") + print("Rename the soundfont to \"soundfont.sf2\" and put it under the same directory as Piano.lf.") + request_stop() + return - # initialize fluidsynth - driver = None - if self.sys.platform == "linux" or self.sys.platform == "linux2": - driver = "alsa" - if not self.fluidsynth.init(self.soundfont, driver): - print("Error: Failed to initialize fluidsynth") - request_stop() - return - - play_sound_init.set((self.fluidsynth, self.Note)) - translate_init.set(0) - =} + # initialize fluidsynth + driver = None + if self.sys.platform == "linux" or self.sys.platform == "linux2": + driver = "alsa" + if not self.fluidsynth.init(self.soundfont, driver): + print("Error: Failed to initialize fluidsynth") + request_stop() + return + + play_sound_init.set((self.fluidsynth, self.Note)) + translate_init.set(0) + =} } /* * Starts the GUI and triggers initialization of UpdateGraphics and GetUserInput reactors. */ reactor StartGui { - preamble {= - import gui - =} - input gui_init; - output user_input_pipe; - output update_graphics_pipe; - - reaction(gui_init) -> user_input_pipe, update_graphics_pipe {= - piano_keys = gui_init.value - user_input_pout, update_graphics_pin = self.gui.start_gui(piano_keys) - user_input_pipe.set(user_input_pout) - update_graphics_pipe.set(update_graphics_pin) - =} + preamble {= + import gui + =} + input gui_init; + output user_input_pipe; + output update_graphics_pipe; + + reaction(gui_init) -> user_input_pipe, update_graphics_pipe {= + piano_keys = gui_init.value + user_input_pout, update_graphics_pin = self.gui.start_gui(piano_keys) + user_input_pipe.set(user_input_pout) + update_graphics_pipe.set(update_graphics_pin) + =} } main reactor { - gui = new StartGui() - fs = new StartFluidSynth() - translate = new TranslateKeyToNote() - update_graphics = new UpdateGraphics() - get_user_input = new GetUserInput() - play_sound = new PlaySound() - - fs.translate_init -> translate.translate_init; - fs.play_sound_init -> play_sound.play_sound_init; - gui.user_input_pipe -> get_user_input.user_input_pipe_init - gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init - get_user_input.user_input -> translate.user_input - translate.note -> update_graphics.note - translate.note -> play_sound.note - translate.gui_init -> gui.gui_init + gui = new StartGui() + fs = new StartFluidSynth() + translate = new TranslateKeyToNote() + update_graphics = new UpdateGraphics() + get_user_input = new GetUserInput() + play_sound = new PlaySound() + + fs.translate_init -> translate.translate_init; + fs.play_sound_init -> play_sound.play_sound_init; + gui.user_input_pipe -> get_user_input.user_input_pipe_init + gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init + get_user_input.user_input -> translate.user_input + translate.note -> update_graphics.note + translate.note -> play_sound.note + translate.gui_init -> gui.gui_init } diff --git a/Python/src/ROS/PythonMigration/lf-python/Main.lf b/Python/src/ROS/PythonMigration/lf-python/Main.lf index 790cc8ae..91920a86 100644 --- a/Python/src/ROS/PythonMigration/lf-python/Main.lf +++ b/Python/src/ROS/PythonMigration/lf-python/Main.lf @@ -15,7 +15,7 @@ import Sender from "Sender.lf" import Receiver from "Receiver.lf" federated reactor { - sender = new Sender(); - receiver = new Receiver(); - sender.topic -> receiver.topic; + sender = new Sender(); + receiver = new Receiver(); + sender.topic -> receiver.topic; } diff --git a/Python/src/ROS/PythonMigration/lf-python/Receiver.lf b/Python/src/ROS/PythonMigration/lf-python/Receiver.lf index c0c4f825..13444af4 100644 --- a/Python/src/ROS/PythonMigration/lf-python/Receiver.lf +++ b/Python/src/ROS/PythonMigration/lf-python/Receiver.lf @@ -13,41 +13,41 @@ target Python; preamble {= - # Locate the MinimalPublisher class written for ROS - # After source the ros package, - # Python import would be: - # from [package name].[file name] import [class name] - from py_pubsub.subscriber_member_function import MinimalSubscriber - - # copy and paste all imports from python file to here - import rclpy - from rclpy.node import Node - from std_msgs.msg import String + # Locate the MinimalPublisher class written for ROS + # After source the ros package, + # Python import would be: + # from [package name].[file name] import [class name] + from py_pubsub.subscriber_member_function import MinimalSubscriber + + # copy and paste all imports from python file to here + import rclpy + from rclpy.node import Node + from std_msgs.msg import String =} reactor Receiver { - state minimal_subscriber; - input topic; - - reaction(startup) {= - rclpy.init(args=None) - self.minimal_subscriber = MinimalSubscriber() - - # rclpy.spin is commented out. - #rclpy.spin(self.minimal_subscriber) - =} - - reaction(topic) {= - # Lingua Franca subscriber can receive the message, - # and deliver it to the listener_callback function. - - print("[LF] Topic data received on LF side: " + str(topic.value.data)) - print("[LF] Calling callback function on ROS side: ") - self.minimal_subscriber.listener_callback(topic.value) - =} - - reaction(shutdown) {= - self.minimal_subscriber.destroy_node() - rclpy.shutdown() - =} + state minimal_subscriber; + input topic; + + reaction(startup) {= + rclpy.init(args=None) + self.minimal_subscriber = MinimalSubscriber() + + # rclpy.spin is commented out. + #rclpy.spin(self.minimal_subscriber) + =} + + reaction(topic) {= + # Lingua Franca subscriber can receive the message, + # and deliver it to the listener_callback function. + + print("[LF] Topic data received on LF side: " + str(topic.value.data)) + print("[LF] Calling callback function on ROS side: ") + self.minimal_subscriber.listener_callback(topic.value) + =} + + reaction(shutdown) {= + self.minimal_subscriber.destroy_node() + rclpy.shutdown() + =} } diff --git a/Python/src/ROS/PythonMigration/lf-python/Sender.lf b/Python/src/ROS/PythonMigration/lf-python/Sender.lf index 18794d9a..2222fc08 100644 --- a/Python/src/ROS/PythonMigration/lf-python/Sender.lf +++ b/Python/src/ROS/PythonMigration/lf-python/Sender.lf @@ -14,45 +14,45 @@ target Python; preamble {= - # Locate the MinimalPublisher class written for ROS - # After source the ros package, - # Python import would be: - # from [package name].[file name] import [class name] - from py_pubsub.publisher_member_function import MinimalPublisher + # Locate the MinimalPublisher class written for ROS + # After source the ros package, + # Python import would be: + # from [package name].[file name] import [class name] + from py_pubsub.publisher_member_function import MinimalPublisher - # copy and paste all imports from python file to here - import rclpy - from rclpy.node import Node - from std_msgs.msg import String + # copy and paste all imports from python file to here + import rclpy + from rclpy.node import Node + from std_msgs.msg import String =} reactor Sender { - state minimal_publisher; - timer t(0, 500ms); - output topic; + state minimal_publisher; + timer t(0, 500ms); + output topic; - reaction(startup) {= - rclpy.init(args=None) - self.minimal_publisher = MinimalPublisher() - - # rclpy.spin is commented out. - #rclpy.spin(self.minimal_publisher) - =} + reaction(startup) {= + rclpy.init(args=None) + self.minimal_publisher = MinimalPublisher() + + # rclpy.spin is commented out. + #rclpy.spin(self.minimal_publisher) + =} - reaction(t) -> topic {= - # Copy and paste the timer callback implementation - # from ROS to Lingua Franca, - # and replace the ROS publish method with Lingua Franca + reaction(t) -> topic {= + # Copy and paste the timer callback implementation + # from ROS to Lingua Franca, + # and replace the ROS publish method with Lingua Franca - msg = String() - msg.data = "Hello World: " + str(self.minimal_publisher.i) - topic.set(msg) - print("[LF] Publishing: " + str(msg.data)) - self.minimal_publisher.i += 1 - =} + msg = String() + msg.data = "Hello World: " + str(self.minimal_publisher.i) + topic.set(msg) + print("[LF] Publishing: " + str(msg.data)) + self.minimal_publisher.i += 1 + =} - reaction(shutdown) {= - self.minimal_publisher.destroy_node() - rclpy.shutdown() - =} + reaction(shutdown) {= + self.minimal_publisher.destroy_node() + rclpy.shutdown() + =} } diff --git a/Python/src/ReflexGame/ReflexGame.lf b/Python/src/ReflexGame/ReflexGame.lf index 867a2f07..fe07b954 100644 --- a/Python/src/ReflexGame/ReflexGame.lf +++ b/Python/src/ReflexGame/ReflexGame.lf @@ -10,70 +10,70 @@ * an interactive, real-time game experience. */ target Python { - keepalive: true, - files: [gui.py] + keepalive: true, + files: [gui.py] } reactor RandomSource(min_time(2 sec), max_time(8 sec)) { - preamble {= - import random - def additional_time(self, min_time, max_time): - return self.random.randint(min_time, max_time) - =} - input another; - output out; - logical action prompt(min_time); - state count(0); - reaction(startup) {= - self.random.seed() - =} + preamble {= + import random + def additional_time(self, min_time, max_time): + return self.random.randint(min_time, max_time) + =} + input another; + output out; + logical action prompt(min_time); + state count(0); + reaction(startup) {= + self.random.seed() + =} - reaction(prompt) -> out {= - self.count += 1 - out.set(self.count) - =} + reaction(prompt) -> out {= + self.count += 1 + out.set(self.count) + =} - reaction(another) -> prompt {= - # schedule a prompt event - prompt.schedule(self.additional_time(0, self.max_time - self.min_time)) - =} + reaction(another) -> prompt {= + # schedule a prompt event + prompt.schedule(self.additional_time(0, self.max_time - self.min_time)) + =} } /* * Receives key presses from the pygame process. */ reactor GetUserInput { - preamble {= - import threading - def listen_for_input(self, user_response): - while 1: - try: - c = self.user_input.recv() - except EOFError: - request_stop() - return - # Each time a key press is received, schedule a user_response event - user_response.schedule(0, c) - =} + preamble {= + import threading + def listen_for_input(self, user_response): + while 1: + try: + c = self.user_input.recv() + except EOFError: + request_stop() + return + # Each time a key press is received, schedule a user_response event + user_response.schedule(0, c) + =} + + physical action user_response; + state user_input({=None=}); # multiprocessing.connection.PipeConnection + input user_input_pipe_init; + output user_input; + + reaction(user_input_pipe_init) -> user_response {= + # Stores the Pipe object that will be used to receive key presses from + # the pygame process + self.user_input = user_input_pipe_init.value - physical action user_response; - state user_input({=None=}); # multiprocessing.connection.PipeConnection - input user_input_pipe_init; - output user_input; - - reaction(user_input_pipe_init) -> user_response {= - # Stores the Pipe object that will be used to receive key presses from - # the pygame process - self.user_input = user_input_pipe_init.value - - # Starts the thread that receives key presses from the pygame process - t = self.threading.Thread(target=self.listen_for_input, args=(user_response, )) - t.start() - =} - - reaction(user_response) -> user_input {= - user_input.set(user_response.value) - =} + # Starts the thread that receives key presses from the pygame process + t = self.threading.Thread(target=self.listen_for_input, args=(user_response, )) + t.start() + =} + + reaction(user_response) -> user_input {= + user_input.set(user_response.value) + =} } @@ -81,115 +81,115 @@ reactor GetUserInput { * Sends graphics updates to the pygame process. */ reactor UpdateGraphics { - input prompt; - input update_graphics_pipe_init; - input user_input; - output another; - state update_graphics({=None=}); # multiprocessing.connection.PipeConnection - state first({=True=}) - state count(0); - state total_time_in_ms(0); - state prompt_time(0); - - reaction(update_graphics_pipe_init) {= - # Stores the Pipe object that will be used to send graphics update to - # the pygame process - self.update_graphics = update_graphics_pipe_init.value - - # Displays an introductory prompt to the user. - self.update_graphics.send(((0,0,0), # Color of background - (255, 255, 255), # Color of text - "Press any key to begin", - "To end the game, you can either: ", - "1. Close this window", - "2. Press CTRL+C in the Terminal", - "3. Press any key before the prompt shows up.")) - =} - - reaction(prompt) {= - # Ask the user for input upon receiving a prompt input from RandomSource - self.update_graphics.send(((152,251,152), - (0, 0, 0), - "{}. Press any key!".format(prompt.value))) - self.prompt_time = lf.time.physical() - =} + input prompt; + input update_graphics_pipe_init; + input user_input; + output another; + state update_graphics({=None=}); # multiprocessing.connection.PipeConnection + state first({=True=}) + state count(0); + state total_time_in_ms(0); + state prompt_time(0); + + reaction(update_graphics_pipe_init) {= + # Stores the Pipe object that will be used to send graphics update to + # the pygame process + self.update_graphics = update_graphics_pipe_init.value - reaction(user_input) -> another {= - if self.first: - # if the first ever key press is detected, set "another" to trigger a prompt from RandomSource - self.first = False - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "Wait for the prompt...")) - - # ask for the first ever prompt - another.set(42) - elif self.prompt_time == 0: - if self.count > 0: - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "YOU CHEATED!", - "Average response time: {:.2f} ms".format(self.total_time_in_ms / self.count))) - else: - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "YOU CHEATED!", - "Average response time: undefined")) - request_stop() - else: - time_in_ms = (lf.time.logical() - self.prompt_time) // MSEC(1) - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "Response time in milliseconds: {}".format(time_in_ms), - "Wait for the prompt...")) - self.count += 1 - self.total_time_in_ms += time_in_ms - self.prompt_time = 0 - - # ask for another prompt - another.set(42) - =} - - reaction(shutdown) {= - if self.count > 0: - print("Average response time: {:.2f} ms".format(self.total_time_in_ms / self.count)) - else: - print("Average response_time: undefined") - =} + # Displays an introductory prompt to the user. + self.update_graphics.send(((0,0,0), # Color of background + (255, 255, 255), # Color of text + "Press any key to begin", + "To end the game, you can either: ", + "1. Close this window", + "2. Press CTRL+C in the Terminal", + "3. Press any key before the prompt shows up.")) + =} + + reaction(prompt) {= + # Ask the user for input upon receiving a prompt input from RandomSource + self.update_graphics.send(((152,251,152), + (0, 0, 0), + "{}. Press any key!".format(prompt.value))) + self.prompt_time = lf.time.physical() + =} + + reaction(user_input) -> another {= + if self.first: + # if the first ever key press is detected, set "another" to trigger a prompt from RandomSource + self.first = False + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "Wait for the prompt...")) + + # ask for the first ever prompt + another.set(42) + elif self.prompt_time == 0: + if self.count > 0: + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "YOU CHEATED!", + "Average response time: {:.2f} ms".format(self.total_time_in_ms / self.count))) + else: + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "YOU CHEATED!", + "Average response time: undefined")) + request_stop() + else: + time_in_ms = (lf.time.logical() - self.prompt_time) // MSEC(1) + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "Response time in milliseconds: {}".format(time_in_ms), + "Wait for the prompt...")) + self.count += 1 + self.total_time_in_ms += time_in_ms + self.prompt_time = 0 + + # ask for another prompt + another.set(42) + =} + + reaction(shutdown) {= + if self.count > 0: + print("Average response time: {:.2f} ms".format(self.total_time_in_ms / self.count)) + else: + print("Average response_time: undefined") + =} } /* * Starts the GUI and pass the user_input_pout and - * update_graphics_pin Pipe objects - * to GetUserInput and UpdateGraphics + * update_graphics_pin Pipe objects + * to GetUserInput and UpdateGraphics */ reactor StartGui { - preamble {= - import gui - =} + preamble {= + import gui + =} - output user_input_pipe; - output update_graphics_pipe; + output user_input_pipe; + output update_graphics_pipe; + + reaction(startup) -> user_input_pipe, update_graphics_pipe {= + # Starts the gui pygame process + user_input_pout, update_graphics_pin = self.gui.start_gui() - reaction(startup) -> user_input_pipe, update_graphics_pipe {= - # Starts the gui pygame process - user_input_pout, update_graphics_pin = self.gui.start_gui() - - # Sets the outputs to trigger the initialization of GetUserInput and UpdateGrpahics - user_input_pipe.set(user_input_pout) - update_graphics_pipe.set(update_graphics_pin) - =} + # Sets the outputs to trigger the initialization of GetUserInput and UpdateGrpahics + user_input_pipe.set(user_input_pout) + update_graphics_pipe.set(update_graphics_pin) + =} } main reactor { - random_source = new RandomSource() - get_user_input = new GetUserInput() - update_graphics = new UpdateGraphics() - random_source.out -> update_graphics.prompt - get_user_input.user_input -> update_graphics.user_input - update_graphics.another -> random_source.another - gui = new StartGui() - gui.user_input_pipe -> get_user_input.user_input_pipe_init; - gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init; + random_source = new RandomSource() + get_user_input = new GetUserInput() + update_graphics = new UpdateGraphics() + random_source.out -> update_graphics.prompt + get_user_input.user_input -> update_graphics.user_input + update_graphics.another -> random_source.another + gui = new StartGui() + gui.user_input_pipe -> get_user_input.user_input_pipe_init; + gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init; } diff --git a/Python/src/TrainDoor/TrainDoor.lf b/Python/src/TrainDoor/TrainDoor.lf index c174db79..d26e6c35 100644 --- a/Python/src/TrainDoor/TrainDoor.lf +++ b/Python/src/TrainDoor/TrainDoor.lf @@ -8,99 +8,99 @@ target Python reactor MotionDetector { - preamble {= - import threading - def listen_for_input(self): - print("***************************************************************") - print("Press 'o' and hit return or enter to open the door") - print("Press 'c' and hit return or enter to close the door") - print("Press 'm' and hit return or enter perturb the motion sensor") - print("Press 'Control-d' to exit") - - global move_action - global open_action - global close_action - - while 1: - try: - c = input("> ") - except EOFError: - request_stop() - return - if c == 'm': - move_action.schedule(0) - elif c == 'o': - open_action.schedule(0) - elif c == 'c': - close_action.schedule(0) - =} - physical action movement - state timestamp(0) - input check - output ok - reaction(startup) -> movement {= - global move_action - move_action = movement - - t = self.threading.Thread(target=self.listen_for_input) - t.start() - =} - reaction(movement) {= - print("Motion detected!") - self.timestamp = lf.time.logical_elapsed() - =} - reaction(check) -> ok {= - if self.timestamp == 0 or lf.time.logical_elapsed() - self.timestamp > SECS(2): - ok.set(True) - else: - ok.set(False) - =} + preamble {= + import threading + def listen_for_input(self): + print("***************************************************************") + print("Press 'o' and hit return or enter to open the door") + print("Press 'c' and hit return or enter to close the door") + print("Press 'm' and hit return or enter perturb the motion sensor") + print("Press 'Control-d' to exit") + + global move_action + global open_action + global close_action + + while 1: + try: + c = input("> ") + except EOFError: + request_stop() + return + if c == 'm': + move_action.schedule(0) + elif c == 'o': + open_action.schedule(0) + elif c == 'c': + close_action.schedule(0) + =} + physical action movement + state timestamp(0) + input check + output ok + reaction(startup) -> movement {= + global move_action + move_action = movement + + t = self.threading.Thread(target=self.listen_for_input) + t.start() + =} + reaction(movement) {= + print("Motion detected!") + self.timestamp = lf.time.logical_elapsed() + =} + reaction(check) -> ok {= + if self.timestamp == 0 or lf.time.logical_elapsed() - self.timestamp > SECS(2): + ok.set(True) + else: + ok.set(False) + =} } reactor DoorController { - - physical action open - physical action close - - output check - input ok - state opened(False) - state requested(False) - reaction(startup) -> open, close {= - global open_action - open_action = open - global close_action - close_action = close - =} - - reaction(open) -> check {= - if self.opened: - print("The door is already open") - else: - print("Checking the motion sensor") - check.set(False) - self.requested = True - =} - - reaction(close) {= - print("Closing the door") - self.opened = False - =} - - reaction(ok) {= - if self.requested and ok.value: - self.opened = True - print("Opening the door.") - else: - print("Cannot open the door recent motion detected.") + + physical action open + physical action close + + output check + input ok + state opened(False) + state requested(False) + reaction(startup) -> open, close {= + global open_action + open_action = open + global close_action + close_action = close + =} + + reaction(open) -> check {= + if self.opened: + print("The door is already open") + else: + print("Checking the motion sensor") + check.set(False) + self.requested = True + =} + + reaction(close) {= + print("Closing the door") + self.opened = False + =} + + reaction(ok) {= + if self.requested and ok.value: + self.opened = True + print("Opening the door.") + else: + print("Cannot open the door recent motion detected.") - self.requested = False - =} + self.requested = False + =} } main reactor TrainDoor { - motion = new MotionDetector() - door = new DoorController() - door.check -> motion.check - motion.ok -> door.ok + motion = new MotionDetector() + door = new DoorController() + door.check -> motion.check + motion.ok -> door.ok } diff --git a/Python/src/YOLOv5/YOLOv5_Webcam.lf b/Python/src/YOLOv5/YOLOv5_Webcam.lf index 15ee3f3b..5aafd433 100644 --- a/Python/src/YOLOv5/YOLOv5_Webcam.lf +++ b/Python/src/YOLOv5/YOLOv5_Webcam.lf @@ -7,7 +7,7 @@ target Python; preamble {= - BILLION = 1_000_000_000 + BILLION = 1_000_000_000 =} /** @@ -20,48 +20,48 @@ preamble {= * according your the local setup. */ reactor WebCam(webcam_id(0)) { - output camera_frame - state stream - state video_capture_thread - state thread_should_be_running - physical action frame_action - preamble {= - from cv2 import cv2 - import threading - - def video_capture(self, frame_action, running): - # Read a frame - ret, frame = self.stream.read() - while running.is_set(): - if ret is True: - # If got a frame, schedule the physical action - frame_action.schedule(0, (lf.time.physical_elapsed(), frame)) - ret, frame = self.stream.read() - return None - =} - reaction(startup) -> frame_action {= - self.stream = self.cv2.VideoCapture(self.webcam_id, self.cv2.CAP_ANY) - if (self.stream.isOpened() is not True): - sys.stderr.write("Error: Failed to capture from the webcam.\n") - exit(1) - - self.stream.set(self.cv2.CAP_PROP_FPS, 30) # Set the camera's FPS to 30 - - self.thread_should_be_running = self.threading.Event() - self.thread_should_be_running.set() - - self.video_capture_thread = self.threading.Thread(target=self.video_capture, args=(frame_action, self.thread_should_be_running)) - self.video_capture_thread.start() - =} - reaction(frame_action) -> camera_frame {= - camera_frame.set(frame_action.value) - =} + output camera_frame + state stream + state video_capture_thread + state thread_should_be_running + physical action frame_action + preamble {= + from cv2 import cv2 + import threading - reaction(shutdown) {= - self.thread_should_be_running.clear() - self.video_capture_thread.join() - self.stream.release() - =} + def video_capture(self, frame_action, running): + # Read a frame + ret, frame = self.stream.read() + while running.is_set(): + if ret is True: + # If got a frame, schedule the physical action + frame_action.schedule(0, (lf.time.physical_elapsed(), frame)) + ret, frame = self.stream.read() + return None + =} + reaction(startup) -> frame_action {= + self.stream = self.cv2.VideoCapture(self.webcam_id, self.cv2.CAP_ANY) + if (self.stream.isOpened() is not True): + sys.stderr.write("Error: Failed to capture from the webcam.\n") + exit(1) + + self.stream.set(self.cv2.CAP_PROP_FPS, 30) # Set the camera's FPS to 30 + + self.thread_should_be_running = self.threading.Event() + self.thread_should_be_running.set() + + self.video_capture_thread = self.threading.Thread(target=self.video_capture, args=(frame_action, self.thread_should_be_running)) + self.video_capture_thread.start() + =} + reaction(frame_action) -> camera_frame {= + camera_frame.set(frame_action.value) + =} + + reaction(shutdown) {= + self.thread_should_be_running.clear() + self.video_capture_thread.join() + self.stream.release() + =} } /** @@ -70,43 +70,43 @@ reactor WebCam(webcam_id(0)) { * (where each label/object is on the frame). */ reactor DNN { - // Image input frame - input frame - - // Label outputs - output labels - // Label coordinates - output label_coordinates - // Send the model to anyone who's interested - output model - - state _model # The DNN model - state _device # The device to use (e.g., cpu or cuda) - preamble {= - import torch - from torch import hub - =} - reaction(startup) -> model {= - # Load YOLOv5 - self._model = self.torch.hub.load("ultralytics/yolov5", "yolov5s", pretrained=True) - # Find out if CUDA is supported - self._device = "cuda" if self.torch.cuda.is_available() else 'cpu' - # Send the model to device - self._model.to(self._device) - # Send the model to whoever is interested (other reactors) - model.set(self._model) - =} - reaction(frame) -> labels, label_coordinates {= - _, frame_data = frame.value - # Convert the frame into a tuple - fr = [frame_data] - # Run the model on the frame - results = self._model(fr) - # Extract the labels - labels.set(results.xyxyn[0][:, -1].cpu().numpy()) - # Extract the coordinates for the label - label_coordinates.set(results.xyxyn[0][:, :-1].cpu().numpy()) - =} + // Image input frame + input frame + + // Label outputs + output labels + // Label coordinates + output label_coordinates + // Send the model to anyone who's interested + output model + + state _model # The DNN model + state _device # The device to use (e.g., cpu or cuda) + preamble {= + import torch + from torch import hub + =} + reaction(startup) -> model {= + # Load YOLOv5 + self._model = self.torch.hub.load("ultralytics/yolov5", "yolov5s", pretrained=True) + # Find out if CUDA is supported + self._device = "cuda" if self.torch.cuda.is_available() else 'cpu' + # Send the model to device + self._model.to(self._device) + # Send the model to whoever is interested (other reactors) + model.set(self._model) + =} + reaction(frame) -> labels, label_coordinates {= + _, frame_data = frame.value + # Convert the frame into a tuple + fr = [frame_data] + # Run the model on the frame + results = self._model(fr) + # Extract the labels + labels.set(results.xyxyn[0][:, -1].cpu().numpy()) + # Extract the coordinates for the label + label_coordinates.set(results.xyxyn[0][:, :-1].cpu().numpy()) + =} } /** @@ -114,98 +114,98 @@ reactor DNN { * each object in the frame. */ reactor Plotter(label_deadline(100 msec)) { - input frame - input labels - input label_coordinates - input model - state _model # Keep the model - state _prev_time(0); + input frame + input labels + input label_coordinates + input model + state _model # Keep the model + state _prev_time(0); - preamble {= - from cv2 import cv2 - =} + preamble {= + from cv2 import cv2 + =} + + /** + * Receive the DNN model + */ + reaction(model) {= + self._model = model.value + print("\n******* Press 'q' to exit *******\n") + =} + + /** + * Impose a deadline on object labels + */ + reaction(labels) {= + # DNN output was on time + =} deadline(label_deadline) {= + print(f"Received the DNN output late by about {(lf.time.physical() - lf.time.logical())/1000000}ms.") + =} + + /** + * Given a frame, object labels, and the corresponding + * object label coordinates, draw an interactive OpenCV window. + */ + reaction(frame, labels, label_coordinates) {= + if (not frame.is_present or + not labels.is_present or + not label_coordinates.is_present): + sys.stderr.write("Error: Expected all inputs to be present at the same time.\n") + request_stop() - /** - * Receive the DNN model - */ - reaction(model) {= - self._model = model.value - print("\n******* Press 'q' to exit *******\n") - =} + elapsed_time, frame_data = frame.value + # Get how many labels we have + n = len(labels.value) + x_shape, y_shape = frame_data.shape[1], frame_data.shape[0] + for i in range(n): + row = label_coordinates.value[i] + # If score is less than 0.2 we avoid making a prediction. + if row[4] < 0.2: + continue + x1 = int(row[0]*x_shape) + y1 = int(row[1]*y_shape) + x2 = int(row[2]*x_shape) + y2 = int(row[3]*y_shape) + bgr = (0, 255, 0) # color of the box + classes = self._model.names # Get the name of label index + label_font = self.cv2.FONT_HERSHEY_SIMPLEX #Font for the label. + self.cv2.rectangle(frame_data, \ + (x1, y1), (x2, y2), \ + bgr, 2) #Plot the boxes + self.cv2.putText(frame_data,\ + classes[int(labels.value[i])], \ + (x1, y1), \ + label_font, 0.9, bgr, 2) #Put a label over box. - /** - * Impose a deadline on object labels - */ - reaction(labels) {= - # DNN output was on time - =} deadline(label_deadline) {= - print(f"Received the DNN output late by about {(lf.time.physical() - lf.time.logical())/1000000}ms.") - =} - - /** - * Given a frame, object labels, and the corresponding - * object label coordinates, draw an interactive OpenCV window. - */ - reaction(frame, labels, label_coordinates) {= - if (not frame.is_present or - not labels.is_present or - not label_coordinates.is_present): - sys.stderr.write("Error: Expected all inputs to be present at the same time.\n") - request_stop() - - elapsed_time, frame_data = frame.value - # Get how many labels we have - n = len(labels.value) - x_shape, y_shape = frame_data.shape[1], frame_data.shape[0] - for i in range(n): - row = label_coordinates.value[i] - # If score is less than 0.2 we avoid making a prediction. - if row[4] < 0.2: - continue - x1 = int(row[0]*x_shape) - y1 = int(row[1]*y_shape) - x2 = int(row[2]*x_shape) - y2 = int(row[3]*y_shape) - bgr = (0, 255, 0) # color of the box - classes = self._model.names # Get the name of label index - label_font = self.cv2.FONT_HERSHEY_SIMPLEX #Font for the label. - self.cv2.rectangle(frame_data, \ - (x1, y1), (x2, y2), \ - bgr, 2) #Plot the boxes - self.cv2.putText(frame_data,\ - classes[int(labels.value[i])], \ - (x1, y1), \ - label_font, 0.9, bgr, 2) #Put a label over box. - - fps = int(1 / (elapsed_time / BILLION - self._prev_time / BILLION)) - self._prev_time = elapsed_time - self.cv2.putText(frame_data, str(fps), (7, 70), - self.cv2.FONT_HERSHEY_SIMPLEX, 3, - (100, 255, 0), 3, self.cv2.LINE_AA) - self.cv2.imshow("frame", frame_data) - # press 'Q' if you want to exit - if self.cv2.waitKey(1) & 0xFF == ord('q'): - request_stop() - =} - - reaction(shutdown) {= - # Destroy the all windows now - self.cv2.destroyAllWindows() - =} + fps = int(1 / (elapsed_time / BILLION - self._prev_time / BILLION)) + self._prev_time = elapsed_time + self.cv2.putText(frame_data, str(fps), (7, 70), + self.cv2.FONT_HERSHEY_SIMPLEX, 3, + (100, 255, 0), 3, self.cv2.LINE_AA) + self.cv2.imshow("frame", frame_data) + # press 'Q' if you want to exit + if self.cv2.waitKey(1) & 0xFF == ord('q'): + request_stop() + =} + + reaction(shutdown) {= + # Destroy the all windows now + self.cv2.destroyAllWindows() + =} } main reactor { - webcam = new WebCam() - dnn = new DNN() - plotter = new Plotter() - - // Send the camera frame to the DNN to be process and to the plotter to be depicted - (webcam.camera_frame)+ -> dnn.frame, plotter.frame - // Send outputs of the DNN (object labels and their coordinates) to the plotter - dnn.labels, dnn.label_coordinates -> plotter.labels, plotter.label_coordinates - - // Send the DNN model to the plotter. It will be used to extract the human-readable names - // of each label. - dnn.model -> plotter.model - + webcam = new WebCam() + dnn = new DNN() + plotter = new Plotter() + + // Send the camera frame to the DNN to be process and to the plotter to be depicted + (webcam.camera_frame)+ -> dnn.frame, plotter.frame + // Send outputs of the DNN (object labels and their coordinates) to the plotter + dnn.labels, dnn.label_coordinates -> plotter.labels, plotter.label_coordinates + + // Send the DNN model to the plotter. It will be used to extract the human-readable names + // of each label. + dnn.model -> plotter.model + } diff --git a/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf b/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf index cd40843b..fe6ee5fd 100644 --- a/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf +++ b/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf @@ -20,44 +20,44 @@ import DNN, Plotter from "YOLOv5_Webcam.lf" * according to your local setup. */ reactor WebCam { - output camera_frame - state stream - state video_capture_thread - state thread_should_be_running - - preamble {= - import cv2 - =} - reaction(startup) {= - self.stream = self.cv2.VideoCapture(0, self.cv2.CAP_ANY) - if (self.stream.isOpened() is not True): - sys.stderr.write("Error: Failed to capture from the webcam.\n") - exit(1) - - self.stream.set(self.cv2.CAP_PROP_FPS, 30) # Set the camera's FPS to 30 - =} - - timer camera_tick(3 sec, 100 msec); - - reaction(camera_tick) -> camera_frame {= - ret, frame = self.stream.read() - if ret is True: - camera_frame.set((lf.time.physical_elapsed(), frame)) - =} - - reaction(shutdown) {= - self.stream.release() - =} + output camera_frame + state stream + state video_capture_thread + state thread_should_be_running + + preamble {= + import cv2 + =} + reaction(startup) {= + self.stream = self.cv2.VideoCapture(0, self.cv2.CAP_ANY) + if (self.stream.isOpened() is not True): + sys.stderr.write("Error: Failed to capture from the webcam.\n") + exit(1) + + self.stream.set(self.cv2.CAP_PROP_FPS, 30) # Set the camera's FPS to 30 + =} + + timer camera_tick(3 sec, 100 msec); + + reaction(camera_tick) -> camera_frame {= + ret, frame = self.stream.read() + if ret is True: + camera_frame.set((lf.time.physical_elapsed(), frame)) + =} + + reaction(shutdown) {= + self.stream.release() + =} } main reactor { - webcam = new WebCam() - dnn = new DNN() - plotter = new Plotter(label_deadline = 100 msec) - - (webcam.camera_frame)+ -> dnn.frame, plotter.frame - dnn.labels, dnn.label_coordinates -> plotter.labels, plotter.label_coordinates - - dnn.model -> plotter.model - -} \ No newline at end of file + webcam = new WebCam() + dnn = new DNN() + plotter = new Plotter(label_deadline = 100 msec) + + (webcam.camera_frame)+ -> dnn.frame, plotter.frame + dnn.labels, dnn.label_coordinates -> plotter.labels, plotter.label_coordinates + + dnn.model -> plotter.model + +} diff --git a/Python/src/acas/ACASXu.lf b/Python/src/acas/ACASXu.lf index 98fcca15..4a67b839 100644 --- a/Python/src/acas/ACASXu.lf +++ b/Python/src/acas/ACASXu.lf @@ -18,7 +18,7 @@ * The original Python code on which this is based was provided by * Arthur Clavière and can be found at: * - * https://svn.onera.fr/schedmcore/branches/ACAS_CaseStudy/ + * https://svn.onera.fr/schedmcore/branches/ACAS_CaseStudy/ * * Since that original code bears an LGPL license, so does this program: * @@ -28,9 +28,9 @@ * # Prerequisites * * ``` - * pip install wheel - * pip install pandas - * pip install matplotlib + * pip install wheel + * pip install pandas + * pip install matplotlib * ``` * * @author Arthur Clavière @@ -38,8 +38,8 @@ * @author Claire Pagetti */ target Python { - fast: true, - timeout: 16 s + fast: true, + timeout: 16 s } import XYPlotter from "lib/XYPlotter.lf" import Aircraft from "lib/Aircraft.lf" @@ -48,48 +48,48 @@ import ACASController from "lib/ACASController.lf" # Calculate the difference between two x-y positions (x1,y1) - (x2, y2). # This assumes that if one input is present, all are present. reactor Diff { - input x1 - input y1 - input x2 - input y2 - output x - output y - reaction(x1, y1, x2, y2) -> x, y {= - x.set(x1.value - x2.value) - y.set(y1.value - y2.value) - =} + input x1 + input y1 + input x2 + input y2 + output x + output y + reaction(x1, y1, x2, y2) -> x, y {= + x.set(x1.value - x2.value) + y.set(y1.value - y2.value) + =} } main reactor { - own = new Aircraft( - x_init = 0.0, # Initial x position in feet. - y_init = 0.0, # Initial y position in feet. - psi_init = 0.0, # Angle in radians, relative to vertical, positive counterclockwise - v_init = 248.74685927665496, # Initial velocity, in feet/second. - period = 10 ms # Rate of updates. - ) - - intruder = new Aircraft( - x_init = -6000.0, - y_init = 0.0, - psi_init = -0.9851107833377457, - v_init = 450.0, - period = 10 ms - ) - controller = new ACASController(period = 1 s) - diff = new Diff() - - plot = new XYPlotter() + own = new Aircraft( + x_init = 0.0, # Initial x position in feet. + y_init = 0.0, # Initial y position in feet. + psi_init = 0.0, # Angle in radians, relative to vertical, positive counterclockwise + v_init = 248.74685927665496, # Initial velocity, in feet/second. + period = 10 ms # Rate of updates. + ) + + intruder = new Aircraft( + x_init = -6000.0, + y_init = 0.0, + psi_init = -0.9851107833377457, + v_init = 450.0, + period = 10 ms + ) + controller = new ACASController(period = 1 s) + diff = new Diff() + + plot = new XYPlotter() - intruder.x, intruder.y -> diff.x1, diff.y1 - own.x, own.y -> diff.x2, diff.y2 - - diff.x, diff.y -> controller.x, controller.y - own.psi, intruder.psi -> controller.psi_own, controller.psi_int - own.v, intruder.v -> controller.v_own, controller.v_int - - controller.command -> own.turn - - own.x, own.y -> plot.x1, plot.y1 - intruder.x, intruder.y -> plot.x2, plot.y2 + intruder.x, intruder.y -> diff.x1, diff.y1 + own.x, own.y -> diff.x2, diff.y2 + + diff.x, diff.y -> controller.x, controller.y + own.psi, intruder.psi -> controller.psi_own, controller.psi_int + own.v, intruder.v -> controller.v_own, controller.v_int + + controller.command -> own.turn + + own.x, own.y -> plot.x1, plot.y1 + intruder.x, intruder.y -> plot.x2, plot.y2 } diff --git a/Python/src/acas/ACASXu2.lf b/Python/src/acas/ACASXu2.lf index cb40c890..9317bd8b 100644 --- a/Python/src/acas/ACASXu2.lf +++ b/Python/src/acas/ACASXu2.lf @@ -19,9 +19,9 @@ * # Prerequisites * * ``` - * pip install wheel - * pip install pandas - * pip install matplotlib + * pip install wheel + * pip install pandas + * pip install matplotlib * ``` * * @author Arthur Clavière @@ -29,8 +29,8 @@ * @author Claire Pagetti */ target Python { - fast: true, - timeout: 16 s + fast: true, + timeout: 16 s } import XYPlotter from "lib/XYPlotter.lf" import Aircraft from "lib/Aircraft.lf" @@ -39,60 +39,60 @@ import ACASController from "lib/ACASController.lf" # Calculate the difference between two x-y positions (x1,y1) - (x2, y2). # This assumes that if one input is present, all are present. reactor Diff { - input x1 - input y1 - input x2 - input y2 - output x - output y - reaction(x1, y1, x2, y2) -> x, y {= - x.set(x1.value - x2.value) - y.set(y1.value - y2.value) - =} + input x1 + input y1 + input x2 + input y2 + output x + output y + reaction(x1, y1, x2, y2) -> x, y {= + x.set(x1.value - x2.value) + y.set(y1.value - y2.value) + =} } main reactor { - own = new Aircraft( - x_init = 0.0, # Initial x position in feet. - y_init = 0.0, # Initial y position in feet. - psi_init = 0.0, # Angle in radians, relative to vertical, positive counterclockwise - v_init = 248.74685927665496, # Initial velocity, in feet/second. - period = 10 ms # Rate of updates. - ) - - intruder = new Aircraft( - x_init = -6000.0, - y_init = 0.0, - psi_init = -0.9851107833377457, - v_init = 450.0, - period = 10 ms - ) - controller1 = new ACASController(period = 1 s) - diff1 = new Diff() - - controller2 = new ACASController(period = 1 s) - diff2 = new Diff() - - plot = new XYPlotter() + own = new Aircraft( + x_init = 0.0, # Initial x position in feet. + y_init = 0.0, # Initial y position in feet. + psi_init = 0.0, # Angle in radians, relative to vertical, positive counterclockwise + v_init = 248.74685927665496, # Initial velocity, in feet/second. + period = 10 ms # Rate of updates. + ) + + intruder = new Aircraft( + x_init = -6000.0, + y_init = 0.0, + psi_init = -0.9851107833377457, + v_init = 450.0, + period = 10 ms + ) + controller1 = new ACASController(period = 1 s) + diff1 = new Diff() + + controller2 = new ACASController(period = 1 s) + diff2 = new Diff() + + plot = new XYPlotter() - intruder.x, intruder.y -> diff1.x1, diff1.y1 - own.x, own.y -> diff1.x2, diff1.y2 - - intruder.x, intruder.y -> diff2.x2, diff2.y2 - own.x, own.y -> diff2.x1, diff2.y1 - - diff1.x, diff1.y -> controller1.x, controller1.y - own.psi, intruder.psi -> controller1.psi_own, controller1.psi_int - own.v, intruder.v -> controller1.v_own, controller1.v_int - controller1.command -> own.turn - - # Reverse role of intruder and own for second aircraft. - diff2.x, diff2.y -> controller2.x, controller2.y - own.psi, intruder.psi -> controller2.psi_int, controller2.psi_own - own.v, intruder.v -> controller2.v_int, controller2.v_own - controller2.command -> intruder.turn - - - own.x, own.y -> plot.x1, plot.y1 - intruder.x, intruder.y -> plot.x2, plot.y2 + intruder.x, intruder.y -> diff1.x1, diff1.y1 + own.x, own.y -> diff1.x2, diff1.y2 + + intruder.x, intruder.y -> diff2.x2, diff2.y2 + own.x, own.y -> diff2.x1, diff2.y1 + + diff1.x, diff1.y -> controller1.x, controller1.y + own.psi, intruder.psi -> controller1.psi_own, controller1.psi_int + own.v, intruder.v -> controller1.v_own, controller1.v_int + controller1.command -> own.turn + + # Reverse role of intruder and own for second aircraft. + diff2.x, diff2.y -> controller2.x, controller2.y + own.psi, intruder.psi -> controller2.psi_int, controller2.psi_own + own.v, intruder.v -> controller2.v_int, controller2.v_own + controller2.command -> intruder.turn + + + own.x, own.y -> plot.x1, plot.y1 + intruder.x, intruder.y -> plot.x2, plot.y2 } diff --git a/Python/src/acas/lib/ACASController.lf b/Python/src/acas/lib/ACASController.lf index 7430587a..40cf5a1e 100644 --- a/Python/src/acas/lib/ACASController.lf +++ b/Python/src/acas/lib/ACASController.lf @@ -18,9 +18,9 @@ * # Prerequisites * * ``` - * pip install wheel - * pip install pandas - * pip install matplotlib + * pip install wheel + * pip install pandas + * pip install matplotlib * ``` * * @author Arthur Clavière @@ -28,111 +28,111 @@ * @author Claire Pagetti */ target Python { - fast: true, - timeout: 16 s + fast: true, + timeout: 16 s } import NN from "ACASNN.lf" preamble {= - import numpy as np - import math + import numpy as np + import math =} reactor PreProcessor( - nn_period(10 ms) + nn_period(10 ms) ) { - input x # x position of intruder relative to own - input y # y position of intruder relative to own - input psi_own # angle of own trajectory - input psi_int # angle of intruder trajectory - input v_own # speed of own trajectory - input v_int # speed of intruder trajectory + input x # x position of intruder relative to own + input y # y position of intruder relative to own + input psi_own # angle of own trajectory + input psi_int # angle of intruder trajectory + input v_own # speed of own trajectory + input v_int # speed of intruder trajectory - output vector # [rho, theta, psi, v_own, v_int] + output vector # [rho, theta, psi, v_own, v_int] + + timer t(0, nn_period) + + reaction(t) x, y, psi_own, psi_int, v_own, v_int -> vector {= - timer t(0, nn_period) + pi = math.pi - reaction(t) x, y, psi_own, psi_int, v_own, v_int -> vector {= - - pi = math.pi - - # 1) compute rho - rho = np.sqrt(x.value**2 + y.value**2) + # 1) compute rho + rho = np.sqrt(x.value**2 + y.value**2) + + # 2) compute theta + if y.value > 0: + angle = -np.arctan(x.value/y.value) + elif x.value < 0: + angle = pi/2 + np.arctan(y.value/x.value) + elif x.value > 0: + angle = -pi/2 + np.arctan(y.value/x.value) + elif x.value == 0: + angle = 0 if y.value==0 else pi + theta = angle - psi_own.value + # wrap theta into [-pi,pi] + while theta < -pi: + theta += 2*pi + while theta > pi: + theta -= 2*pi - # 2) compute theta - if y.value > 0: - angle = -np.arctan(x.value/y.value) - elif x.value < 0: - angle = pi/2 + np.arctan(y.value/x.value) - elif x.value > 0: - angle = -pi/2 + np.arctan(y.value/x.value) - elif x.value == 0: - angle = 0 if y.value==0 else pi - theta = angle - psi_own.value - # wrap theta into [-pi,pi] - while theta < -pi: - theta += 2*pi - while theta > pi: - theta -= 2*pi - - # 3) compute psi - psi = psi_int.value - psi_own.value - # wrap psi into [-pi,pi] - while psi < -pi: - psi += 2*pi - while psi > pi: - psi -= 2*pi - - vector.set(np.array([rho, theta, psi, v_own.value, v_int.value])) - =} + # 3) compute psi + psi = psi_int.value - psi_own.value + # wrap psi into [-pi,pi] + while psi < -pi: + psi += 2*pi + while psi > pi: + psi -= 2*pi + + vector.set(np.array([rho, theta, psi, v_own.value, v_int.value])) + =} } # Output is produced one microstep later. reactor PostProcessor( - initial_label(0), - available_commands({=[0.0,1.5,-1.5,3.5,-3.5]=}) # Angles in degrees/second + initial_label(0), + available_commands({=[0.0,1.5,-1.5,3.5,-3.5]=}) # Angles in degrees/second ) { - input score - output previous_label - output command - - logical action next - - reaction(startup) -> previous_label, command {= - previous_label.set(self.initial_label) - command.set(self.available_commands[self.initial_label]) - =} - reaction(next) -> previous_label, command {= - previous_label.set(next.value) - command.set(self.available_commands[next.value] * math.pi/180.0) - =} - reaction(score) -> next {= - next.schedule(0, np.argmin(score.value)) - =} + input score + output previous_label + output command + + logical action next + + reaction(startup) -> previous_label, command {= + previous_label.set(self.initial_label) + command.set(self.available_commands[self.initial_label]) + =} + reaction(next) -> previous_label, command {= + previous_label.set(next.value) + command.set(self.available_commands[next.value] * math.pi/180.0) + =} + reaction(score) -> next {= + next.schedule(0, np.argmin(score.value)) + =} } reactor ACASController( - period(1 s) + period(1 s) ) { - input x # x position of intruder relative to own - input y # y position of intruder relative to own - input psi_own # angle of own trajectory - input psi_int # angle of intruder trajectory - input v_own # speed of own trajectory - input v_int # speed of intruder trajectory + input x # x position of intruder relative to own + input y # y position of intruder relative to own + input psi_own # angle of own trajectory + input psi_int # angle of intruder trajectory + input v_own # speed of own trajectory + input v_int # speed of intruder trajectory - output command # Turn command for own in radians/sec + output command # Turn command for own in radians/sec - pre = new PreProcessor(nn_period = period) - nn = new NN() - post = new PostProcessor() - - x, y -> pre.x, pre.y - psi_own, psi_int -> pre.psi_own, pre.psi_int - v_own, v_int -> pre.v_own, pre.v_int - - pre.vector -> nn.vector - nn.score -> post.score - post.command -> command - post.previous_label -> nn.index + pre = new PreProcessor(nn_period = period) + nn = new NN() + post = new PostProcessor() + + x, y -> pre.x, pre.y + psi_own, psi_int -> pre.psi_own, pre.psi_int + v_own, v_int -> pre.v_own, pre.v_int + + pre.vector -> nn.vector + nn.score -> post.score + post.command -> command + post.previous_label -> nn.index } diff --git a/Python/src/acas/lib/ACASNN.lf b/Python/src/acas/lib/ACASNN.lf index b24cf015..3141df51 100644 --- a/Python/src/acas/lib/ACASNN.lf +++ b/Python/src/acas/lib/ACASNN.lf @@ -17,9 +17,9 @@ * # Prerequisites * * ``` - * pip install wheel - * pip install pandas - * pip install matplotlib + * pip install wheel + * pip install pandas + * pip install matplotlib * ``` * * @author Arthur Clavière @@ -27,60 +27,60 @@ * @author Claire Pagetti */ target Python { - files: ["../code/src/utils.py", "../code/src/mlmodels/ffnn.py", "../code/src/systems/acasxu/nnets/"] + files: ["../code/src/utils.py", "../code/src/mlmodels/ffnn.py", "../code/src/systems/acasxu/nnets/"] } preamble {= - import numpy as np - from utils import parse_nnet_format + import numpy as np + from utils import parse_nnet_format =} reactor NN( - prefix_nnet_names("nnet_acas_") + prefix_nnet_names("nnet_acas_") ) { - input vector - input index # Index of neural network to use. - - output score # A vector with six scores. - - state nnets({=[]=}) # Empty list initially. - state norm_parameters({={}=}) # Empty map initially. - state nnets_dict({={}=}) # Empty map initially. - - # Read and parse neural network definitions. - reaction(startup) {= - for i in range(1,6): - # retrieve the path to the i^th network - nnet_filename = "ACASXU_experimental_v2a_{}_1.nnet".format(i) - path_nnet = os.path.join(os.path.dirname(__file__), "nnets", nnet_filename) - # parse the network - (nnet, norm_params) = parse_nnet_format(path_nnet, "relu") - # name the network - nnet_name = self.prefix_nnet_names + str(i-1) - # append the network to the nnets list - self.nnets.append((nnet_name, nnet, path_nnet)) - # update the norm_parameters dictionary - self.norm_parameters[nnet_name] = norm_params + input vector + input index # Index of neural network to use. + + output score # A vector with six scores. + + state nnets({=[]=}) # Empty list initially. + state norm_parameters({={}=}) # Empty map initially. + state nnets_dict({={}=}) # Empty map initially. + + # Read and parse neural network definitions. + reaction(startup) {= + for i in range(1,6): + # retrieve the path to the i^th network + nnet_filename = "ACASXU_experimental_v2a_{}_1.nnet".format(i) + path_nnet = os.path.join(os.path.dirname(__file__), "nnets", nnet_filename) + # parse the network + (nnet, norm_params) = parse_nnet_format(path_nnet, "relu") + # name the network + nnet_name = self.prefix_nnet_names + str(i-1) + # append the network to the nnets list + self.nnets.append((nnet_name, nnet, path_nnet)) + # update the norm_parameters dictionary + self.norm_parameters[nnet_name] = norm_params - # Create a dictionary with the neural networks - for (nnet_name, nnet, path_nnet) in self.nnets: - self.nnets_dict[nnet_name] = nnet - =} + # Create a dictionary with the neural networks + for (nnet_name, nnet, path_nnet) in self.nnets: + self.nnets_dict[nnet_name] = nnet + =} + + reaction(vector) index -> score {= + + # (ii) select the neural network to be executed, depending on the index + nnet_name = self.prefix_nnet_names + str(index.value) - reaction(vector) index -> score {= + # Normalize the vector + norm_params = self.norm_parameters[nnet_name] + x_mean = norm_params[2] + x_range = norm_params[3] + x_norm = (vector.value - x_mean) / x_range - # (ii) select the neural network to be executed, depending on the index - nnet_name = self.prefix_nnet_names + str(index.value) + # (iv) evaluate the neural network + score.set(self.nnets_dict[nnet_name].compute_output(x_norm)) - # Normalize the vector - norm_params = self.norm_parameters[nnet_name] - x_mean = norm_params[2] - x_range = norm_params[3] - x_norm = (vector.value - x_mean) / x_range - - # (iv) evaluate the neural network - score.set(self.nnets_dict[nnet_name].compute_output(x_norm)) - - # score.set([1.0, 0.0, 1.0, 1.0, 1.0, 1.0]) - =} + # score.set([1.0, 0.0, 1.0, 1.0, 1.0, 1.0]) + =} } diff --git a/Python/src/acas/lib/Aircraft.lf b/Python/src/acas/lib/Aircraft.lf index 820d5bcb..f56df7f2 100644 --- a/Python/src/acas/lib/Aircraft.lf +++ b/Python/src/acas/lib/Aircraft.lf @@ -16,8 +16,8 @@ * # Prerequisites * * ``` - * pip install wheel - * pip install pandas + * pip install wheel + * pip install pandas * ``` * * @author Arthur Clavière @@ -27,48 +27,48 @@ target Python preamble {= - import math + import math =} reactor Aircraft( - x_init(0.0), # Initial x position in feet. - y_init(0.0), # Initial y position in feet. - psi_init(0.0), # Angle in radians, relative to vertical, positive counterclockwise - v_init(250.0), # Initial velocity, in feet/second. - period(10 ms) # Rate of updates. + x_init(0.0), # Initial x position in feet. + y_init(0.0), # Initial y position in feet. + psi_init(0.0), # Angle in radians, relative to vertical, positive counterclockwise + v_init(250.0), # Initial velocity, in feet/second. + period(10 ms) # Rate of updates. ) { - input turn # Angle change, in radians/second. Leave unconnected for no turns. - - # Outputs are used as state variables. - output x # x position - output y # y position - output psi # Angle in radians - output v # Velocity in feet/second - - state latest_turn(0.0) # Most recently received turn command. + input turn # Angle change, in radians/second. Leave unconnected for no turns. + + # Outputs are used as state variables. + output x # x position + output y # y position + output psi # Angle in radians + output v # Velocity in feet/second + + state latest_turn(0.0) # Most recently received turn command. - timer t(period, period) # Offset of period to not overwrite initial state. - - reaction(startup) -> x, y, psi, v {= - x.set(self.x_init) - y.set(self.y_init) - psi.set(self.psi_init) - v.set(self.v_init) - =} - reaction(turn) {= - self.latest_turn = turn.value - =} - reaction(t) -> x, y, psi, v {= - delta_t = self.period / 1e9 # Period is seconds - x.set( - x.value - delta_t * (v.value * np.sin(psi.value)) - ) - y.set( - y.value + delta_t * (v.value * np.cos(psi.value)) - ) - # Update the angle after updating the position. - psi.set( - psi.value + delta_t * self.latest_turn - ) - =} + timer t(period, period) # Offset of period to not overwrite initial state. + + reaction(startup) -> x, y, psi, v {= + x.set(self.x_init) + y.set(self.y_init) + psi.set(self.psi_init) + v.set(self.v_init) + =} + reaction(turn) {= + self.latest_turn = turn.value + =} + reaction(t) -> x, y, psi, v {= + delta_t = self.period / 1e9 # Period is seconds + x.set( + x.value - delta_t * (v.value * np.sin(psi.value)) + ) + y.set( + y.value + delta_t * (v.value * np.cos(psi.value)) + ) + # Update the angle after updating the position. + psi.set( + psi.value + delta_t * self.latest_turn + ) + =} } diff --git a/Python/src/acas/lib/XYPlotter.lf b/Python/src/acas/lib/XYPlotter.lf index 3da4bf1d..1df93b43 100644 --- a/Python/src/acas/lib/XYPlotter.lf +++ b/Python/src/acas/lib/XYPlotter.lf @@ -5,38 +5,38 @@ */ target Python preamble {= - import numpy as np - import matplotlib.pyplot as plt + import numpy as np + import matplotlib.pyplot as plt =} reactor XYPlotter { - input x1 - input y1 - input x2 - input y2 - - state x1_list({=[]=}) # collected x values - state y1_list({=[]=}) # collected y values - state x2_list({=[]=}) # collected x values - state y2_list({=[]=}) # collected y values + input x1 + input y1 + input x2 + input y2 + + state x1_list({=[]=}) # collected x values + state y1_list({=[]=}) # collected y values + state x2_list({=[]=}) # collected x values + state y2_list({=[]=}) # collected y values - reaction(x1, y1, x2, y2) {= - self.x1_list.append(x1.value) - self.y1_list.append(y1.value) - self.x2_list.append(x2.value) - self.y2_list.append(y2.value) - =} - reaction(shutdown) {= - # make data - x1 = np.array(self.x1_list) - y1 = np.array(self.y1_list) - x2 = np.array(self.x2_list) - y2 = np.array(self.y2_list) - - # plot - plt.plot(x1, y1, "b") - plt.plot(x2, y2, "r") - - plt.show() - =} + reaction(x1, y1, x2, y2) {= + self.x1_list.append(x1.value) + self.y1_list.append(y1.value) + self.x2_list.append(x2.value) + self.y2_list.append(y2.value) + =} + reaction(shutdown) {= + # make data + x1 = np.array(self.x1_list) + y1 = np.array(self.y1_list) + x2 = np.array(self.x2_list) + y2 = np.array(self.y2_list) + + # plot + plt.plot(x1, y1, "b") + plt.plot(x2, y2, "r") + + plt.show() + =} } diff --git a/Rust/src/CounterProgram.lf b/Rust/src/CounterProgram.lf index 55942cb5..67feb598 100644 --- a/Rust/src/CounterProgram.lf +++ b/Rust/src/CounterProgram.lf @@ -1,34 +1,34 @@ //! A simple counter program. The counter's stride and period //! may be configured using the CLI: -//! ./counter_program --main-stride 20 --main-period 500msec +//! ./counter_program --main-stride 20 --main-period 500msec //! //! Author: Clément Fournier target Rust { - timeout: 3 sec, - cargo-features: ["cli"] + timeout: 3 sec, + cargo-features: ["cli"] }; reactor Counter(stride: u32(1), period: time(1 sec)) { - state count: u32(0); - state stride(stride); - timer t(0, period); - output out: u32; - reaction(t) -> out {= - ctx.set(out, self.count); - self.count += self.stride; - =} + state count: u32(0); + state stride(stride); + timer t(0, period); + output out: u32; + reaction(t) -> out {= + ctx.set(out, self.count); + self.count += self.stride; + =} } reactor Printer { - input in: u32; - reaction(in) {= - if let Some(value) = ctx.get(r#in) { - println!("Hello World! value={}.", value); - } - =} + input in: u32; + reaction(in) {= + if let Some(value) = ctx.get(r#in) { + println!("Hello World! value={}.", value); + } + =} } main reactor CounterProgram(stride: u32(10), period: time(1 sec)) { - counter = new Counter(stride=stride, period=period); - printer = new Printer(); - counter.out -> printer.in; + counter = new Counter(stride=stride, period=period); + printer = new Printer(); + counter.out -> printer.in; } diff --git a/Rust/src/Snake/KeyboardEvents.lf b/Rust/src/Snake/KeyboardEvents.lf index d4034b2d..192d506b 100644 --- a/Rust/src/Snake/KeyboardEvents.lf +++ b/Rust/src/Snake/KeyboardEvents.lf @@ -11,61 +11,61 @@ //! Support reactor for the Snake.lf example. target Rust { - cargo-dependencies: { termion: "1", } + cargo-dependencies: { termion: "1", } }; /// Capture asynchronous key presses, and sends them through an output port. /// Used by the other examples. reactor KeyboardEvents { - preamble {= - use std::io::Stdout; + preamble {= + use std::io::Stdout; - use termion::event::Key; - use termion::raw::{RawTerminal, IntoRawMode}; - =} + use termion::event::Key; + use termion::raw::{RawTerminal, IntoRawMode}; + =} - /// The latest key press. - output arrow_key_pressed: Key; + /// The latest key press. + output arrow_key_pressed: Key; - physical action key_press: Key; + physical action key_press: Key; - state raw_terminal: Option>; + state raw_terminal: Option>; - reaction(key_press) -> arrow_key_pressed {= - ctx.set(arrow_key_pressed, ctx.get(key_press).unwrap()); - =} + reaction(key_press) -> arrow_key_pressed {= + ctx.set(arrow_key_pressed, ctx.get(key_press).unwrap()); + =} - reaction(shutdown) {= - drop(self.raw_terminal.take()) // exit raw mode - =} + reaction(shutdown) {= + drop(self.raw_terminal.take()) // exit raw mode + =} - reaction(startup) -> key_press {= + reaction(startup) -> key_press {= - let stdin = std::io::stdin(); + let stdin = std::io::stdin(); - // enter raw mode, to get key presses one by one - // this will stay so until this variable is dropped - self.raw_terminal = Some(std::io::stdout().into_raw_mode().unwrap()); + // enter raw mode, to get key presses one by one + // this will stay so until this variable is dropped + self.raw_terminal = Some(std::io::stdout().into_raw_mode().unwrap()); - let key_press = key_press.clone(); - ctx.spawn_physical_thread(move |link| { - use termion::input::TermRead; + let key_press = key_press.clone(); + ctx.spawn_physical_thread(move |link| { + use termion::input::TermRead; - for c in stdin.keys() { - match c.unwrap() { - k@(Key::Left | Key::Right | Key::Up | Key::Down) => { - trace!("received {:?}", k); - link.schedule_physical_with_v(&key_press, Some(k), Asap).ok(); - }, - Key::Ctrl('c') => { - link.request_stop(Asap).unwrap(); - break; - }, - k => { - trace!("received {:?}", k); - } - } - } - }); - =} + for c in stdin.keys() { + match c.unwrap() { + k@(Key::Left | Key::Right | Key::Up | Key::Down) => { + trace!("received {:?}", k); + link.schedule_physical_with_v(&key_press, Some(k), Asap).ok(); + }, + Key::Ctrl('c') => { + link.request_stop(Asap).unwrap(); + break; + }, + k => { + trace!("received {:?}", k); + } + } + } + }); + =} } diff --git a/Rust/src/Snake/Snake.lf b/Rust/src/Snake/Snake.lf index 6f90d518..c36b32af 100644 --- a/Rust/src/Snake/Snake.lf +++ b/Rust/src/Snake/Snake.lf @@ -17,17 +17,17 @@ //! under the path examples/src/Snake.lf target Rust { - // LF-Rust programs integrate well with Cargo - cargo-dependencies: { - termcolor: "1", - termion: "1", // (this doesn't support windows) - rand: "0.8", - }, - // This will be linked into the root of the crate as a Rust module: `pub mod snakes;` - rust-include: "snakes.rs", - // This is a conditional compilation flag that enables the CLI. - // Without it, command-line arguments are ignored and produce a warning. - cargo-features: ["cli"], + // LF-Rust programs integrate well with Cargo + cargo-dependencies: { + termcolor: "1", + termion: "1", // (this doesn't support windows) + rand: "0.8", + }, + // This will be linked into the root of the crate as a Rust module: `pub mod snakes;` + rust-include: "snakes.rs", + // This is a conditional compilation flag that enables the CLI. + // Without it, command-line arguments are ignored and produce a warning. + cargo-features: ["cli"], }; // Import a shared reactor @@ -36,108 +36,108 @@ import KeyboardEvents from "KeyboardEvents.lf"; // main reactor parameters can be set on the CLI, eg: // ./snake --main-grid-side 48 main reactor Snake(grid_side: usize(32), - tempo_step: time(40 msec), - food_limit: u32(2)) { - preamble {= - use crate::snakes::*; - use crate::snakes; - use termion::event::Key; - use rand::prelude::*; - =} - - /// this thing helps capturing key presses - keyboard = new KeyboardEvents(); - - // model classes for the game. - state snake: CircularSnake ({= CircularSnake::new(grid_side) =}); - state grid: SnakeGrid ({= SnakeGrid::new(grid_side, &snake) =}); // note that this one borrows snake temporarily - - /// Triggers a screen refresh, not a timer because we can - /// shrink the period over time to speed up the game. - logical action screen_refresh; - /// The game speed level - state tempo: u32(1); - state tempo_step(tempo_step); - - /// Changes with arrow key presses, might be invalid. - /// Only committed to snake_direction on grid update. - state pending_direction: Direction ({= Direction::RIGHT =}); - /// Whither the snake has slithered last - state snake_direction: Direction ({= Direction::RIGHT =}); - - /// manually triggered - logical action manually_add_more_food; - /// periodic - timer add_more_food(0, 5 sec); - // state vars for food - state food_on_grid: u32(0); - state max_food_on_grid(food_limit); - - // @label startup - reaction(startup) -> screen_refresh {= - // KeyboardEvents makes stdout raw on startup so this is safe - snakes::output::paint_on_raw_console(&self.grid); - - // schedule the first one, then it reschedules itself. - ctx.schedule(screen_refresh, after!(1 sec)); - =} - - // @label schedule_next_tick - reaction(screen_refresh) -> screen_refresh {= - // select a delay depending on the tempo - let delay = delay!(400 ms) - (self.tempo_step * self.tempo).min(delay!(300 ms)); - - ctx.schedule(screen_refresh, After(delay)); - =} - - // @label refresh_screen - reaction(screen_refresh) -> manually_add_more_food {= - // check that the user's command is valid - if self.pending_direction != self.snake_direction.opposite() { - self.snake_direction = self.pending_direction; + tempo_step: time(40 msec), + food_limit: u32(2)) { + preamble {= + use crate::snakes::*; + use crate::snakes; + use termion::event::Key; + use rand::prelude::*; + =} + + /// this thing helps capturing key presses + keyboard = new KeyboardEvents(); + + // model classes for the game. + state snake: CircularSnake ({= CircularSnake::new(grid_side) =}); + state grid: SnakeGrid ({= SnakeGrid::new(grid_side, &snake) =}); // note that this one borrows snake temporarily + + /// Triggers a screen refresh, not a timer because we can + /// shrink the period over time to speed up the game. + logical action screen_refresh; + /// The game speed level + state tempo: u32(1); + state tempo_step(tempo_step); + + /// Changes with arrow key presses, might be invalid. + /// Only committed to snake_direction on grid update. + state pending_direction: Direction ({= Direction::RIGHT =}); + /// Whither the snake has slithered last + state snake_direction: Direction ({= Direction::RIGHT =}); + + /// manually triggered + logical action manually_add_more_food; + /// periodic + timer add_more_food(0, 5 sec); + // state vars for food + state food_on_grid: u32(0); + state max_food_on_grid(food_limit); + + // @label startup + reaction(startup) -> screen_refresh {= + // KeyboardEvents makes stdout raw on startup so this is safe + snakes::output::paint_on_raw_console(&self.grid); + + // schedule the first one, then it reschedules itself. + ctx.schedule(screen_refresh, after!(1 sec)); + =} + + // @label schedule_next_tick + reaction(screen_refresh) -> screen_refresh {= + // select a delay depending on the tempo + let delay = delay!(400 ms) - (self.tempo_step * self.tempo).min(delay!(300 ms)); + + ctx.schedule(screen_refresh, After(delay)); + =} + + // @label refresh_screen + reaction(screen_refresh) -> manually_add_more_food {= + // check that the user's command is valid + if self.pending_direction != self.snake_direction.opposite() { + self.snake_direction = self.pending_direction; + } + + match self.snake.slither_forward(self.snake_direction, &mut self.grid) { + UpdateResult::GameOver => { ctx.request_stop(Asap); return; }, + UpdateResult::FoodEaten => { + self.food_on_grid -= 1; + if self.food_on_grid == 0 { + ctx.schedule(manually_add_more_food, Asap); } - - match self.snake.slither_forward(self.snake_direction, &mut self.grid) { - UpdateResult::GameOver => { ctx.request_stop(Asap); return; }, - UpdateResult::FoodEaten => { - self.food_on_grid -= 1; - if self.food_on_grid == 0 { - ctx.schedule(manually_add_more_food, Asap); - } - self.tempo += 1; - }, - UpdateResult::NothingInParticular => {/* do nothing in particular. */} - } - - snakes::output::paint_on_raw_console(&self.grid); - =} - - // @label handle_key_press - reaction(keyboard.arrow_key_pressed) {= - // this might be overwritten several times, only committed on screen refreshes - self.pending_direction = match ctx.get(keyboard__arrow_key_pressed).unwrap() { - Key::Left => Direction::LEFT, - Key::Right => Direction::RIGHT, - Key::Up => Direction::UP, - Key::Down => Direction::DOWN, - _ => unreachable!(), - }; - =} - - // @label add_food - reaction(manually_add_more_food, add_more_food) {= - if self.food_on_grid >= self.max_food_on_grid { - return; // there's enough food there - } - - if let Some(cell) = self.grid.find_random_free_cell() { - self.grid[cell] = CellState::Food; // next screen update will catch this. - self.food_on_grid += 1; - } - =} - - // @label shutdown - reaction(shutdown) {= - println!("New high score: {}", self.snake.len()); - =} + self.tempo += 1; + }, + UpdateResult::NothingInParticular => {/* do nothing in particular. */} + } + + snakes::output::paint_on_raw_console(&self.grid); + =} + + // @label handle_key_press + reaction(keyboard.arrow_key_pressed) {= + // this might be overwritten several times, only committed on screen refreshes + self.pending_direction = match ctx.get(keyboard__arrow_key_pressed).unwrap() { + Key::Left => Direction::LEFT, + Key::Right => Direction::RIGHT, + Key::Up => Direction::UP, + Key::Down => Direction::DOWN, + _ => unreachable!(), + }; + =} + + // @label add_food + reaction(manually_add_more_food, add_more_food) {= + if self.food_on_grid >= self.max_food_on_grid { + return; // there's enough food there + } + + if let Some(cell) = self.grid.find_random_free_cell() { + self.grid[cell] = CellState::Food; // next screen update will catch this. + self.food_on_grid += 1; + } + =} + + // @label shutdown + reaction(shutdown) {= + println!("New high score: {}", self.snake.len()); + =} } diff --git a/TypeScript/src/ChatApplication/SimpleChat.lf b/TypeScript/src/ChatApplication/SimpleChat.lf index 04114aef..37199d82 100644 --- a/TypeScript/src/ChatApplication/SimpleChat.lf +++ b/TypeScript/src/ChatApplication/SimpleChat.lf @@ -6,59 +6,59 @@ */ target TypeScript { - coordination-options: {advance-message-interval: 100 msec} + coordination-options: {advance-message-interval: 100 msec} } reactor InputHandler { - output out:string; - physical action response; + output out:string; + physical action response; - preamble {= - import * as readline from "readline"; - =} + preamble {= + import * as readline from "readline"; + =} - reaction(startup, response) -> out, response {= - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); + reaction(startup, response) -> out, response {= + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); - if (response !== undefined) { - out = response as string; - } + if (response !== undefined) { + out = response as string; + } - rl.question("Enter message to send: ", (buf) => { - actions.response.schedule(0, buf as string); - rl.close(); - }); - =} + rl.question("Enter message to send: ", (buf) => { + actions.response.schedule(0, buf as string); + rl.close(); + }); + =} } reactor Printer { - input inp:string; + input inp:string; - reaction(inp) {= - console.log("Received: " + inp); - =} + reaction(inp) {= + console.log("Received: " + inp); + =} } reactor ChatHandler { - input receive:string; - output send:string; - u = new InputHandler(); - p = new Printer(); - - reaction(u.out) -> send {= - send = u.out; - =} - reaction(receive) -> p.inp {= - p.inp = receive; - =} + input receive:string; + output send:string; + u = new InputHandler(); + p = new Printer(); + + reaction(u.out) -> send {= + send = u.out; + =} + reaction(receive) -> p.inp {= + p.inp = receive; + =} } federated reactor SimpleChat { - a = new ChatHandler(); - b = new ChatHandler(); - b.send -> a.receive; - a.send -> b.receive; + a = new ChatHandler(); + b = new ChatHandler(); + b.send -> a.receive; + a.send -> b.receive; } diff --git a/TypeScript/src/DistributedHelloWorld/HelloWorld.lf b/TypeScript/src/DistributedHelloWorld/HelloWorld.lf index 4dcf7c5b..27bb26b1 100644 --- a/TypeScript/src/DistributedHelloWorld/HelloWorld.lf +++ b/TypeScript/src/DistributedHelloWorld/HelloWorld.lf @@ -6,9 +6,9 @@ * The code generator generates two TypeScript files: * * * src-gen/DistrubtedHelloWorld/HelloWorld/src/DistrubtedHelloWorld_source.ts: - * The program that produces the sequence of messages. + * The program that produces the sequence of messages. * * src-gen/DistrubtedHelloWorld/HelloWorld/src/DistrubtedHelloWorld_print.ts: - * The program that prints the sequence of messages received from source. + * The program that prints the sequence of messages received from source. * * The code generator also creates compiled JavaScript files out of TypeScript * files above, respectively. @@ -29,7 +29,7 @@ * @author Hokeun Kim */ target TypeScript { - timeout: 10 secs + timeout: 10 secs }; /** @@ -40,17 +40,17 @@ target TypeScript { * @output message The message. */ reactor MessageGenerator(root:string("")) { - // Output type char* instead of string is used for dynamically - // allocated character arrays (as opposed to static constant strings). - output message:string; - state count:number(1); - // Send first message after 1 sec so that the startup reactions - // do not factor into the transport time measurement on the first message. - timer t(1 sec, 1 sec); - reaction(t) -> message {= - message = root + " " + count++; - console.log(`At time (elapsed) logical tag ${util.getElapsedLogicalTime()}, source sends message: ${message}`); - =} + // Output type char* instead of string is used for dynamically + // allocated character arrays (as opposed to static constant strings). + output message:string; + state count:number(1); + // Send first message after 1 sec so that the startup reactions + // do not factor into the transport time measurement on the first message. + timer t(1 sec, 1 sec); + reaction(t) -> message {= + message = root + " " + count++; + console.log(`At time (elapsed) logical tag ${util.getElapsedLogicalTime()}, source sends message: ${message}`); + =} } /** @@ -59,14 +59,14 @@ reactor MessageGenerator(root:string("")) { * @input message The message. */ reactor PrintMessage { - input message:string; - reaction(message) {= - console.log(`PrintMessage: At (elapsed) logical time ${util.getElapsedLogicalTime()}, receiver receives: ${message}`); - =} + input message:string; + reaction(message) {= + console.log(`PrintMessage: At (elapsed) logical time ${util.getElapsedLogicalTime()}, receiver receives: ${message}`); + =} } federated reactor HelloWorld { - source = new MessageGenerator(root = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; -} \ No newline at end of file + source = new MessageGenerator(root = "Hello World"); + print = new PrintMessage(); + source.message -> print.message; +} diff --git a/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf b/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf index 7a7fc3dc..1743d952 100644 --- a/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf +++ b/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf @@ -9,36 +9,36 @@ * asynchronous network events in a timely manner. */ target TypeScript{ - keepalive : true - //logging : "debug" + keepalive : true + //logging : "debug" }; main reactor { - preamble {= - import * as https from "https" - import * as http from "http" - =} - physical action data:{= Buffer =}; - physical action done; - reaction (startup) -> data, done {= - https.get("https://ptolemy.berkeley.edu/projects/icyphy/", (res : http.IncomingMessage) => { - console.log("statusCode:", res.statusCode); - console.log("headers:", res.headers); - res.on("data", (d : Buffer) => { - actions.data.schedule(0, d); - }); - res.on("end", () => { - actions.done.schedule(0, null); - }); - }); - =} - reaction (data) {= - let serverData = data; - if (serverData) { - console.log(serverData.toString()); - } - =} - reaction (done) {= - console.log("No more data in response."); - util.requestStop(); - =} + preamble {= + import * as https from "https" + import * as http from "http" + =} + physical action data:{= Buffer =}; + physical action done; + reaction (startup) -> data, done {= + https.get("https://ptolemy.berkeley.edu/projects/icyphy/", (res : http.IncomingMessage) => { + console.log("statusCode:", res.statusCode); + console.log("headers:", res.headers); + res.on("data", (d : Buffer) => { + actions.data.schedule(0, d); + }); + res.on("end", () => { + actions.done.schedule(0, null); + }); + }); + =} + reaction (data) {= + let serverData = data; + if (serverData) { + console.log(serverData.toString()); + } + =} + reaction (done) {= + console.log("No more data in response."); + util.requestStop(); + =} } diff --git a/TypeScript/src/HTTPServerReactor/HTTPServer.lf b/TypeScript/src/HTTPServerReactor/HTTPServer.lf index 50d28992..f2559344 100644 --- a/TypeScript/src/HTTPServerReactor/HTTPServer.lf +++ b/TypeScript/src/HTTPServerReactor/HTTPServer.lf @@ -10,42 +10,42 @@ * Lingua Franca framework. */ target TypeScript { - keepalive : true - //logging : "debug" - //timeout : 10 sec + keepalive : true + //logging : "debug" + //timeout : 10 sec }; main reactor { - preamble {= - import * as https from "https" - import * as http from "http" - =} - state count:number(0); - state server:{=http.Server | undefined=}({=undefined=}); - physical action serverRequest:{= [ http.IncomingMessage, http.ServerResponse ] =}; - reaction (startup) -> serverRequest {= - let options = {}; - server = http.createServer(options, (req : http.IncomingMessage, res : http.ServerResponse) => { - // Generally browsers make two requests, the first is for favicon.ico. - // See https://stackoverflow.com/questions/11961902/nodejs-http-createserver-seems-to-call-twice - if (req.url != "/favicon.ico") { - actions.serverRequest.schedule(0, [req, res]) - } - }).listen(8000); - =} - reaction (serverRequest) {= - let requestArray = serverRequest; - if (requestArray) { - let req = requestArray[0]; - let res = requestArray[1]; - res.writeHead(200); - count++; - res.write("ping count: " + count + "\n"); - res.end("hello world\n"); - } - =} - reaction (shutdown) {= - if (server) { - server.close(); - } - =} + preamble {= + import * as https from "https" + import * as http from "http" + =} + state count:number(0); + state server:{=http.Server | undefined=}({=undefined=}); + physical action serverRequest:{= [ http.IncomingMessage, http.ServerResponse ] =}; + reaction (startup) -> serverRequest {= + let options = {}; + server = http.createServer(options, (req : http.IncomingMessage, res : http.ServerResponse) => { + // Generally browsers make two requests, the first is for favicon.ico. + // See https://stackoverflow.com/questions/11961902/nodejs-http-createserver-seems-to-call-twice + if (req.url != "/favicon.ico") { + actions.serverRequest.schedule(0, [req, res]) + } + }).listen(8000); + =} + reaction (serverRequest) {= + let requestArray = serverRequest; + if (requestArray) { + let req = requestArray[0]; + let res = requestArray[1]; + res.writeHead(200); + count++; + res.write("ping count: " + count + "\n"); + res.end("hello world\n"); + } + =} + reaction (shutdown) {= + if (server) { + server.close(); + } + =} } diff --git a/TypeScript/src/SimpleWebserver.lf b/TypeScript/src/SimpleWebserver.lf index c6abc760..07c5fe71 100644 --- a/TypeScript/src/SimpleWebserver.lf +++ b/TypeScript/src/SimpleWebserver.lf @@ -8,38 +8,38 @@ * and closes upon the shutdown of the program. */ target TypeScript { - keepalive : true + keepalive : true }; main reactor { - preamble {= - import * as http from "http" - =} - state server:{=http.Server | undefined=}({=undefined=}); - physical action serverRequest:{= [http.IncomingMessage, http.ServerResponse] =}; - reaction (startup) -> serverRequest {= - let options = {}; - server = http.createServer(options, (req : http.IncomingMessage, res : http.ServerResponse) => { - // Generally, browsers make two requests; the first is for favicon.ico. - // See https://stackoverflow.com/questions/11961902/nodejs-http-createserver-seems-to-call-twice - if (req.url != "/favicon.ico") { - actions.serverRequest.schedule(0, [req, res]) - } - }).listen(8000); - console.log("Started web server at http://localhost:8000/") - =} - reaction (serverRequest) {= - let requestArray = serverRequest; - if (requestArray) { - let req = requestArray[0]; - let res = requestArray[1]; - res.writeHead(200); - res.end("Hello world!\n"); - } - =} - reaction (shutdown) {= - if (server) { - server.close(); - } - =} -} \ No newline at end of file + preamble {= + import * as http from "http" + =} + state server:{=http.Server | undefined=}({=undefined=}); + physical action serverRequest:{= [http.IncomingMessage, http.ServerResponse] =}; + reaction (startup) -> serverRequest {= + let options = {}; + server = http.createServer(options, (req : http.IncomingMessage, res : http.ServerResponse) => { + // Generally, browsers make two requests; the first is for favicon.ico. + // See https://stackoverflow.com/questions/11961902/nodejs-http-createserver-seems-to-call-twice + if (req.url != "/favicon.ico") { + actions.serverRequest.schedule(0, [req, res]) + } + }).listen(8000); + console.log("Started web server at http://localhost:8000/") + =} + reaction (serverRequest) {= + let requestArray = serverRequest; + if (requestArray) { + let req = requestArray[0]; + let res = requestArray[1]; + res.writeHead(200); + res.end("Hello world!\n"); + } + =} + reaction (shutdown) {= + if (server) { + server.close(); + } + =} +} diff --git a/experimental/C/src/Autoware/Autoware.lf b/experimental/C/src/Autoware/Autoware.lf index ce587f40..9dfb9bc5 100644 --- a/experimental/C/src/Autoware/Autoware.lf +++ b/experimental/C/src/Autoware/Autoware.lf @@ -27,254 +27,254 @@ // - Sensor Fusion causes merge (choke) points that collapse multiple logical timelines into one. target C { - keepalive: false + keepalive: false }; // Send a periodic image out reactor Camera(offset:time(0), period:time(1 sec)) { - output image:string; - timer t(offset, period); + output image:string; + timer t(offset, period); - reaction(t) -> image {= - lf_set(image, "kitty"); - =} + reaction(t) -> image {= + lf_set(image, "kitty"); + =} } // Send a periodic LIDAR pointcloud out reactor LIDAR(offset:time(0), period:time(1 sec)) { - output pointcloud:string; - timer t(offset, period); + output pointcloud:string; + timer t(offset, period); - reaction(t) -> pointcloud {= - lf_set(pointcloud, "INTERSECTION"); - =} + reaction(t) -> pointcloud {= + lf_set(pointcloud, "INTERSECTION"); + =} } // Simulate object detection on GPU reactor ImageObjectDetection { - input image:string; - output object:string; - - - state thread_id:pthread_t(0); - - preamble {= - #include + input image:string; + output object:string; + + + state thread_id:pthread_t(0); + + preamble {= + #include - void camera_callback(void* a) { - // cudaDeviceSynchronize() and copy back goes here. - lf_schedule(a, 0); - } - // Simulate time passing before a callback occurs instead of executing on GPU. - void* camera_take_time(void* a) { - struct timespec sleep_time = {(time_t) 0, (long)5000000}; // WCET for GPU is 5msec - struct timespec remaining_time; - nanosleep(&sleep_time, &remaining_time); - camera_callback(a); - return NULL; - } - pthread_t threadId; - =} - - physical action CUDA(100 msec); - reaction(image) -> CUDA {= - // cudaMalloc(&y_state_d, SIZE); - // cudaMemcpy(&y_state_d, y_state, SIZE, cudaMemcpyHosttoDevice); - // kernel<<<1,1>>>(y_state_d); - - /* Busy wait for 2 msecs for now instead of calling CUDA kernels */ - interval_t sleep_time = MSEC(2); - instant_t start_time = lf_time_physical(); - while (lf_time_physical() < start_time + sleep_time) {}; - - pthread_create(&self->thread_id, NULL, &camera_take_time, CUDA); - =} - - reaction(CUDA) -> object {= - // cudaMalloc(&y_out_d, SIZE); - - // cudaMemcpy(&y_out, results_d, SIZE, cudaMemcpyDevicetoHost); + void camera_callback(void* a) { + // cudaDeviceSynchronize() and copy back goes here. + lf_schedule(a, 0); + } + // Simulate time passing before a callback occurs instead of executing on GPU. + void* camera_take_time(void* a) { + struct timespec sleep_time = {(time_t) 0, (long)5000000}; // WCET for GPU is 5msec + struct timespec remaining_time; + nanosleep(&sleep_time, &remaining_time); + camera_callback(a); + return NULL; + } + pthread_t threadId; + =} + + physical action CUDA(100 msec); + reaction(image) -> CUDA {= + // cudaMalloc(&y_state_d, SIZE); + // cudaMemcpy(&y_state_d, y_state, SIZE, cudaMemcpyHosttoDevice); + // kernel<<<1,1>>>(y_state_d); + + /* Busy wait for 2 msecs for now instead of calling CUDA kernels */ + interval_t sleep_time = MSEC(2); + instant_t start_time = lf_time_physical(); + while (lf_time_physical() < start_time + sleep_time) {}; + + pthread_create(&self->thread_id, NULL, &camera_take_time, CUDA); + =} + + reaction(CUDA) -> object {= + // cudaMalloc(&y_out_d, SIZE); + + // cudaMemcpy(&y_out, results_d, SIZE, cudaMemcpyDevicetoHost); - // cudaDeviceSynchronize(); + // cudaDeviceSynchronize(); - lf_set(object, "DUMMY Results"); - =} + lf_set(object, "DUMMY Results"); + =} } // Simulate LIDAR object detection on GPU reactor LIDARObjectDetection { - input pointcloud:string; - output object:string; - - - state thread_id:pthread_t(0); - - preamble {= - #include + input pointcloud:string; + output object:string; + + + state thread_id:pthread_t(0); + + preamble {= + #include - void LIDAR_callback(void* a) { - // cudaDeviceSynchronize() and copy back goes here. - lf_schedule(a, 0); - } - // Simulate time passing before a callback occurs instead of executing on GPU. - void* LIDAR_take_time(void* a) { - struct timespec sleep_time = {(time_t) 0, (long)12000000}; // WCET for LIDAR GPU is 12 msec - struct timespec remaining_time; - nanosleep(&sleep_time, &remaining_time); - LIDAR_callback(a); - return NULL; - } - pthread_t threadId; - =} - - logical action CUDA(100 msec, 20 msec); - reaction(pointcloud) -> CUDA {= - // self->y_state = y_in; - // cudaMalloc(&y_state_d, SIZE); - // cudaMemcpy(&y_state_d, y_state, SIZE, cudaMemcpyHosttoDevice); - // kernel<<<1,1>>>(y_state_d); - //printf("LIDAR triggered at %llu\n", lf_time_physical()); - - /* Busy wait for 2 msecs for now instead of calling CUDA kernels */ - interval_t sleep_time = MSEC(2); - instant_t start_time = lf_time_physical(); - while (lf_time_physical() < start_time + sleep_time) {}; - - pthread_create(&self->thread_id, NULL, &LIDAR_take_time, CUDA); - =} - - reaction(CUDA) -> object {= - // cudaMalloc(&y_out_d, SIZE); - //printf("LIDAR triggered at %llu\n", lf_time_physical()); - // printf("LIDAR triggered at %llu\n", lf_time_logical()); - - // cudaMemcpy(&y_out, results_d, SIZE, cudaMemcpyDevicetoHost); + void LIDAR_callback(void* a) { + // cudaDeviceSynchronize() and copy back goes here. + lf_schedule(a, 0); + } + // Simulate time passing before a callback occurs instead of executing on GPU. + void* LIDAR_take_time(void* a) { + struct timespec sleep_time = {(time_t) 0, (long)12000000}; // WCET for LIDAR GPU is 12 msec + struct timespec remaining_time; + nanosleep(&sleep_time, &remaining_time); + LIDAR_callback(a); + return NULL; + } + pthread_t threadId; + =} + + logical action CUDA(100 msec, 20 msec); + reaction(pointcloud) -> CUDA {= + // self->y_state = y_in; + // cudaMalloc(&y_state_d, SIZE); + // cudaMemcpy(&y_state_d, y_state, SIZE, cudaMemcpyHosttoDevice); + // kernel<<<1,1>>>(y_state_d); + //printf("LIDAR triggered at %llu\n", lf_time_physical()); + + /* Busy wait for 2 msecs for now instead of calling CUDA kernels */ + interval_t sleep_time = MSEC(2); + instant_t start_time = lf_time_physical(); + while (lf_time_physical() < start_time + sleep_time) {}; + + pthread_create(&self->thread_id, NULL, &LIDAR_take_time, CUDA); + =} + + reaction(CUDA) -> object {= + // cudaMalloc(&y_out_d, SIZE); + //printf("LIDAR triggered at %llu\n", lf_time_physical()); + // printf("LIDAR triggered at %llu\n", lf_time_logical()); + + // cudaMemcpy(&y_out, results_d, SIZE, cudaMemcpyDevicetoHost); - // cudaDeviceSynchronize(); + // cudaDeviceSynchronize(); - lf_set(object, "Hey look there is a kitty!"); - =} + lf_set(object, "Hey look there is a kitty!"); + =} } // Fuse LIDAR and Camera detected objects reactor DataFusion(threshold:time(20 msec)) { - input imageobject:string; - input LIDARobject:string; - output object:string; - logical action both_ports_are_present(0); - - state tmpImageobject:string(""); - state tmpImageobjectTag:time(0); - state tmpLIDARobject:string(""); - state tmpLIDARobjectTag:time(0); - - - // Handle two ports - reaction(imageobject, LIDARobject) -> both_ports_are_present {= - if(imageobject->is_present) - { - self->tmpImageobject = imageobject->value; - self->tmpImageobjectTag = lf_time_logical(); // Store the tag - - instant_t window = self->tmpLIDARobjectTag - lf_time_logical(); - - if(LIDARobject->is_present) - { - lf_schedule(both_ports_are_present, 0); - } - - else if(window < (long)1000000) - { - lf_schedule(both_ports_are_present, 0); - } - else - { - // instant_t elapsed = lf_time_physical() - lf_time_logical(); // How much time has passed - soft - instant_t elapsed = self->threshold; // How much time has passed - hard - instant_t remaining = (long)30000000 - elapsed; // How much time is left - instant_t schedule_in = remaining - (long)8000000; // Combined WCET of the remaining reactions is 8 msec - lf_schedule(both_ports_are_present, schedule_in); - } - // printf("Received image object at %llu physical and %llu logical.\n", lf_time_physical(), lf_time_logical()); - } - else if(LIDARobject->is_present) - { - self->tmpLIDARobject = LIDARobject->value; - self->tmpLIDARobjectTag = lf_time_logical(); // Store the tag - - instant_t window = self->tmpImageobjectTag - lf_time_logical(); - - if( window < (long)1000000) - { - lf_schedule(both_ports_are_present, 0); - } - else - { - // instant_t elapsed = lf_time_physical() - lf_time_logical(); // How much time has passed - soft - instant_t elapsed = self->threshold; // How much time has passed - hard - instant_t remaining = (long)30000000 - elapsed; // How much time is left - instant_t schedule_in = remaining - (long)8000000; // Combined WCET of the remaining reactions is 8 msec - lf_schedule(both_ports_are_present, schedule_in); - } - // printf("Received LIDAR object at %llu physical and %llu logical.\n", lf_time_physical(), lf_time_logical()); - } + input imageobject:string; + input LIDARobject:string; + output object:string; + logical action both_ports_are_present(0); + + state tmpImageobject:string(""); + state tmpImageobjectTag:time(0); + state tmpLIDARobject:string(""); + state tmpLIDARobjectTag:time(0); + + + // Handle two ports + reaction(imageobject, LIDARobject) -> both_ports_are_present {= + if(imageobject->is_present) + { + self->tmpImageobject = imageobject->value; + self->tmpImageobjectTag = lf_time_logical(); // Store the tag + + instant_t window = self->tmpLIDARobjectTag - lf_time_logical(); + + if(LIDARobject->is_present) + { + lf_schedule(both_ports_are_present, 0); + } + + else if(window < (long)1000000) + { + lf_schedule(both_ports_are_present, 0); + } + else + { + // instant_t elapsed = lf_time_physical() - lf_time_logical(); // How much time has passed - soft + instant_t elapsed = self->threshold; // How much time has passed - hard + instant_t remaining = (long)30000000 - elapsed; // How much time is left + instant_t schedule_in = remaining - (long)8000000; // Combined WCET of the remaining reactions is 8 msec + lf_schedule(both_ports_are_present, schedule_in); + } + // printf("Received image object at %llu physical and %llu logical.\n", lf_time_physical(), lf_time_logical()); + } + else if(LIDARobject->is_present) + { + self->tmpLIDARobject = LIDARobject->value; + self->tmpLIDARobjectTag = lf_time_logical(); // Store the tag + + instant_t window = self->tmpImageobjectTag - lf_time_logical(); + + if( window < (long)1000000) + { + lf_schedule(both_ports_are_present, 0); + } + else + { + // instant_t elapsed = lf_time_physical() - lf_time_logical(); // How much time has passed - soft + instant_t elapsed = self->threshold; // How much time has passed - hard + instant_t remaining = (long)30000000 - elapsed; // How much time is left + instant_t schedule_in = remaining - (long)8000000; // Combined WCET of the remaining reactions is 8 msec + lf_schedule(both_ports_are_present, schedule_in); + } + // printf("Received LIDAR object at %llu physical and %llu logical.\n", lf_time_physical(), lf_time_logical()); + } - - =} deadline(threshold) {= - printf("Deadline violation detected.\n"); - =} - - // Fuse - reaction(both_ports_are_present) -> object - {= - - printf("Fusion scheduled at: ( %llu , %llu ).\n", lf_time_physical(), lf_time_logical()); - //printf("Fusion: %llu\n", lf_time_physical()); - /* Busy wait for 2 msecs for now instead of calling CUDA kernels */ - interval_t sleep_time = MSEC(2); - instant_t start_time = lf_time_physical(); - while (lf_time_physical() < start_time + sleep_time) {}; - lf_set(object, "fused"); - =} + + =} deadline(threshold) {= + printf("Deadline violation detected.\n"); + =} + + // Fuse + reaction(both_ports_are_present) -> object + {= + + printf("Fusion scheduled at: ( %llu , %llu ).\n", lf_time_physical(), lf_time_logical()); + //printf("Fusion: %llu\n", lf_time_physical()); + /* Busy wait for 2 msecs for now instead of calling CUDA kernels */ + interval_t sleep_time = MSEC(2); + instant_t start_time = lf_time_physical(); + while (lf_time_physical() < start_time + sleep_time) {}; + lf_set(object, "fused"); + =} } // Make driving semantic decisions reactor Semantics { - input fusedObject:string; - output actuation:int; - - reaction(fusedObject) -> actuation {= - struct timespec sleep_time = {(time_t) 0, (long)6000000}; - struct timespec remaining_time; - nanosleep(&sleep_time, &remaining_time); - lf_set(actuation, 5); - =} + input fusedObject:string; + output actuation:int; + + reaction(fusedObject) -> actuation {= + struct timespec sleep_time = {(time_t) 0, (long)6000000}; + struct timespec remaining_time; + nanosleep(&sleep_time, &remaining_time); + lf_set(actuation, 5); + =} } reactor Actuator(threshold:time(33 msec)){ - input actuation:int; - - reaction(actuation) {= - // Do nothing; - printf("Actuator scheduled at: ( %llu , %llu ).\n", lf_time_physical(), lf_time_logical()); - // printf("Actuator: %llu\n", lf_time_physical()); - =} deadline(threshold) {= - printf("Deadline violation detected in Actuator.\n"); - =} + input actuation:int; + + reaction(actuation) {= + // Do nothing; + printf("Actuator scheduled at: ( %llu , %llu ).\n", lf_time_physical(), lf_time_logical()); + // printf("Actuator: %llu\n", lf_time_physical()); + =} deadline(threshold) {= + printf("Deadline violation detected in Actuator.\n"); + =} } /* An alternative path exists between the LIDAR and the actuator for the safety brake system */ reactor SafetyBrake { - input LIDARPointCloud:string; - output actuation:int; + input LIDARPointCloud:string; + output actuation:int; - reaction(LIDARPointCloud) -> actuation {= - lf_set(actuation, 5); - =} + reaction(LIDARPointCloud) -> actuation {= + lf_set(actuation, 5); + =} } @@ -284,30 +284,30 @@ reactor SafetyBrake { // Sub-deadlines (i.e., reaction deadlines) must not be violated main reactor Autoware { - c = new Camera(offset = 3 msec, period = 33 msec); // Camera has a phase (startup time) of 3 msec and a period of 33 msec - l = new LIDAR(offset = 10 msec, period = 10 msec); // Lidar has a phase (spooling up time) of 10 msec and a period of 10 msec - - iobjectdetection = new ImageObjectDetection(); - c.image -> iobjectdetection.image; + c = new Camera(offset = 3 msec, period = 33 msec); // Camera has a phase (startup time) of 3 msec and a period of 33 msec + l = new LIDAR(offset = 10 msec, period = 10 msec); // Lidar has a phase (spooling up time) of 10 msec and a period of 10 msec + + iobjectdetection = new ImageObjectDetection(); + c.image -> iobjectdetection.image; - lobjectdetection = new LIDARObjectDetection(); - l.pointcloud -> lobjectdetection.pointcloud; + lobjectdetection = new LIDARObjectDetection(); + l.pointcloud -> lobjectdetection.pointcloud; - // Choke point - fuse = new DataFusion(); - iobjectdetection.object -> fuse.imageobject; - lobjectdetection.object -> fuse.LIDARobject; + // Choke point + fuse = new DataFusion(); + iobjectdetection.object -> fuse.imageobject; + lobjectdetection.object -> fuse.LIDARobject; - sem = new Semantics(); - fuse.object -> sem.fusedObject; + sem = new Semantics(); + fuse.object -> sem.fusedObject; - ac = new Actuator(); - sem.actuation -> ac.actuation; + ac = new Actuator(); + sem.actuation -> ac.actuation; - // An alternative path that activates the safety brake system based on LIDAR input - sb = new SafetyBrake(); - l.pointcloud -> sb.LIDARPointCloud; - // sb.actuation -> ac.actuation; // Cannot construct the alternative path because actuation may only be connected to a single upstream port + // An alternative path that activates the safety brake system based on LIDAR input + sb = new SafetyBrake(); + l.pointcloud -> sb.LIDARPointCloud; + // sb.actuation -> ac.actuation; // Cannot construct the alternative path because actuation may only be connected to a single upstream port } diff --git a/experimental/C/src/Controller/ResponseTime.lf b/experimental/C/src/Controller/ResponseTime.lf index c0db769d..b0459eda 100644 --- a/experimental/C/src/Controller/ResponseTime.lf +++ b/experimental/C/src/Controller/ResponseTime.lf @@ -12,53 +12,53 @@ * planning involved, such as those found in industrial automation or robotics. */ target C { - timeout: 1 sec + timeout: 1 sec } reactor PhysicalPlant { - input control:double; - output sensor:double; - timer t(0, 33 msec); - state last_sensor_time:time(0); - reaction(t) -> sensor {= - lf_set(sensor, 42); - self->last_sensor_time = lf_time_physical(); - =} - reaction(control) {= - instant_t control_time = lf_time_physical(); - lf_print("Latency %lld.", control_time - self->last_sensor_time); - =} STP(33 msec) {= - lf_print_warning("STP violation."); - =} + input control:double; + output sensor:double; + timer t(0, 33 msec); + state last_sensor_time:time(0); + reaction(t) -> sensor {= + lf_set(sensor, 42); + self->last_sensor_time = lf_time_physical(); + =} + reaction(control) {= + instant_t control_time = lf_time_physical(); + lf_print("Latency %lld.", control_time - self->last_sensor_time); + =} STP(33 msec) {= + lf_print_warning("STP violation."); + =} } reactor Controller { - input sensor:double; - output control:double; - - output request_for_planning:double; - input planning:double; - - reaction(sensor) -> request_for_planning {= - lf_set(request_for_planning, sensor->value); - =} - reaction(planning) -> control {= - lf_set(control, planning->value); - =} + input sensor:double; + output control:double; + + output request_for_planning:double; + input planning:double; + + reaction(sensor) -> request_for_planning {= + lf_set(request_for_planning, sensor->value); + =} + reaction(planning) -> control {= + lf_set(control, planning->value); + =} } reactor Planner { - input request:double; - output response: double; - reaction(request) -> response {= - lf_nanosleep(MSEC(10)); - lf_set(response, request->value); - =} + input request:double; + output response: double; + reaction(request) -> response {= + lf_nanosleep(MSEC(10)); + lf_set(response, request->value); + =} } main reactor { - p = new PhysicalPlant(); - c = new Controller(); - pl = new Planner(); - - p.sensor -> c.sensor; - c.request_for_planning -> pl.request; - pl.response -> c.planning; - c.control -> p.control; -} \ No newline at end of file + p = new PhysicalPlant(); + c = new Controller(); + pl = new Planner(); + + p.sensor -> c.sensor; + c.request_for_planning -> pl.request; + pl.response -> c.planning; + c.control -> p.control; +} diff --git a/experimental/C/src/Controller/ResponseTime2.lf b/experimental/C/src/Controller/ResponseTime2.lf index 47c7ae14..f739e42d 100644 --- a/experimental/C/src/Controller/ResponseTime2.lf +++ b/experimental/C/src/Controller/ResponseTime2.lf @@ -14,63 +14,63 @@ * accounting for latency and timing constraints. */ target C { - timeout: 1 sec + timeout: 1 sec } reactor PhysicalPlant { - input control:double; - output sensor:double; - timer t(0, 33 msec); - state last_sensor_time:time(0); - state previous_sensor_time:time(0); - reaction(t) -> sensor {= - lf_set(sensor, 42); - self->previous_sensor_time = self->last_sensor_time; - self->last_sensor_time = lf_time_physical(); - =} - reaction(control) {= - instant_t control_time = lf_time_physical(); - lf_print("Latency %lld.", control_time - self->previous_sensor_time); - lf_print("Logical time: %lld.", lf_time_logical_elapsed()); - =} STP(33 msec) {= - lf_print_warning("STP violation."); - =} + input control:double; + output sensor:double; + timer t(0, 33 msec); + state last_sensor_time:time(0); + state previous_sensor_time:time(0); + reaction(t) -> sensor {= + lf_set(sensor, 42); + self->previous_sensor_time = self->last_sensor_time; + self->last_sensor_time = lf_time_physical(); + =} + reaction(control) {= + instant_t control_time = lf_time_physical(); + lf_print("Latency %lld.", control_time - self->previous_sensor_time); + lf_print("Logical time: %lld.", lf_time_logical_elapsed()); + =} STP(33 msec) {= + lf_print_warning("STP violation."); + =} } reactor Controller { - input sensor:double; - output control:double; - - state latest_control:double(0.0); - state first:bool(true); - - output request_for_planning:double; - input planning:double; - - reaction(planning) {= - self->latest_control = planning->value; - =} - reaction(sensor) -> control, request_for_planning {= - if (!self->first) { - lf_set(control, self->latest_control); - } - self->first = false; - lf_set(request_for_planning, sensor->value); - =} + input sensor:double; + output control:double; + + state latest_control:double(0.0); + state first:bool(true); + + output request_for_planning:double; + input planning:double; + + reaction(planning) {= + self->latest_control = planning->value; + =} + reaction(sensor) -> control, request_for_planning {= + if (!self->first) { + lf_set(control, self->latest_control); + } + self->first = false; + lf_set(request_for_planning, sensor->value); + =} } reactor Planner { - input request:double; - output response: double; - reaction(request) -> response {= - lf_nanosleep(MSEC(10)); - lf_set(response, request->value); - =} + input request:double; + output response: double; + reaction(request) -> response {= + lf_nanosleep(MSEC(10)); + lf_set(response, request->value); + =} } main reactor { - p = new PhysicalPlant(); - c = new Controller(); - pl = new Planner(); - - p.sensor -> c.sensor; - c.request_for_planning -> pl.request; - pl.response -> c.planning after 0; - c.control -> p.control; -} \ No newline at end of file + p = new PhysicalPlant(); + c = new Controller(); + pl = new Planner(); + + p.sensor -> c.sensor; + c.request_for_planning -> pl.request; + pl.response -> c.planning after 0; + c.control -> p.control; +} diff --git a/experimental/C/src/Controller/ResponseTime3.lf b/experimental/C/src/Controller/ResponseTime3.lf index 83599a20..a6c6eea3 100644 --- a/experimental/C/src/Controller/ResponseTime3.lf +++ b/experimental/C/src/Controller/ResponseTime3.lf @@ -11,83 +11,83 @@ * embedded systems, including latency measurements and STP checks. */ target C { - timeout: 1 sec, - keepalive: true + timeout: 1 sec, + keepalive: true } reactor PhysicalPlant { - preamble {= - void* my_thread(void* a) { - while(true) { - lf_schedule_int(a, 0, 42); - lf_nanosleep(MSEC(33)); - } - } - =} - input control:double; - output sensor:double; - state last_sensor_time:time(0); - state previous_sensor_time:time(0); - state thread_id:lf_thread_t(0); - physical action a:int; - reaction(startup) -> a {= - lf_thread_create(&self->thread_id, &my_thread, a); - =} - reaction(a) -> sensor {= - lf_set(sensor, 42); - self->previous_sensor_time = self->last_sensor_time; - self->last_sensor_time = lf_time_logical(); - =} - reaction(control) {= - instant_t control_time = lf_time_physical(); - lf_print("Latency %lld.", control_time - self->previous_sensor_time); - lf_print("Logical time: %lld.", lf_time_logical_elapsed()); - lf_print("Lag: %lld.", lf_time_physical_elapsed() - lf_time_logical_elapsed()); - =} STP(33 msec) {= - lf_print_warning("STP violation."); - =} + preamble {= + void* my_thread(void* a) { + while(true) { + lf_schedule_int(a, 0, 42); + lf_nanosleep(MSEC(33)); + } + } + =} + input control:double; + output sensor:double; + state last_sensor_time:time(0); + state previous_sensor_time:time(0); + state thread_id:lf_thread_t(0); + physical action a:int; + reaction(startup) -> a {= + lf_thread_create(&self->thread_id, &my_thread, a); + =} + reaction(a) -> sensor {= + lf_set(sensor, 42); + self->previous_sensor_time = self->last_sensor_time; + self->last_sensor_time = lf_time_logical(); + =} + reaction(control) {= + instant_t control_time = lf_time_physical(); + lf_print("Latency %lld.", control_time - self->previous_sensor_time); + lf_print("Logical time: %lld.", lf_time_logical_elapsed()); + lf_print("Lag: %lld.", lf_time_physical_elapsed() - lf_time_logical_elapsed()); + =} STP(33 msec) {= + lf_print_warning("STP violation."); + =} } reactor Controller { - input sensor:double; - output control:double; - - state latest_control:double(0.0); - state first:bool(true); - state in_progress:bool(false); - - output request_for_planning:double; - input planning:double; - - reaction(planning) {= - self->latest_control = planning->value; - self->in_progress = false; - =} - reaction(sensor) -> control, request_for_planning {= - if (!self->first) { - lf_set(control, self->latest_control); - } - self->first = false; - lf_print("******** in_progress: %d", self->in_progress); - if (!self->in_progress) { - self->in_progress = true; - lf_set(request_for_planning, sensor->value); - } - =} + input sensor:double; + output control:double; + + state latest_control:double(0.0); + state first:bool(true); + state in_progress:bool(false); + + output request_for_planning:double; + input planning:double; + + reaction(planning) {= + self->latest_control = planning->value; + self->in_progress = false; + =} + reaction(sensor) -> control, request_for_planning {= + if (!self->first) { + lf_set(control, self->latest_control); + } + self->first = false; + lf_print("******** in_progress: %d", self->in_progress); + if (!self->in_progress) { + self->in_progress = true; + lf_set(request_for_planning, sensor->value); + } + =} } reactor Planner { - input request:double; - output response: double; - reaction(request) -> response {= - lf_nanosleep(MSEC(100)); - lf_set(response, request->value); - =} + input request:double; + output response: double; + reaction(request) -> response {= + lf_nanosleep(MSEC(100)); + lf_set(response, request->value); + =} } main reactor { - p = new PhysicalPlant(); - c = new Controller(); - pl = new Planner(); - - p.sensor -> c.sensor; - c.request_for_planning -> pl.request; - pl.response -> c.planning after 100 msec; - c.control -> p.control; -} \ No newline at end of file + p = new PhysicalPlant(); + c = new Controller(); + pl = new Planner(); + + p.sensor -> c.sensor; + c.request_for_planning -> pl.request; + pl.response -> c.planning after 100 msec; + c.control -> p.control; +} diff --git a/experimental/C/src/Intersection/Intersection.lf b/experimental/C/src/Intersection/Intersection.lf index 62d4713a..1a07f565 100644 --- a/experimental/C/src/Intersection/Intersection.lf +++ b/experimental/C/src/Intersection/Intersection.lf @@ -14,140 +14,140 @@ * This is a very rough starting point that needs a lot of work. */ target C { - timeout: 5 sec + timeout: 5 sec } preamble {= - typedef struct { - double speed; - double distance; - } request_message_t; - - typedef struct { - // Average speed vehicle should maintain in the intersection. - double target_speed; // FIXME: Deadline. = t/w - // Time at which the vehicle can enter the intersection. - instant_t arrival_time; - } grant_message_t; - - // Table of offsets by vehicle bank_index: - interval_t timer_offsets[] = { - 0LL, - MSEC(200), - MSEC(400), - MSEC(600) - }; - // Table of periods by vehicle bank_index: - interval_t timer_periods[] = { - SEC(4), - SEC(8), - SEC(16), - SEC(32) - }; + typedef struct { + double speed; + double distance; + } request_message_t; + + typedef struct { + // Average speed vehicle should maintain in the intersection. + double target_speed; // FIXME: Deadline. = t/w + // Time at which the vehicle can enter the intersection. + instant_t arrival_time; + } grant_message_t; + + // Table of offsets by vehicle bank_index: + interval_t timer_offsets[] = { + 0LL, + MSEC(200), + MSEC(400), + MSEC(600) + }; + // Table of periods by vehicle bank_index: + interval_t timer_periods[] = { + SEC(4), + SEC(8), + SEC(16), + SEC(32) + }; =} reactor Vehicle ( - offset:time(0), - period:time(1 sec), - speed:double(42.0), // in km per hour. About 11.7 m/sec - distance:double(42.0) // in meters. About 4 sec to traverse. + offset:time(0), + period:time(1 sec), + speed:double(42.0), // in km per hour. About 11.7 m/sec + distance:double(42.0) // in meters. About 4 sec to traverse. ) { - input grant:grant_message_t; - - output request:request_message_t; - - logical action delay; - - reaction(startup) -> request, delay {= - if (timer_offsets[self->bank_index] == 0LL) { - // Need to send a message at the start time. - request_message_t message; - message.speed = self->speed; - message.distance = self->distance; - lf_set(request, message); - lf_schedule(delay, timer_periods[self->bank_index]); - } else { - lf_schedule(delay, timer_offsets[self->bank_index]); - } - =} + input grant:grant_message_t; + + output request:request_message_t; + + logical action delay; + + reaction(startup) -> request, delay {= + if (timer_offsets[self->bank_index] == 0LL) { + // Need to send a message at the start time. + request_message_t message; + message.speed = self->speed; + message.distance = self->distance; + lf_set(request, message); + lf_schedule(delay, timer_periods[self->bank_index]); + } else { + lf_schedule(delay, timer_offsets[self->bank_index]); + } + =} - reaction(delay) -> request, delay {= - request_message_t message; - message.speed = self->speed; - message.distance = self->distance; - lf_set(request, message); - lf_schedule(delay, timer_periods[self->bank_index]); - =} - - reaction(grant) {= - lf_print("Granted access at elapsed logical time %lld. Physical time is %lld", - lf_time_logical_elapsed(), - lf_time_physical_elapsed() - ); - =} + reaction(delay) -> request, delay {= + request_message_t message; + message.speed = self->speed; + message.distance = self->distance; + lf_set(request, message); + lf_schedule(delay, timer_periods[self->bank_index]); + =} + + reaction(grant) {= + lf_print("Granted access at elapsed logical time %lld. Physical time is %lld", + lf_time_logical_elapsed(), + lf_time_physical_elapsed() + ); + =} } reactor RSU ( - num_entries:int(4), - intersection_width:double(42.0), // in meters. - // If the vehicle is told to slow down, then its target - // average speed in the intersection should be at least this. - nominal_speed_in_intersection:double(10.0) // In km/hr. 2.8 m/sec. 15 sec to traverse. + num_entries:int(4), + intersection_width:double(42.0), // in meters. + // If the vehicle is told to slow down, then its target + // average speed in the intersection should be at least this. + nominal_speed_in_intersection:double(10.0) // In km/hr. 2.8 m/sec. 15 sec to traverse. ) { - input[num_entries] request:request_message_t; - output[num_entries] grant:grant_message_t; - - state earliest_free:time(0); - - reaction(request) -> grant {= - for (int i = 0; i < self->num_entries; i++) { - if (request[i]->is_present) { - // Calculate the time it will take the approaching vehicle to - // arrive at its current speed. Note that this is - // time from the time the vehicle sends the message - // according to the arriving vehicle's clock. - double speed_in_m_per_sec = request[i]->value.speed * 1000.0 / 3600.0; - double arrival_in = request[i]->value.distance / speed_in_m_per_sec; - - instant_t time_message_sent = lf_time_logical(); - - // Convert the time interval to nsec (it is seconds). - interval_t arrival_time_ns = time_message_sent + (interval_t) (arrival_in * BILLION); - - grant_message_t response; - if (arrival_time_ns >= self->earliest_free) { - // Vehicle can maintain speed. - response.target_speed = request[i]->value.speed; - response.arrival_time = arrival_time_ns; - } else { - // Vehicle has to slow down and maybe stop. - response.arrival_time = self->earliest_free; - // Could be smarter than this, but just send the nominal speed in intersection. - response.target_speed = self->nominal_speed_in_intersection; - } - lf_set(grant[i], response); - // Update earliest free on the assumption that the vehicle - // maintains its target speed (on average) within the intersection. - interval_t time_in_intersection - = (interval_t)(BILLION * self->intersection_width * 3600 - / (1000 * response.target_speed) - ); - self->earliest_free = response.arrival_time + time_in_intersection; - - lf_print("*** Grant access to vehicle %d to enter at time %lld. Next available time is %lld", - i, - response.arrival_time - lf_time_start(), - self->earliest_free - lf_time_start() - ); - } + input[num_entries] request:request_message_t; + output[num_entries] grant:grant_message_t; + + state earliest_free:time(0); + + reaction(request) -> grant {= + for (int i = 0; i < self->num_entries; i++) { + if (request[i]->is_present) { + // Calculate the time it will take the approaching vehicle to + // arrive at its current speed. Note that this is + // time from the time the vehicle sends the message + // according to the arriving vehicle's clock. + double speed_in_m_per_sec = request[i]->value.speed * 1000.0 / 3600.0; + double arrival_in = request[i]->value.distance / speed_in_m_per_sec; + + instant_t time_message_sent = lf_time_logical(); + + // Convert the time interval to nsec (it is seconds). + interval_t arrival_time_ns = time_message_sent + (interval_t) (arrival_in * BILLION); + + grant_message_t response; + if (arrival_time_ns >= self->earliest_free) { + // Vehicle can maintain speed. + response.target_speed = request[i]->value.speed; + response.arrival_time = arrival_time_ns; + } else { + // Vehicle has to slow down and maybe stop. + response.arrival_time = self->earliest_free; + // Could be smarter than this, but just send the nominal speed in intersection. + response.target_speed = self->nominal_speed_in_intersection; } - =} + lf_set(grant[i], response); + // Update earliest free on the assumption that the vehicle + // maintains its target speed (on average) within the intersection. + interval_t time_in_intersection + = (interval_t)(BILLION * self->intersection_width * 3600 + / (1000 * response.target_speed) + ); + self->earliest_free = response.arrival_time + time_in_intersection; + + lf_print("*** Grant access to vehicle %d to enter at time %lld. Next available time is %lld", + i, + response.arrival_time - lf_time_start(), + self->earliest_free - lf_time_start() + ); + } + } + =} } main reactor { - vehicles = new[4] Vehicle(offset = 0); - - rsu = new RSU(); - vehicles.request -> rsu.request; - rsu.grant -> vehicles.grant; -} \ No newline at end of file + vehicles = new[4] Vehicle(offset = 0); + + rsu = new RSU(); + vehicles.request -> rsu.request; + rsu.grant -> vehicles.grant; +} diff --git a/experimental/C/src/Logic/Logic.lf b/experimental/C/src/Logic/Logic.lf index 1ba3aa17..3f834e11 100644 --- a/experimental/C/src/Logic/Logic.lf +++ b/experimental/C/src/Logic/Logic.lf @@ -3,52 +3,52 @@ target C {timeout: 200 msec}; // Test illustrating that the current implementation of lf_schedule leads to unexpected behavior. reactor Source(name:string("source"), even:bool(true)) { - output out:bool; - state on:bool(false); - state count:int(0); - logical action next; + output out:bool; + state on:bool(false); + state count:int(0); + logical action next; + + reaction(startup) -> out {= + if (!self->even) { + self->on = true; + } + =} + + reaction(startup, next) -> next, out {= + if (self->on) { + lf_set(out, true); + } - reaction(startup) -> out {= - if (!self->even) { - self->on = true; - } - =} + self->on = !self->on; - reaction(startup, next) -> next, out {= - if (self->on) { - lf_set(out, true); - } - - self->on = !self->on; - - if (self->count < 9) { - self->count++; - lf_schedule(next, 0); - } else { - lf_schedule(next, MSEC(100)); - self->count = 0; - } - =} + if (self->count < 9) { + self->count++; + lf_schedule(next, 0); + } else { + lf_schedule(next, MSEC(100)); + self->count = 0; + } + =} } reactor LogicalAnd { - input a:bool; - input b:bool; + input a:bool; + input b:bool; - reaction(a, b) {= - printf("Output: %d, tag: (%lld, %u)\n", - a->is_present & b->is_present, lf_time_logical_elapsed(), lf_tag().microstep - ); - =} + reaction(a, b) {= + printf("Output: %d, tag: (%lld, %u)\n", + a->is_present & b->is_present, lf_time_logical_elapsed(), lf_tag().microstep + ); + =} } main reactor Logic { - x = new Source(name="x", even=true); - y = new Source(name="y", even=false); - z = new LogicalAnd(); - - x.out -> z.a after 1 msec; - y.out -> z.b after 1 msec; - //x.out -> z.a; - //y.out -> z.b; -} \ No newline at end of file + x = new Source(name="x", even=true); + y = new Source(name="y", even=false); + z = new LogicalAnd(); + + x.out -> z.a after 1 msec; + y.out -> z.b after 1 msec; + //x.out -> z.a; + //y.out -> z.b; +} diff --git a/experimental/C/src/Microsteps/Anomaly.lf b/experimental/C/src/Microsteps/Anomaly.lf index 117a1870..6e0138b1 100644 --- a/experimental/C/src/Microsteps/Anomaly.lf +++ b/experimental/C/src/Microsteps/Anomaly.lf @@ -7,52 +7,52 @@ * @author Edward A. Lee */ target C { - timeout: 50 msec, + timeout: 50 msec, } reactor Source { - output out1:int; - output out2:int; - logical action redo; - state count:int(0); - reaction(startup, redo) -> out1, out2, redo {= - if (self->count++ < 4) { - if (self->count % 2 == 0) { - lf_set (out2, self->count); - } else { - lf_set(out1, self->count); - } - lf_schedule(redo, 0); - } else { - self->count = 0; - // The following resets the microstep to 0. - // If it were to not do that, then microsteps would - // grow monotonically as execution advances. - lf_schedule(redo, MSEC(10)); - } - =} + output out1:int; + output out2:int; + logical action redo; + state count:int(0); + reaction(startup, redo) -> out1, out2, redo {= + if (self->count++ < 4) { + if (self->count % 2 == 0) { + lf_set (out2, self->count); + } else { + lf_set(out1, self->count); + } + lf_schedule(redo, 0); + } else { + self->count = 0; + // The following resets the microstep to 0. + // If it were to not do that, then microsteps would + // grow monotonically as execution advances. + lf_schedule(redo, MSEC(10)); + } + =} } reactor Destination(name:string("dest")) { - input in1:int; - input in2:int; - reaction (in1, in2) {= - if (in1->is_present) { - lf_print("%s: Tag (%lld, %d): in1 received %d", self->name, - lf_time_logical_elapsed(), lf_tag().microstep, in1->value - ); - } - if (in2->is_present) { - lf_print("%s: Tag (%lld, %d): in2 received %d", self->name, - lf_time_logical_elapsed(), lf_tag().microstep, in2->value - ); - } - =} + input in1:int; + input in2:int; + reaction (in1, in2) {= + if (in1->is_present) { + lf_print("%s: Tag (%lld, %d): in1 received %d", self->name, + lf_time_logical_elapsed(), lf_tag().microstep, in1->value + ); + } + if (in2->is_present) { + lf_print("%s: Tag (%lld, %d): in2 received %d", self->name, + lf_time_logical_elapsed(), lf_tag().microstep, in2->value + ); + } + =} } main reactor Anomaly { - s = new Source(); - d1 = new Destination(name = "d1"); - d2 = new Destination(name = "d2"); - s.out1 -> d1.in1; - s.out2 -> d1.in2; - s.out1 -> d2.in1 after 5 msec; - s.out2 -> d2.in2 after 5 msec; -} \ No newline at end of file + s = new Source(); + d1 = new Destination(name = "d1"); + d2 = new Destination(name = "d2"); + s.out1 -> d1.in1; + s.out2 -> d1.in2; + s.out1 -> d2.in1 after 5 msec; + s.out2 -> d2.in2 after 5 msec; +} diff --git a/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf index 29200308..7cf3a13c 100644 --- a/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf +++ b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes.lf @@ -14,34 +14,34 @@ target C; reactor GhostCloseBehavior { - initial mode ChaseGhost { - @label("Condition: !GhostScared") - reaction() -> AvoidGhost {==} - - @label("Action: ChaseGhost") - reaction() {==} - } - mode AvoidGhost { - @label("Condition: GhostScared") - reaction() -> ChaseGhost {==} - - @label("Action: AvoidGhost") - reaction() {==} - } + initial mode ChaseGhost { + @label("Condition: !GhostScared") + reaction() -> AvoidGhost {==} + + @label("Action: ChaseGhost") + reaction() {==} + } + mode AvoidGhost { + @label("Condition: GhostScared") + reaction() -> ChaseGhost {==} + + @label("Action: AvoidGhost") + reaction() {==} + } } main reactor { - initial mode HandleGhostClose { - @label("Condition: !GhostClose") - reaction() -> EatPills {==} - - inner = new GhostCloseBehavior() - } - mode EatPills { - @label("Condition: GhostClose") - reaction() -> reset(HandleGhostClose) {==} - - @label("Action: EatPills") - reaction() {==} - } -} \ No newline at end of file + initial mode HandleGhostClose { + @label("Condition: !GhostClose") + reaction() -> EatPills {==} + + inner = new GhostCloseBehavior() + } + mode EatPills { + @label("Condition: GhostClose") + reaction() -> reset(HandleGhostClose) {==} + + @label("Action: EatPills") + reaction() {==} + } +} diff --git a/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf index 3971a0a2..9a08e960 100644 --- a/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf +++ b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Modes_Triggers.lf @@ -14,44 +14,44 @@ target C; reactor GhostCloseBehavior { - input GhostScared:bool + input GhostScared:bool + + initial mode ChaseGhost { + @label("Condition: !GhostScared") + reaction(reset, GhostScared) -> AvoidGhost {==} - initial mode ChaseGhost { - @label("Condition: !GhostScared") - reaction(reset, GhostScared) -> AvoidGhost {==} - - @label("Action: ChaseGhost") - reaction(reset) {==} - } - mode AvoidGhost { - @label("Condition: GhostScared") - reaction(GhostScared) -> ChaseGhost {==} - - @label("Action: AvoidGhost") - reaction(reset) {==} - } + @label("Action: ChaseGhost") + reaction(reset) {==} + } + mode AvoidGhost { + @label("Condition: GhostScared") + reaction(GhostScared) -> ChaseGhost {==} + + @label("Action: AvoidGhost") + reaction(reset) {==} + } } reactor PacManBehavior { - input GhostClose:bool - input GhostScared:bool + input GhostClose:bool + input GhostScared:bool + + initial mode HandleGhostClose { + @label("Condition: !GhostClose") + reaction(GhostClose) -> EatPills {==} + + inner = new GhostCloseBehavior() + GhostScared -> inner.GhostScared + } + mode EatPills { + @label("Condition: GhostClose") + reaction(GhostClose) -> reset(HandleGhostClose) {==} - initial mode HandleGhostClose { - @label("Condition: !GhostClose") - reaction(GhostClose) -> EatPills {==} - - inner = new GhostCloseBehavior() - GhostScared -> inner.GhostScared - } - mode EatPills { - @label("Condition: GhostClose") - reaction(GhostClose) -> reset(HandleGhostClose) {==} - - @label("Action: EatPills") - reaction(reset) {==} - } + @label("Action: EatPills") + reaction(reset) {==} + } } main reactor { - p = new PacManBehavior() -} \ No newline at end of file + p = new PacManBehavior() +} diff --git a/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf index 954be96c..23c9e7ef 100644 --- a/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf +++ b/experimental/C/src/ModalModels/BehaviorTrees/pacman/PacMan_BehaviorTree_Mockup_Reactors.lf @@ -17,116 +17,116 @@ target C; // Utilities reactor BehaviorNode { - input start:bool - output success:bool - output failure:bool + input start:bool + output success:bool + output failure:bool } reactor MergeOr { - input right:bool - input left:bool - output merged:bool - - reaction(left, right) -> merged {= - lf_set(merged, true); - =} + input right:bool + input left:bool + output merged:bool + + reaction(left, right) -> merged {= + lf_set(merged, true); + =} } // Actions and Conditions @btnode("action") reactor EatPills extends BehaviorNode { - reaction(start) -> success, failure {==} + reaction(start) -> success, failure {==} } @btnode("action") reactor AvoidGhost extends BehaviorNode { - reaction(start) -> success, failure {==} + reaction(start) -> success, failure {==} } @btnode("action") reactor ChaseGhost extends BehaviorNode { - reaction(start) -> success, failure {==} + reaction(start) -> success, failure {==} } @btnode("condition") reactor GhostClose extends BehaviorNode { - reaction(start) -> success, failure {==} + reaction(start) -> success, failure {==} } @btnode("condition") reactor GhostScared extends BehaviorNode { - reaction(start) -> success, failure {==} + reaction(start) -> success, failure {==} } // Behavior tree structure @btnode("fallback") reactor PacMan0 extends BehaviorNode { - left = new PacMan1() - right = new EatPills() - merge = new MergeOr() - - start -> left.start - left.success -> merge.left - left.failure -> right.start - right.success -> merge.right - right.failure -> failure - merge.merged -> success + left = new PacMan1() + right = new EatPills() + merge = new MergeOr() + + start -> left.start + left.success -> merge.left + left.failure -> right.start + right.success -> merge.right + right.failure -> failure + merge.merged -> success } @btnode("sequence") reactor PacMan1 extends BehaviorNode { - left = new GhostClose() - right = new PacMan2() - merge = new MergeOr() - - start -> left.start - left.success -> right.start - left.failure -> merge.left - right.success -> success - right.failure -> merge.right - merge.merged -> failure + left = new GhostClose() + right = new PacMan2() + merge = new MergeOr() + + start -> left.start + left.success -> right.start + left.failure -> merge.left + right.success -> success + right.failure -> merge.right + merge.merged -> failure } @btnode("fallback") reactor PacMan2 extends BehaviorNode { - left = new PacMan3() - right = new AvoidGhost() - merge = new MergeOr() - - start -> left.start - left.success -> merge.left - left.failure -> right.start - right.success -> merge.right - right.failure -> failure - merge.merged -> success + left = new PacMan3() + right = new AvoidGhost() + merge = new MergeOr() + + start -> left.start + left.success -> merge.left + left.failure -> right.start + right.success -> merge.right + right.failure -> failure + merge.merged -> success } @btnode("sequence") reactor PacMan3 extends BehaviorNode { - // Fallback - left = new GhostScared() - right = new ChaseGhost() - merge = new MergeOr() - - start -> left.start - left.success -> right.start - left.failure -> merge.left - right.success -> success - right.failure -> merge.right - merge.merged -> failure + // Fallback + left = new GhostScared() + right = new ChaseGhost() + merge = new MergeOr() + + start -> left.start + left.success -> right.start + left.failure -> merge.left + right.success -> success + right.failure -> merge.right + merge.merged -> failure } // Root main reactor { - timer t (0, 5msec) - - bt = new PacMan0() - - reaction(t) -> bt.start {= - lf_set(bt.start, true); - =} -} \ No newline at end of file + timer t (0, 5msec) + + bt = new PacMan0() + + reaction(t) -> bt.start {= + lf_set(bt.start, true); + =} +} diff --git a/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_advanced.lf b/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_advanced.lf index eb024812..489e05ff 100644 --- a/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_advanced.lf +++ b/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_advanced.lf @@ -10,132 +10,132 @@ * of the task sequence at the point where it was left when the battery ran out. */ target C { -// logging: debug +// logging: debug } reactor GenericTask(name:string("")) { - output success:bool - output failure:bool + output success:bool + output failure:bool - initial mode Running { - // Just for testing - timer work(0, 250msec) - timer finish(1sec, 1sec) - - reaction(work) {= - printf("%s\n", self->name); - =} - - reaction(finish) -> success, Succeeded, failure, Failed {= - int r = rand() % 6; - if (r == 0) { - lf_set(failure, true); - lf_set_mode(Failed); - } else { - lf_set(success, true); - lf_set_mode(Succeeded); - } - =} - } + initial mode Running { + // Just for testing + timer work(0, 250msec) + timer finish(1sec, 1sec) + + reaction(work) {= + printf("%s\n", self->name); + =} - mode Succeeded {} - mode Failed {} + reaction(finish) -> success, Succeeded, failure, Failed {= + int r = rand() % 6; + if (r == 0) { + lf_set(failure, true); + lf_set_mode(Failed); + } else { + lf_set(success, true); + lf_set_mode(Succeeded); + } + =} + } + + mode Succeeded {} + mode Failed {} } reactor NominalBehavior { - input BatteryOK:bool - - output success:bool - output failure:bool + input BatteryOK:bool + + output success:bool + output failure:bool + + initial mode MoveToObj { + MoveToObjTask = new GenericTask(name="MoveToObj") - initial mode MoveToObj { - MoveToObjTask = new GenericTask(name="MoveToObj") - - MoveToObjTask.failure -> failure - - reaction(MoveToObjTask.success) -> CloseGrip {= - lf_set_mode(CloseGrip); - =} - } + MoveToObjTask.failure -> failure - mode CloseGrip { - CloseGripTask = new GenericTask(name="CloseGrip") - - CloseGripTask.failure -> failure - - reaction(CloseGripTask.success) -> MoveHome {= - lf_set_mode(MoveHome); - =} - } + reaction(MoveToObjTask.success) -> CloseGrip {= + lf_set_mode(CloseGrip); + =} + } + + mode CloseGrip { + CloseGripTask = new GenericTask(name="CloseGrip") - mode MoveHome { - MoveHomeTask = new GenericTask(name="MoveHome") - - MoveHomeTask.failure -> failure - - reaction(MoveHomeTask.success) -> success {= - lf_set(success, true); - =} - } -} - -reactor Robot { - input BatteryOK:bool + CloseGripTask.failure -> failure - output success:bool - output failure:bool + reaction(CloseGripTask.success) -> MoveHome {= + lf_set_mode(MoveHome); + =} + } + + mode MoveHome { + MoveHomeTask = new GenericTask(name="MoveHome") - initial mode Nominal { - NominalBehavior = new NominalBehavior() - - NominalBehavior.success -> success - NominalBehavior.failure -> failure - - reaction(BatteryOK) -> Charging {= - if (!BatteryOK->value) { - lf_set_mode(Charging); - printf("Battery empty\n"); - } - =} - } + MoveHomeTask.failure -> failure - mode Charging { - GoCharge = new GenericTask(name="GoCharge") - - GoCharge.failure -> failure - - reaction(BatteryOK, GoCharge.success) -> Nominal {= - // Assumes simultaneous presence - if (BatteryOK->value && GoCharge.success->value) { - lf_set_mode(Nominal); - printf("Battery charged\n"); - } - =} - } + reaction(MoveHomeTask.success) -> success {= + lf_set(success, true); + =} + } } -main reactor { - timer Battery(1sec, 1sec) - state battery_state:int(1) +reactor Robot { + input BatteryOK:bool + + output success:bool + output failure:bool + + initial mode Nominal { + NominalBehavior = new NominalBehavior() - robot = new Robot() + NominalBehavior.success -> success + NominalBehavior.failure -> failure - reaction(Battery) -> robot.BatteryOK {= - self->battery_state--; - lf_set(robot.BatteryOK, self->battery_state > 0); - if (self->battery_state <= 0) { - self->battery_state = 5; - } + reaction(BatteryOK) -> Charging {= + if (!BatteryOK->value) { + lf_set_mode(Charging); + printf("Battery empty\n"); + } =} + } + + mode Charging { + GoCharge = new GenericTask(name="GoCharge") - reaction(robot.success) {= - printf("Total success\n"); - lf_request_stop(); - =} + GoCharge.failure -> failure - reaction(robot.failure) {= - printf("Utter failure\n"); - lf_request_stop(); + reaction(BatteryOK, GoCharge.success) -> Nominal {= + // Assumes simultaneous presence + if (BatteryOK->value && GoCharge.success->value) { + lf_set_mode(Nominal); + printf("Battery charged\n"); + } =} + } +} -} \ No newline at end of file +main reactor { + timer Battery(1sec, 1sec) + state battery_state:int(1) + + robot = new Robot() + + reaction(Battery) -> robot.BatteryOK {= + self->battery_state--; + lf_set(robot.BatteryOK, self->battery_state > 0); + if (self->battery_state <= 0) { + self->battery_state = 5; + } + =} + + reaction(robot.success) {= + printf("Total success\n"); + lf_request_stop(); + =} + + reaction(robot.failure) {= + printf("Utter failure\n"); + lf_request_stop(); + =} + +} diff --git a/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_simple.lf b/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_simple.lf index f47df525..e15d12b0 100644 --- a/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_simple.lf +++ b/experimental/C/src/ModalModels/BehaviorTrees/robohub_example_simple.lf @@ -12,105 +12,105 @@ target C; reactor GenericTask(name:string("")) { - input start:bool - output success:bool - output failure:bool - - logical action continue_task - - reaction(start, continue_task) -> continue_task, success, failure {= - printf("%s\n", self->name); - int r = rand() % 10; - if (r == 0) { - lf_set(failure, true); - } else if (r >= 6) { - lf_set(success, true); - } else { - lf_schedule(continue_task, MSEC(250)); - } - =} + input start:bool + output success:bool + output failure:bool + + logical action continue_task + + reaction(start, continue_task) -> continue_task, success, failure {= + printf("%s\n", self->name); + int r = rand() % 10; + if (r == 0) { + lf_set(failure, true); + } else if (r >= 6) { + lf_set(success, true); + } else { + lf_schedule(continue_task, MSEC(250)); + } + =} } reactor Robot { - input start:bool - input BatteryOK:bool - output success:bool - output failure:bool + input start:bool + input BatteryOK:bool + output success:bool + output failure:bool + + initial mode Nominal { + MoveToObj = new GenericTask(name="MoveToObj") + CloseGrip = new GenericTask(name="CloseGrip") + MoveHome = new GenericTask(name="MoveHome") - initial mode Nominal { - MoveToObj = new GenericTask(name="MoveToObj") - CloseGrip = new GenericTask(name="CloseGrip") - MoveHome = new GenericTask(name="MoveHome") - - start -> MoveToObj.start // No resume after charging - MoveToObj.success -> CloseGrip.start - CloseGrip.success -> MoveHome.start - MoveHome.success -> success - - MoveToObj.failure -> failure - CloseGrip.failure -> failure - MoveHome.failure -> failure - - // Potential solution for resuming after charging -// reaction(entry) -> MoveToObj.start {= -// // PROBLEM!! -// lf_set(MoveToObj.start, true); -// =} - - reaction(BatteryOK) -> Charging {= - if (!BatteryOK->value) { - lf_set_mode(Charging); - printf("Battery empty\n"); - } - =} - } + start -> MoveToObj.start // No resume after charging + MoveToObj.success -> CloseGrip.start + CloseGrip.success -> MoveHome.start + MoveHome.success -> success - mode Charging { - GoCharge = new GenericTask(name="GoCharge") - - GoCharge.failure -> failure - - // Potential solution for starting task when mode is entered because no start event is provided -// reaction(entry) -> GoCharge.start {= -// lf_set(GoCharge.start, true); -// =} - - reaction(BatteryOK, GoCharge.success) -> Nominal {= - // Assumes simultaneous presence - if (BatteryOK->value && GoCharge.success->value) { - lf_set_mode(Nominal); - printf("Battery charged\n"); - } - =} - } -} - -main reactor { - timer Battery(1sec, 1sec) - state battery_state:int(1) + MoveToObj.failure -> failure + CloseGrip.failure -> failure + MoveHome.failure -> failure - robot = new Robot() + // Potential solution for resuming after charging +// reaction(entry) -> MoveToObj.start {= +// // PROBLEM!! +// lf_set(MoveToObj.start, true); +// =} - reaction(startup) -> robot.start {= - lf_set(robot.start, true); - =} - - reaction(Battery) -> robot.BatteryOK {= - self->battery_state--; - lf_set(robot.BatteryOK, self->battery_state > 0); - if (self->battery_state <= 0) { - self->battery_state = 5; - } + reaction(BatteryOK) -> Charging {= + if (!BatteryOK->value) { + lf_set_mode(Charging); + printf("Battery empty\n"); + } =} + } + + mode Charging { + GoCharge = new GenericTask(name="GoCharge") - reaction(robot.success) {= - printf("Total success\n"); - lf_request_stop(); - =} + GoCharge.failure -> failure - reaction(robot.failure) {= - printf("Utter failure\n"); - lf_request_stop(); + // Potential solution for starting task when mode is entered because no start event is provided +// reaction(entry) -> GoCharge.start {= +// lf_set(GoCharge.start, true); +// =} + + reaction(BatteryOK, GoCharge.success) -> Nominal {= + // Assumes simultaneous presence + if (BatteryOK->value && GoCharge.success->value) { + lf_set_mode(Nominal); + printf("Battery charged\n"); + } =} + } +} + +main reactor { + timer Battery(1sec, 1sec) + state battery_state:int(1) + + robot = new Robot() + + reaction(startup) -> robot.start {= + lf_set(robot.start, true); + =} + + reaction(Battery) -> robot.BatteryOK {= + self->battery_state--; + lf_set(robot.BatteryOK, self->battery_state > 0); + if (self->battery_state <= 0) { + self->battery_state = 5; + } + =} + + reaction(robot.success) {= + printf("Total success\n"); + lf_request_stop(); + =} + + reaction(robot.failure) {= + printf("Utter failure\n"); + lf_request_stop(); + =} } diff --git a/experimental/C/src/ModalModels/Motivation/Chrono/Chrono.lf b/experimental/C/src/ModalModels/Motivation/Chrono/Chrono.lf index 0c4dd72c..3871ec3d 100644 --- a/experimental/C/src/ModalModels/Motivation/Chrono/Chrono.lf +++ b/experimental/C/src/ModalModels/Motivation/Chrono/Chrono.lf @@ -12,147 +12,147 @@ * @author Reinhard von Hanxleden */ target C { - threads: 1, - keepalive: true + threads: 1, + keepalive: true }; /* POSSIBLE NEW SYNTAX FOR DESCRIBING MODES AT LF-LEVEL * Still open questions include * Q1: Do we want hierarchy, ie, mixing state/dataflow across levels as in Ptolemy/SCADE? - modes Stop, Start; // Q2: This is actually redundant - still useful? + modes Stop, Start; // Q2: This is actually redundant - still useful? - mode Stop() { ... // Q3: Should possible successor states ("Start") be listed here as well? - reaction(enter) {= - printf("Entered Stop!"); - } + mode Stop() { ... // Q3: Should possible successor states ("Start") be listed here as well? + reaction(enter) {= + printf("Entered Stop!"); + } - reaction(stst) -> Start {= // Q4: Should transitions be encoded as reactions? - changemode(Start); // Q5: Should this be hostcode? Should this be instantaneous? - =} + reaction(stst) -> Start {= // Q4: Should transitions be encoded as reactions? + changemode(Start); // Q5: Should this be hostcode? Should this be instantaneous? + =} } mode Start() { ... - timer dTimer(10 msec, 10 msec); + timer dTimer(10 msec, 10 msec); - reaction(enter) {= - printf("Entered Start!"); - } + reaction(enter) {= + printf("Entered Start!"); + } - reaction(dTimer) -> d, s, m {= ... =} - - reaction(stst) -> Stop {= - changemode(Stop); - =} + reaction(dTimer) -> d, s, m {= ... =} + + reaction(stst) -> Stop {= + changemode(Stop); + =} } */ reactor ChronoLogic { - input stst:int; - - // time output - output m:int; - output s:int; - output d:int; - - // time state - state dState:int(0); - state sState:int(0); - state mState:int(0); - - // active state - state ststState:int(0); // 0 = STOP, 1 = START - - // provide base time - timer dTimer(10 msec, 10 msec); - - reaction(startup) {= - printf("'x' + 'Enter' kills the program.\n"); - printf("Just 'Enter' alternates between modes STOP and START.\n\n"); - =} - - reaction(stst) {= - self->ststState = 1 - self->ststState; - if (self->ststState) { - printf("Entered START!\n"); - } else { - printf("Entered STOP!\n"); - } - =} + input stst:int; + + // time output + output m:int; + output s:int; + output d:int; + + // time state + state dState:int(0); + state sState:int(0); + state mState:int(0); + + // active state + state ststState:int(0); // 0 = STOP, 1 = START + + // provide base time + timer dTimer(10 msec, 10 msec); + + reaction(startup) {= + printf("'x' + 'Enter' kills the program.\n"); + printf("Just 'Enter' alternates between modes STOP and START.\n\n"); + =} + + reaction(stst) {= + self->ststState = 1 - self->ststState; + if (self->ststState) { + printf("Entered START!\n"); + } else { + printf("Entered STOP!\n"); + } + =} - reaction(dTimer) -> d, s, m {= - if (self->ststState) { - self->dState = (self->dState + 1) % 100; - if (self->dState == 0) { - self->sState = (self->sState + 1) % 60; - if (self->sState == 0) { - self->mState = (self->mState + 1) % 60; - } - } - // Only create outputs for changes - lf_set(d, self->dState); - lf_set(s, self->sState); - lf_set(m, self->mState); + reaction(dTimer) -> d, s, m {= + if (self->ststState) { + self->dState = (self->dState + 1) % 100; + if (self->dState == 0) { + self->sState = (self->sState + 1) % 60; + if (self->sState == 0) { + self->mState = (self->mState + 1) % 60; } - =} + } + // Only create outputs for changes + lf_set(d, self->dState); + lf_set(s, self->sState); + lf_set(m, self->mState); + } + =} } reactor GetUserInput { - preamble {= - void* read_input(void* user_input) { - int c; - while(1) { - c = getchar(); - lf_schedule_copy(user_input, 0, &c, 1); - if (c == EOF) break; - } - return NULL; - } - =} + preamble {= + void* read_input(void* user_input) { + int c; + while(1) { + c = getchar(); + lf_schedule_copy(user_input, 0, &c, 1); + if (c == EOF) break; + } + return NULL; + } + =} - physical action user_input:char; - output stst:int; - - reaction(startup) -> user_input {= - // Start the thread that listens for Enter or Return. - pthread_t thread_id; - pthread_create(&thread_id, NULL, &read_input, user_input); - =} - - reaction(user_input) -> stst {= - if (user_input->value == 'x') { - lf_request_stop(); - } else { - lf_set(stst, 42); - } - =} + physical action user_input:char; + output stst:int; + + reaction(startup) -> user_input {= + // Start the thread that listens for Enter or Return. + pthread_t thread_id; + pthread_create(&thread_id, NULL, &read_input, user_input); + =} + + reaction(user_input) -> stst {= + if (user_input->value == 'x') { + lf_request_stop(); + } else { + lf_set(stst, 42); + } + =} } reactor PrintOutput { - input m:int; - input s:int; - input d:int; - - reaction(m) {= - printf("m = %d, ", m->value); - =} - - reaction(s) {= - printf("s = %d, ", s->value); - =} - - reaction(d) {= - printf("d = %d\n", d->value); - =} + input m:int; + input s:int; + input d:int; + + reaction(m) {= + printf("m = %d, ", m->value); + =} + + reaction(s) {= + printf("s = %d, ", s->value); + =} + + reaction(d) {= + printf("d = %d\n", d->value); + =} } main reactor Chrono { - in = new GetUserInput(); - chrono = new ChronoLogic(); - out = new PrintOutput(); - - in.stst -> chrono.stst; - chrono.m -> out.m; - chrono.s -> out.s; - chrono.d -> out.d; + in = new GetUserInput(); + chrono = new ChronoLogic(); + out = new PrintOutput(); + + in.stst -> chrono.stst; + chrono.m -> out.m; + chrono.s -> out.s; + chrono.d -> out.d; } diff --git a/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf b/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf index 951b26a0..b38b1f91 100644 --- a/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf +++ b/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg.lf @@ -7,124 +7,124 @@ * @author Alexander Schulz-Rosengarten */ target Python{ - threads: 0 + threads: 0 }; reactor Sinewave(sample_rate(125 usec), frequency(440), phase(0)) { - output data; - - timer rate(0, sample_rate); - - state ramp({=itertools.count(0)=}) + output data; + + timer rate(0, sample_rate); + + state ramp({=itertools.count(0)=}) - preamble {= - import math - import itertools - =} - - reaction(rate) -> data {= - x = next(self.ramp) * ((self.frequency * 2 * self.math.pi) * (self.sample_rate / SEC(1))) + self.phase - #print("Sinewave (%f, %f)" % (x, self.math.sin(x))) - data.set(self.math.sin(x)) - =} + preamble {= + import math + import itertools + =} + + reaction(rate) -> data {= + x = next(self.ramp) * ((self.frequency * 2 * self.math.pi) * (self.sample_rate / SEC(1))) + self.phase + #print("Sinewave (%f, %f)" % (x, self.math.sin(x))) + data.set(self.math.sin(x)) + =} } reactor ModalModel(sample_size(10)) { - input data; - output out; - - state sample({=[None] * sample_size=}); - state count(0); - - state _mode(0); # Only present w/o mode support - - // These actions only mimic the Ptolemy structure - logical action processAVG - logical action processMAX - - -// initial mode AVG { + input data; + output out; + + state sample({=[None] * sample_size=}); + state count(0); + + state _mode(0); # Only present w/o mode support + + // These actions only mimic the Ptolemy structure + logical action processAVG + logical action processMAX + + +// initial mode AVG { - /** @label Mode AVG: Collect */ - reaction(data) -> processAVG {= - if self._mode == 0: # Only present w/o mode support - self.sample[self.count] = data.value - self.count += 1 - if self.count == self.sample_size: - self.count = 0 - processAVG.lf_schedule(0) - =} - - /** @label Mode AVG: Process and Transition */ - reaction(processAVG) -> out {= - if self._mode == 0: # Only present w/o mode support - #print("Processing: ", self.sample) - out.set(sum(self.sample) / self.sample_size) - # Transition to MAX - self._mode = 1 # set_mode(MAX) - =} - -// } -// mode MAX { - - /** @label Mode MAX: Collect */ - reaction(data) -> processMAX {= - if self._mode == 1: # Only present w/o mode support - self.sample[self.count] = data.value - self.count += 1 - if self.count == self.sample_size: - self.count = 0 - processMAX.lf_schedule(0) - =} - - /** @label Mode MAX: Process and Transition */ - reaction(processMAX) -> out {= - if self._mode == 1: # Only present w/o mode support - #print("Processing: ", self.sample) - out.set(max(self.sample)) - # Transition to MAX - self._mode = 0 # set_mode(AVG) - =} -// } + /** @label Mode AVG: Collect */ + reaction(data) -> processAVG {= + if self._mode == 0: # Only present w/o mode support + self.sample[self.count] = data.value + self.count += 1 + if self.count == self.sample_size: + self.count = 0 + processAVG.lf_schedule(0) + =} + + /** @label Mode AVG: Process and Transition */ + reaction(processAVG) -> out {= + if self._mode == 0: # Only present w/o mode support + #print("Processing: ", self.sample) + out.set(sum(self.sample) / self.sample_size) + # Transition to MAX + self._mode = 1 # set_mode(MAX) + =} + +// } +// mode MAX { + + /** @label Mode MAX: Collect */ + reaction(data) -> processMAX {= + if self._mode == 1: # Only present w/o mode support + self.sample[self.count] = data.value + self.count += 1 + if self.count == self.sample_size: + self.count = 0 + processMAX.lf_schedule(0) + =} + + /** @label Mode MAX: Process and Transition */ + reaction(processMAX) -> out {= + if self._mode == 1: # Only present w/o mode support + #print("Processing: ", self.sample) + out.set(max(self.sample)) + # Transition to MAX + self._mode = 0 # set_mode(AVG) + =} +// } } reactor Plotter { - input data; - - state plot_data({=[]=}) - - preamble {= - import matplotlib.pyplot as plt - =} - - reaction(startup) {= - self.plt.ion() - self.plt.title("Plot") - - # No idea why multiple draws are required but it works this way - self.plt.draw() - self.plt.pause(0.00000001) - self.plt.draw() - self.plt.pause(0.00000001) - =} - - reaction(data) {= - #print("Plotting ", data.value) - self.plot_data.append(data.value) - self.plt.stem(self.plot_data) - - # No idea why multiple draws are required but it works this way - self.plt.draw() - self.plt.pause(0.00000001) - self.plt.draw() - self.plt.pause(0.00000001) - =} + input data; + + state plot_data({=[]=}) + + preamble {= + import matplotlib.pyplot as plt + =} + + reaction(startup) {= + self.plt.ion() + self.plt.title("Plot") + + # No idea why multiple draws are required but it works this way + self.plt.draw() + self.plt.pause(0.00000001) + self.plt.draw() + self.plt.pause(0.00000001) + =} + + reaction(data) {= + #print("Plotting ", data.value) + self.plot_data.append(data.value) + self.plt.stem(self.plot_data) + + # No idea why multiple draws are required but it works this way + self.plt.draw() + self.plt.pause(0.00000001) + self.plt.draw() + self.plt.pause(0.00000001) + =} } main reactor { - s = new Sinewave(sample_rate = 125 msec, frequency = 0.44) - m = new ModalModel() - p = new Plotter() - s.data -> m.data - m.out -> p.data + s = new Sinewave(sample_rate = 125 msec, frequency = 0.44) + m = new ModalModel() + p = new Plotter() + s.data -> m.data + m.out -> p.data } diff --git a/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf b/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf index 7fb12065..99a71528 100644 --- a/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf +++ b/experimental/C/src/ModalModels/Motivation/SineAvgMax/sine_max_avg_v2.lf @@ -7,60 +7,60 @@ * @author Alexander Schulz-Rosengarten */ target Python{ - threads: 0 + threads: 0 }; import Plotter, Sinewave from "./sine_max_avg.lf" reactor ModalModel(sample_size(10)) { - input data; - output out; - - state sample({=[None] * sample_size=}); - state count(0); - - state _mode(0); # Only present w/o mode support - - logical action process - - reaction(data) -> process {= - if self._mode == 0: # Only present w/o mode support - self.sample[self.count] = data.value - self.count += 1 - if self.count == self.sample_size: - self.count = 0 - process.lf_schedule(0) - =} - -// initial mode AVG { + input data; + output out; + + state sample({=[None] * sample_size=}); + state count(0); + + state _mode(0); # Only present w/o mode support + + logical action process + + reaction(data) -> process {= + if self._mode == 0: # Only present w/o mode support + self.sample[self.count] = data.value + self.count += 1 + if self.count == self.sample_size: + self.count = 0 + process.lf_schedule(0) + =} + +// initial mode AVG { - /** @label Mode AVG: Process and Transition */ - reaction(process) -> out {= - if self._mode == 0: # Only present w/o mode support - print("Processing: ", self.sample) - out.set(sum(self.sample) / self.sample_size) - # Transition to MAX - self._mode = 1 # set_mode(MAX) - =} - -// } -// mode MAX { - - /** @label Mode MAX: Process and Transition */ - reaction(process) -> out {= - if self._mode == 1: # Only present w/o mode support - print("Processing: ", self.sample) - out.set(max(self.sample)) - # Transition to MAX - self._mode = 0 # set_mode(AVG) - =} -// } + /** @label Mode AVG: Process and Transition */ + reaction(process) -> out {= + if self._mode == 0: # Only present w/o mode support + print("Processing: ", self.sample) + out.set(sum(self.sample) / self.sample_size) + # Transition to MAX + self._mode = 1 # set_mode(MAX) + =} + +// } +// mode MAX { + + /** @label Mode MAX: Process and Transition */ + reaction(process) -> out {= + if self._mode == 1: # Only present w/o mode support + print("Processing: ", self.sample) + out.set(max(self.sample)) + # Transition to MAX + self._mode = 0 # set_mode(AVG) + =} +// } } main reactor { - s = new Sinewave(sample_rate = 125 msec, frequency = 0.44) - m = new ModalModel() - p = new Plotter() - s.data -> m.data - m.out -> p.data + s = new Sinewave(sample_rate = 125 msec, frequency = 0.44) + m = new ModalModel() + p = new Plotter() + s.data -> m.data + m.out -> p.data } diff --git a/experimental/C/src/ModalModels/ReflexGame/ModalReflexGame.lf b/experimental/C/src/ModalModels/ReflexGame/ModalReflexGame.lf index a9db172a..2e7d7be3 100644 --- a/experimental/C/src/ModalModels/ReflexGame/ModalReflexGame.lf +++ b/experimental/C/src/ModalModels/ReflexGame/ModalReflexGame.lf @@ -15,7 +15,7 @@ * @author Alexander Schulz-Rosengarten */ target C { - keepalive: true + keepalive: true }; /** @@ -26,41 +26,41 @@ target C { * @param max_time The maximum time between outputs. */ reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { - preamble {= - // Generate a random additional delay over the minimum. - // Assume millisecond precision is enough. - interval_t additional_time(interval_t min_time, interval_t max_time) { - int interval_in_msec = (max_time - min_time) / MSEC(1); - return (rand() % interval_in_msec) * MSEC(1); - } - =} - input another:int; - output out:int; - logical action prompt(min_time); - state count:int(0); + preamble {= + // Generate a random additional delay over the minimum. + // Assume millisecond precision is enough. + interval_t additional_time(interval_t min_time, interval_t max_time) { + int interval_in_msec = (max_time - min_time) / MSEC(1); + return (rand() % interval_in_msec) * MSEC(1); + } + =} + input another:int; + output out:int; + logical action prompt(min_time); + state count:int(0); + + reaction(startup) -> prompt {= + printf("***********************************************\n"); + printf("Watch for the prompt, then hit Return or Enter.\n"); + printf("Type Control-D (EOF) to quit.\n\n"); - reaction(startup) -> prompt {= - printf("***********************************************\n"); - printf("Watch for the prompt, then hit Return or Enter.\n"); - printf("Type Control-D (EOF) to quit.\n\n"); - - // Random number functions are part of stdlib.h, which is included by reactor.h. - // Set a seed for random number generation based on the current time. - srand(time(0)); - - // Schedule the first event. - lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); - =} - reaction(prompt) -> out {= - self->count++; - printf("%d. Hit Return or Enter!", self->count); - fflush(stdout); - lf_set(out, self->count); - =} - reaction(another) -> prompt {= - // Schedule the next event. - lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); - =} + // Random number functions are part of stdlib.h, which is included by reactor.h. + // Set a seed for random number generation based on the current time. + srand(time(0)); + + // Schedule the first event. + lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); + =} + reaction(prompt) -> out {= + self->count++; + printf("%d. Hit Return or Enter!", self->count); + fflush(stdout); + lf_set(out, self->count); + =} + reaction(another) -> prompt {= + // Schedule the next event. + lf_schedule(prompt, additional_time(0, self->max_time - self->min_time)); + =} } /** * Upon receiving a prompt, record the time of the prompt, @@ -69,81 +69,81 @@ reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { * of this event and then report the response time. */ reactor GetUserInput { - preamble {= - // Thread to read input characters until an EOF is received. - // Each time a newline is received, lf_schedule a user_response action. - void* read_input(void* user_response) { - int c; - while(1) { - while((c = getchar()) != '\n') { - if (c == EOF) break; - } - lf_schedule_copy(user_response, 0, &c, 1); - if (c == EOF) break; - } - return NULL; + preamble {= + // Thread to read input characters until an EOF is received. + // Each time a newline is received, lf_schedule a user_response action. + void* read_input(void* user_response) { + int c; + while(1) { + while((c = getchar()) != '\n') { + if (c == EOF) break; } - =} + lf_schedule_copy(user_response, 0, &c, 1); + if (c == EOF) break; + } + return NULL; + } + =} - physical action user_response:char; - state prompt_time:time(0); - state total_time_in_ms:int(0); - state count:int(0); - - input prompt:int; - output another:int; - - reaction(startup) -> user_response {= - // Start the thread that listens for Enter or Return. - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, user_response); + physical action user_response:char; + state prompt_time:time(0); + state total_time_in_ms:int(0); + state count:int(0); + + input prompt:int; + output another:int; + + reaction(startup) -> user_response {= + // Start the thread that listens for Enter or Return. + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, user_response); + =} + + initial mode Preparing { + reaction(prompt) -> Waiting {= + self->prompt_time = lf_time_logical(); + // Switch to mode waiting for user input + lf_set_mode(Waiting); =} - initial mode Preparing { - reaction(prompt) -> Waiting {= - self->prompt_time = lf_time_logical(); - // Switch to mode waiting for user input - lf_set_mode(Waiting); - =} - - reaction(user_response) {= - printf("YOU CHEATED!\n"); - lf_request_stop(); - =} - } - mode Waiting { - reaction(user_response) -> another, Preparing {= - int time_in_ms = (lf_time_logical() - self->prompt_time) / 1000000LL; - printf("Response time in milliseconds: %d\n", time_in_ms); - self->count++; - self->total_time_in_ms += time_in_ms; - // In the original the time was set to zero to encode the mode. This is no longer necessary. - // self->prompt_time = 0LL; - // Trigger another prompt. - lf_set(another, 42); - // Switch to mode waiting for next prompt - lf_set_mode(Preparing); - =} - } - reaction(user_response) {= - if (user_response->value == EOF) { - lf_request_stop(); - return; - } + printf("YOU CHEATED!\n"); + lf_request_stop(); =} - - reaction(shutdown) {= - if (self->count > 0) { - printf("\n**** Average response time: %d.\n", self->total_time_in_ms/self->count); - } else { - printf("\n**** No attempts.\n"); - } + } + mode Waiting { + reaction(user_response) -> another, Preparing {= + int time_in_ms = (lf_time_logical() - self->prompt_time) / 1000000LL; + printf("Response time in milliseconds: %d\n", time_in_ms); + self->count++; + self->total_time_in_ms += time_in_ms; + // In the original the time was set to zero to encode the mode. This is no longer necessary. + // self->prompt_time = 0LL; + // Trigger another prompt. + lf_set(another, 42); + // Switch to mode waiting for next prompt + lf_set_mode(Preparing); =} + } + + reaction(user_response) {= + if (user_response->value == EOF) { + lf_request_stop(); + return; + } + =} + + reaction(shutdown) {= + if (self->count > 0) { + printf("\n**** Average response time: %d.\n", self->total_time_in_ms/self->count); + } else { + printf("\n**** No attempts.\n"); + } + =} } main reactor { - p = new RandomSource(); - g = new GetUserInput(); - p.out -> g.prompt; - g.another -> p.another; -} \ No newline at end of file + p = new RandomSource(); + g = new GetUserInput(); + p.out -> g.prompt; + g.another -> p.another; +} diff --git a/experimental/C/src/Mutations/ScatterGather.lf b/experimental/C/src/Mutations/ScatterGather.lf index faa7cbde..d78b228e 100644 --- a/experimental/C/src/Mutations/ScatterGather.lf +++ b/experimental/C/src/Mutations/ScatterGather.lf @@ -15,114 +15,114 @@ * be abstracted out into an API. */ target C { - threads: 4 + threads: 4 }; reactor Source { - output out:int[]; - reaction(startup) -> out {= - SET_NEW_ARRAY(out, 8); - for (int i=0; i < 8; i++) { - out->value[i] = i; - } - =} + output out:int[]; + reaction(startup) -> out {= + SET_NEW_ARRAY(out, 8); + for (int i=0; i < 8; i++) { + out->value[i] = i; + } + =} } reactor Worker(id:int(2)) { - input worker_in:int; - output worker_out:int; - reaction(worker_in) -> worker_out {= - printf("Worker received in first reaction%d\n", worker_in->value); - lf_set(worker_out, worker_in->value * self->id); - =} - reaction(worker_in) {= - printf("Worker received second %d\n", worker_in->value); - =} + input worker_in:int; + output worker_out:int; + reaction(worker_in) -> worker_out {= + printf("Worker received in first reaction%d\n", worker_in->value); + lf_set(worker_out, worker_in->value * self->id); + =} + reaction(worker_in) {= + printf("Worker received second %d\n", worker_in->value); + =} } reactor ScatterGather2 { - input in:int[]; - output out:int[]; - - // Create a state variable to pass the workers created in - // the first reaction to the second reaction. This is an array - // of pointers to the self struct of the worker. - // The type of a self struct is the reactor class name, converted to - // lower case, followed by _self_t. - state workers:worker_self_t**({=NULL=}); - + input in:int[]; + output out:int[]; + + // Create a state variable to pass the workers created in + // the first reaction to the second reaction. This is an array + // of pointers to the self struct of the worker. + // The type of a self struct is the reactor class name, converted to + // lower case, followed by _self_t. + state workers:worker_self_t**({=NULL=}); + + // The data type of the upstream source does not match the + // data type of the workers' inputs, so we have to create an + // array of places to store the input data. + state worker_inputs:worker_worker_in_t*({=NULL=}); + + // Create a template worker, which ensures that downstream + // levels are correct. The template worker could have any + // number of reactions sensitive to the input we will provide. + template_worker = new Worker(); + reaction(in) -> template_worker.worker_in {= + lf_set(template_worker.worker_in, in->value[0]); + // SCATTER(in, worker_in, Worker, self->workers, id); + // Create an array of pointers to work self structs. + self->workers = malloc(in->length * sizeof(worker_self_t*)); // The data type of the upstream source does not match the // data type of the workers' inputs, so we have to create an // array of places to store the input data. - state worker_inputs:worker_worker_in_t*({=NULL=}); - - // Create a template worker, which ensures that downstream - // levels are correct. The template worker could have any - // number of reactions sensitive to the input we will provide. - template_worker = new Worker(); - reaction(in) -> template_worker.worker_in {= - lf_set(template_worker.worker_in, in->value[0]); - // SCATTER(in, worker_in, Worker, self->workers, id); - // Create an array of pointers to work self structs. - self->workers = malloc(in->length * sizeof(worker_self_t*)); - // The data type of the upstream source does not match the - // data type of the workers' inputs, so we have to create an - // array of places to store the input data. - self->worker_inputs = malloc(in->length * sizeof(worker_worker_in_t)); - pthread_mutex_lock(&mutex); - for (int i=1; i < in->length; i++) { - printf("Initializing worker %d\n", i); - self->workers[i] = new_Worker(); - self->workers[i]->id = i; - printf("HERE %d\n", i); - self->workers[i]->_lf_worker_in = &self->worker_inputs[i]; - self->worker_inputs[i].is_present = true; - self->worker_inputs[i].value = in->value[i]; // Copies the value or pointer. - self->worker_inputs[i].num_destinations = 1; - trigger_t worker_trigger = self->workers[i]->_lf__worker_in; - for(int j = 0; j < worker_trigger.number_of_reactions; j++) { - worker_trigger.reactions[j]->index = self->_lf__reaction_0.index + j + 1; - // The chain_id is the same for each worker, which ensures that the - // second reaction below will not be invoked before all the workers - // have finished. The second reaction below has a larger level than - // the workers, and since the chain_id overlaps, the workers must - // finish before the reaction will execute. - worker_trigger.reactions[j]->chain_id = self->_lf__reaction_0.chain_id; - pqueue_insert(reaction_q, worker_trigger.reactions[j]); - } - printf("Initialized worker %d\n", i); - } - // Signal all the worker threads. - pthread_cond_broadcast(&reaction_q_changed); - pthread_mutex_unlock(&mutex); - =} - reaction(in, template_worker.worker_out) -> out {= - SET_NEW_ARRAY(out, in->length); - // FIXME: We should be checking template_worker.worker_out_is_present. - // But what do we do if it is not present? - out->value[0] = template_worker.worker_out->value; - for (int i=1; i < in->length; i++) { - printf("Gather received %d at index %d.\n", self->workers[i]->_lf_worker_out.value, i); - out->value[i] = self->workers[i]->_lf_worker_out.value; - } - // FIXME: Invoke the destructor for each of the workers, once - // they have a destructor. - =} + self->worker_inputs = malloc(in->length * sizeof(worker_worker_in_t)); + pthread_mutex_lock(&mutex); + for (int i=1; i < in->length; i++) { + printf("Initializing worker %d\n", i); + self->workers[i] = new_Worker(); + self->workers[i]->id = i; + printf("HERE %d\n", i); + self->workers[i]->_lf_worker_in = &self->worker_inputs[i]; + self->worker_inputs[i].is_present = true; + self->worker_inputs[i].value = in->value[i]; // Copies the value or pointer. + self->worker_inputs[i].num_destinations = 1; + trigger_t worker_trigger = self->workers[i]->_lf__worker_in; + for(int j = 0; j < worker_trigger.number_of_reactions; j++) { + worker_trigger.reactions[j]->index = self->_lf__reaction_0.index + j + 1; + // The chain_id is the same for each worker, which ensures that the + // second reaction below will not be invoked before all the workers + // have finished. The second reaction below has a larger level than + // the workers, and since the chain_id overlaps, the workers must + // finish before the reaction will execute. + worker_trigger.reactions[j]->chain_id = self->_lf__reaction_0.chain_id; + pqueue_insert(reaction_q, worker_trigger.reactions[j]); + } + printf("Initialized worker %d\n", i); + } + // Signal all the worker threads. + pthread_cond_broadcast(&reaction_q_changed); + pthread_mutex_unlock(&mutex); + =} + reaction(in, template_worker.worker_out) -> out {= + SET_NEW_ARRAY(out, in->length); + // FIXME: We should be checking template_worker.worker_out_is_present. + // But what do we do if it is not present? + out->value[0] = template_worker.worker_out->value; + for (int i=1; i < in->length; i++) { + printf("Gather received %d at index %d.\n", self->workers[i]->_lf_worker_out.value, i); + out->value[i] = self->workers[i]->_lf_worker_out.value; + } + // FIXME: Invoke the destructor for each of the workers, once + // they have a destructor. + =} } reactor Print { - input in:int[]; - reaction(in) {= - printf("["); - for(int i = 0; i < in->length; i++) { - printf("%d", in->value[i]); - if (i < in->length - 1) { - printf(", "); - } - } - printf("]\n"); - =} + input in:int[]; + reaction(in) {= + printf("["); + for(int i = 0; i < in->length; i++) { + printf("%d", in->value[i]); + if (i < in->length - 1) { + printf(", "); + } + } + printf("]\n"); + =} } main reactor { - s = new Source(); - g = new ScatterGather2(); - p = new Print(); - s.out -> g.in; - g.out -> p.in; -} \ No newline at end of file + s = new Source(); + g = new ScatterGather2(); + p = new Print(); + s.out -> g.in; + g.out -> p.in; +} diff --git a/experimental/C/src/Mutations/SieveOfEratosthenes.lf b/experimental/C/src/Mutations/SieveOfEratosthenes.lf index c0e24db0..32305ba1 100644 --- a/experimental/C/src/Mutations/SieveOfEratosthenes.lf +++ b/experimental/C/src/Mutations/SieveOfEratosthenes.lf @@ -17,121 +17,121 @@ * @author Edward A. Lee */ target C { - fast: true + fast: true } reactor Count { - timer t(0, 1 sec); - output out:int; - // Start at 3 because 1 and 2 are taken care of. - state c:int(3); - reaction(t) -> out {= - lf_set(out, self->c); - // printf("Count sent %d\n", self->c); - self->c++; - =} + timer t(0, 1 sec); + output out:int; + // Start at 3 because 1 and 2 are taken care of. + state c:int(3); + reaction(t) -> out {= + lf_set(out, self->c); + // printf("Count sent %d\n", self->c); + self->c++; + =} } reactor Filter(prime:int(2)) { - input in:int; - output out:int; - - reaction(in) -> out {= - // printf("Filter for prime %d received %d\n", self->prime, in->value); - if (in->value % self->prime != 0) { - // printf("Filter forwarding %d\n", in->value); - lf_set(out, in->value); - } - =} + input in:int; + output out:int; + + reaction(in) -> out {= + // printf("Filter for prime %d received %d\n", self->prime, in->value); + if (in->value % self->prime != 0) { + // printf("Filter forwarding %d\n", in->value); + lf_set(out, in->value); + } + =} } reactor Sieve { - input in:int; - output out:int; + input in:int; + output out:int; + + // Create the first filter. + filter2 = new Filter(prime = 2); + in -> filter2.in; + + // React to a new prime number. + reaction(filter2.out) -> out {= + // Forward the new prime number. + // printf("Sieve received prime: %d\n", filter2.out->value); + lf_set(out, filter2.out->value); - // Create the first filter. - filter2 = new Filter(prime = 2); - in -> filter2.in; + // Splice in a new Filter. + filter_self_t* filter = new_Filter(); + filter->prime = filter2.out->value; + filter->_lf_out_width = -2; // FIXME: Why isn't this done by the constructor? + filter->_lf_in_width = -2; // FIXME: Why isn't this done by the constructor? + filter->_lf__reaction_0.num_outputs = 1; + filter->_lf__reaction_0.triggers = (trigger_t***)calloc(1, sizeof(trigger_t**)); + filter->_lf__reaction_0.triggered_sizes = (int*)calloc(1, sizeof(int)); + filter->_lf__reaction_0.output_produced = (bool**)calloc(1, sizeof(bool*)); + filter->_lf__reaction_0.output_produced[0] = &filter->_lf_out.is_present; - // React to a new prime number. - reaction(filter2.out) -> out {= - // Forward the new prime number. - // printf("Sieve received prime: %d\n", filter2.out->value); - lf_set(out, filter2.out->value); - - // Splice in a new Filter. - filter_self_t* filter = new_Filter(); - filter->prime = filter2.out->value; - filter->_lf_out_width = -2; // FIXME: Why isn't this done by the constructor? - filter->_lf_in_width = -2; // FIXME: Why isn't this done by the constructor? - filter->_lf__reaction_0.num_outputs = 1; - filter->_lf__reaction_0.triggers = (trigger_t***)calloc(1, sizeof(trigger_t**)); - filter->_lf__reaction_0.triggered_sizes = (int*)calloc(1, sizeof(int)); - filter->_lf__reaction_0.output_produced = (bool**)calloc(1, sizeof(bool*)); - filter->_lf__reaction_0.output_produced[0] = &filter->_lf_out.is_present; - - // Get a pointer to the self struct of the last reactor in the chain. - assert(self->_lf__reaction_0.last_enabling_reaction == &last->_lf__reaction_0); - filter_self_t *last = (filter_self_t*)self->_lf__reaction_0.last_enabling_reaction->self; - - // Adjust last_enabling_reaction for optimized execution of a chain. - self->_lf__reaction_0.last_enabling_reaction = &filter->_lf__reaction_0; - filter->_lf__reaction_0.last_enabling_reaction = &last->_lf__reaction_0; - - filter->_lf_out.num_destinations = 1; - - filter->_lf__reaction_0.triggered_sizes[0] = 1; - trigger_t** trigger_array = (trigger_t**)calloc(1, sizeof(trigger_t*)); - filter->_lf__reaction_0.triggers[0] = trigger_array; - - // Inherit destination triggers from the last filter in the chain. - filter->_lf__reaction_0.triggers[0][0] = last->_lf__reaction_0.triggers[0][0]; - - // Update the triggers of the last reactor to point to the new filter... - last->_lf__reaction_0.triggers[0][0] = &filter->_lf__in; - - // The input data of the new filter comes from the output of the last filter. - filter->_lf_in = (filter_in_t*)&last->_lf_out; - - // The container's reaction gets its data from the new filter from now on. - self->_lf_filter2.out = (filter_out_t*)&filter->_lf_out; + // Get a pointer to the self struct of the last reactor in the chain. + assert(self->_lf__reaction_0.last_enabling_reaction == &last->_lf__reaction_0); + filter_self_t *last = (filter_self_t*)self->_lf__reaction_0.last_enabling_reaction->self; + + // Adjust last_enabling_reaction for optimized execution of a chain. + self->_lf__reaction_0.last_enabling_reaction = &filter->_lf__reaction_0; + filter->_lf__reaction_0.last_enabling_reaction = &last->_lf__reaction_0; + + filter->_lf_out.num_destinations = 1; + + filter->_lf__reaction_0.triggered_sizes[0] = 1; + trigger_t** trigger_array = (trigger_t**)calloc(1, sizeof(trigger_t*)); + filter->_lf__reaction_0.triggers[0] = trigger_array; - // Unfortunately, need to reallocate the arrays that indicate the is_present - // fields because now there are two more. - // FIXME: This should be done in large chunks rather than on each call? - bool** new_memory = (bool**)realloc(_lf_is_present_fields, (_lf_is_present_fields_size + 1) * sizeof(bool*)); - if (new_memory == NULL) { - lf_print_error("Out of memory!"); - lf_request_stop(); - } else { - _lf_is_present_fields = new_memory; - - new_memory = (bool**)realloc(_lf_is_present_fields_abbreviated, (_lf_is_present_fields_size + 1) * sizeof(bool*)); - if (new_memory == NULL) { - lf_print_error("Out of memory!"); - lf_request_stop(); - } else { - _lf_is_present_fields_abbreviated = new_memory; - } - _lf_is_present_fields_size += 1; - - _lf_is_present_fields[_lf_is_present_fields_size - 1] = &filter->_lf_out.is_present; - - // NOTE: The level of the reaction in the newly created reactor should be set - // and downstream reactions incremented, but levels have no effect on this system. - // In fact, the reaction queue never has more than one item on it. - } - =} + // Inherit destination triggers from the last filter in the chain. + filter->_lf__reaction_0.triggers[0][0] = last->_lf__reaction_0.triggers[0][0]; + + // Update the triggers of the last reactor to point to the new filter... + last->_lf__reaction_0.triggers[0][0] = &filter->_lf__in; + + // The input data of the new filter comes from the output of the last filter. + filter->_lf_in = (filter_in_t*)&last->_lf_out; + + // The container's reaction gets its data from the new filter from now on. + self->_lf_filter2.out = (filter_out_t*)&filter->_lf_out; + + // Unfortunately, need to reallocate the arrays that indicate the is_present + // fields because now there are two more. + // FIXME: This should be done in large chunks rather than on each call? + bool** new_memory = (bool**)realloc(_lf_is_present_fields, (_lf_is_present_fields_size + 1) * sizeof(bool*)); + if (new_memory == NULL) { + lf_print_error("Out of memory!"); + lf_request_stop(); + } else { + _lf_is_present_fields = new_memory; + + new_memory = (bool**)realloc(_lf_is_present_fields_abbreviated, (_lf_is_present_fields_size + 1) * sizeof(bool*)); + if (new_memory == NULL) { + lf_print_error("Out of memory!"); + lf_request_stop(); + } else { + _lf_is_present_fields_abbreviated = new_memory; + } + _lf_is_present_fields_size += 1; + + _lf_is_present_fields[_lf_is_present_fields_size - 1] = &filter->_lf_out.is_present; + + // NOTE: The level of the reaction in the newly created reactor should be set + // and downstream reactions incremented, but levels have no effect on this system. + // In fact, the reaction queue never has more than one item on it. + } + =} } reactor Print(stop_after:int(10000)) { - input in:int; - state count:int(1); - reaction(in) {= - printf("Prime %d: %d\n", self->count++, in->value); - if (self->count > self->stop_after) lf_request_stop(); - =} + input in:int; + state count:int(1); + reaction(in) {= + printf("Prime %d: %d\n", self->count++, in->value); + if (self->count > self->stop_after) lf_request_stop(); + =} } main reactor { - c = new Count(); - s = new Sieve(); - p = new Print(); - c.out -> s.in; - s.out -> p.in; -} \ No newline at end of file + c = new Count(); + s = new Sieve(); + p = new Print(); + c.out -> s.in; + s.out -> p.in; +} diff --git a/experimental/C/src/PowerTrain/PowerTrain.lf b/experimental/C/src/PowerTrain/PowerTrain.lf index 448d7db0..ea6cc96d 100644 --- a/experimental/C/src/PowerTrain/PowerTrain.lf +++ b/experimental/C/src/PowerTrain/PowerTrain.lf @@ -13,231 +13,231 @@ target C {threads: 1, keepalive: true, flags: "-lncurses"}; * @author Marten Lohstroh */ preamble {= - #include - #include - #include + #include + #include + #include - struct { - void* brake; - void* accelerate; - } pedals; + struct { + void* brake; + void* accelerate; + } pedals; - int calc_brake_force(int angle) { // ~0-600 Nm in Tesla Model 3 - return 110 * angle; - } + int calc_brake_force(int angle) { // ~0-600 Nm in Tesla Model 3 + return 110 * angle; + } - int calc_motor_torque(int angle) { - return 100 * angle; - } + int calc_motor_torque(int angle) { + return 100 * angle; + } - void* read_input(void* arg) { - initscr(); - noecho(); // Don't echo input - cbreak(); // Don't interrupt for user input - timeout(1); // Wait for key press in ms - printw("***************************************************************\n"); - printw("Press '1-6' to change the angle of the accelerator\n"); - printw("Press 'q-y' to change the angle of the brake pedal\n"); - char c = 0; // Command: [1-6|q-y] - int v = 0; - while (true) { - bool skip = false; // Whether to lf_schedule and event or not - c = getch(); - switch(c) { - case 'q': - v = 0; - break; - case 'w': - v = 1; - break; - case 'e': - v = 2; - break; - case 'r': - v = 3; - break; - case 't': - v = 4; - break; - case 'y': - v = 5; - break; - default: - skip = true; - break; - } - - if (!skip) { - lf_schedule_int(pedals.brake, 0, v); - } - - skip = false; - switch(c) { - case '1': - v = 0; - break; - case '2': - v = 1; - break; - case '3': - v = 2; - break; - case '4': - v = 3; - break; - case '5': - v = 4; - break; - case '6': - v = 5; - break; - default: - skip = true; - break; - } - - if (!skip) { - lf_schedule_int(pedals.accelerate, 0, v); - } - } - endwin(); - return 0; - } - pthread_t curses_thread; - bool initialized = false; - void init_sensors() { - if (!initialized) { - pthread_create(&curses_thread, NULL, &read_input, NULL); - initialized = true; - } + void* read_input(void* arg) { + initscr(); + noecho(); // Don't echo input + cbreak(); // Don't interrupt for user input + timeout(1); // Wait for key press in ms + printw("***************************************************************\n"); + printw("Press '1-6' to change the angle of the accelerator\n"); + printw("Press 'q-y' to change the angle of the brake pedal\n"); + char c = 0; // Command: [1-6|q-y] + int v = 0; + while (true) { + bool skip = false; // Whether to lf_schedule and event or not + c = getch(); + switch(c) { + case 'q': + v = 0; + break; + case 'w': + v = 1; + break; + case 'e': + v = 2; + break; + case 'r': + v = 3; + break; + case 't': + v = 4; + break; + case 'y': + v = 5; + break; + default: + skip = true; + break; + } + + if (!skip) { + lf_schedule_int(pedals.brake, 0, v); + } + + skip = false; + switch(c) { + case '1': + v = 0; + break; + case '2': + v = 1; + break; + case '3': + v = 2; + break; + case '4': + v = 3; + break; + case '5': + v = 4; + break; + case '6': + v = 5; + break; + default: + skip = true; + break; + } + + if (!skip) { + lf_schedule_int(pedals.accelerate, 0, v); + } + } + endwin(); + return 0; + } + pthread_t curses_thread; + bool initialized = false; + void init_sensors() { + if (!initialized) { + pthread_create(&curses_thread, NULL, &read_input, NULL); + initialized = true; } + } =} reactor MotorControl { - - input brkOn:bool; - input angle:int; - output torque:int; - state braking:bool(true); - - //@label Set torque to zero if car is braking - reaction(brkOn) -> torque {= - self->braking = brkOn->value; - lf_set(torque, 0); - =} - - //@label Adjust torque unless car is braking - reaction(angle) -> torque {= - if (!self->braking) { - lf_set(torque, calc_motor_torque(angle->value)); - } else if (angle->value > 0){ - printw("Cannot accelerate; release brake pedal first.\n"); - } - =} + + input brkOn:bool; + input angle:int; + output torque:int; + state braking:bool(true); + + //@label Set torque to zero if car is braking + reaction(brkOn) -> torque {= + self->braking = brkOn->value; + lf_set(torque, 0); + =} + + //@label Adjust torque unless car is braking + reaction(angle) -> torque {= + if (!self->braking) { + lf_set(torque, calc_motor_torque(angle->value)); + } else if (angle->value > 0){ + printw("Cannot accelerate; release brake pedal first.\n"); + } + =} } reactor BrakePedal { - output angle:int; - output applied:bool; - - physical action a(0, 1 msec, "replace"):int; - state last:int(1); - - // @label Setup callback - reaction(startup) -> a {= - pedals.brake = a; - init_sensors(); - =} - - // @label Output reported angle - reaction(a) -> angle, applied {= - if (self->last != a->value) { - if (self->last == 0) { - lf_set(applied, true); // zero to nonzero - } else if (a->value == 0) { - lf_set(applied, false); // nonzero to zero - } - - self->last = a->value; - - lf_set(angle, a->value); - } - =} + output angle:int; + output applied:bool; + + physical action a(0, 1 msec, "replace"):int; + state last:int(1); + + // @label Setup callback + reaction(startup) -> a {= + pedals.brake = a; + init_sensors(); + =} + + // @label Output reported angle + reaction(a) -> angle, applied {= + if (self->last != a->value) { + if (self->last == 0) { + lf_set(applied, true); // zero to nonzero + } else if (a->value == 0) { + lf_set(applied, false); // nonzero to zero + } + + self->last = a->value; + + lf_set(angle, a->value); + } + =} } reactor Accelerator { - - state pedal:int(-1); - physical action a(0, 2 msec, "replace"):int; - output angle:int; - state last:int(0); - - // @label Setup callback - reaction(startup) -> a {= - pedals.accelerate = a; - init_sensors(); - =} - - // @label Output reported angle - reaction(a) -> angle {= - if (self->last != a->value) { - lf_set(angle, a->value); - self->last = a->value; - } - =} - + + state pedal:int(-1); + physical action a(0, 2 msec, "replace"):int; + output angle:int; + state last:int(0); + + // @label Setup callback + reaction(startup) -> a {= + pedals.accelerate = a; + init_sensors(); + =} + + // @label Output reported angle + reaction(a) -> angle {= + if (self->last != a->value) { + lf_set(angle, a->value); + self->last = a->value; + } + =} + } // @label Apply the requested force reactor Brakes { - input force:int; - - // @label Reaction with deadline - reaction(force) {= - printw("Adjusting brake power to %dN; on time!\n", force->value); - =} deadline (2 msec) {= - printw("Adjusting brake power to %dN; too late!\n", force->value); - =} + input force:int; + + // @label Reaction with deadline + reaction(force) {= + printw("Adjusting brake power to %dN; on time!\n", force->value); + =} deadline (2 msec) {= + printw("Adjusting brake power to %dN; too late!\n", force->value); + =} } // @label Apply the requested torque reactor Motor { - input torque:int; - - // @label Reaction with deadline - reaction(torque) {= - printw("Adjusting engine torque to %dNm; on time!\n", torque->value); - =} deadline (3 msec) {= - printw("Adjusting engine torque to %dNm; too late!\n", torque->value); - =} + input torque:int; + + // @label Reaction with deadline + reaction(torque) {= + printw("Adjusting engine torque to %dNm; on time!\n", torque->value); + =} deadline (3 msec) {= + printw("Adjusting engine torque to %dNm; too late!\n", torque->value); + =} } // @label Adjust the force reactor BrakeControl { - input angle:int; - output force:int; - - reaction(angle) -> force {= - lf_set(force, calc_brake_force(angle->value)); - =} + input angle:int; + output force:int; + + reaction(angle) -> force {= + lf_set(force, calc_brake_force(angle->value)); + =} } /** * Reactor that implements a simplified power train control module. */ main reactor PowerTrain { - - bp = new BrakePedal(); - a = new Accelerator(); - bc = new BrakeControl(); - mc = new MotorControl(); - b = new Brakes(); - m = new Motor(); - - bp.angle -> bc.angle; - bc.force -> b.force; - bp.applied -> mc.brkOn; - mc.torque -> m.torque; - a.angle -> mc.angle; - + + bp = new BrakePedal(); + a = new Accelerator(); + bc = new BrakeControl(); + mc = new MotorControl(); + b = new Brakes(); + m = new Motor(); + + bp.angle -> bc.angle; + bc.force -> b.force; + bp.applied -> mc.brkOn; + mc.torque -> m.torque; + a.angle -> mc.angle; + } diff --git a/experimental/C/src/RecursiveInstantiation/SieveOfErastothenes.lf b/experimental/C/src/RecursiveInstantiation/SieveOfErastothenes.lf index c5742f78..12cfb0f6 100644 --- a/experimental/C/src/RecursiveInstantiation/SieveOfErastothenes.lf +++ b/experimental/C/src/RecursiveInstantiation/SieveOfErastothenes.lf @@ -7,58 +7,58 @@ target C { fast: true, Build-Type: Debug } reactor Count(start: int(2)) { - timer t(0, 1 sec); - output out: int; - state c: int(start); - reaction(t) -> out {= - lf_set(out, self->c++); - =} + timer t(0, 1 sec); + output out: int; + state c: int(start); + reaction(t) -> out {= + lf_set(out, self->c++); + =} } reactor Filter { - input n_in: int - output n_out: int - output result: int - input initialize: int + input n_in: int + output n_out: int + output result: int + input initialize: int - reset state p: int(0) + reset state p: int(0) - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } - mode Filter { - end = new Filter() + mode Filter { + end = new Filter() - end.result -> result + end.result -> result - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Print(stop_after:int(10000)) { - input in:int; - state count:int(1); - reaction(in) {= - printf("Prime %d: %d\n", self->count++, in->value); - if (self->count > self->stop_after) lf_request_stop(); - =} + input in:int; + state count:int(1); + reaction(in) {= + printf("Prime %d: %d\n", self->count++, in->value); + if (self->count > self->stop_after) lf_request_stop(); + =} } main reactor SieveOfErastothenes { - count = new Count() - filter = new Filter() - print = new Print(stop_after=10) + count = new Count() + filter = new Filter() + print = new Print(stop_after=10) - count.out -> filter.n_in - filter.result -> print.in + count.out -> filter.n_in + filter.result -> print.in } diff --git a/experimental/C/src/RecursiveInstantiation/SieveOfErastothenesUnrolled.lf b/experimental/C/src/RecursiveInstantiation/SieveOfErastothenesUnrolled.lf index 05826e0e..5634306d 100644 --- a/experimental/C/src/RecursiveInstantiation/SieveOfErastothenesUnrolled.lf +++ b/experimental/C/src/RecursiveInstantiation/SieveOfErastothenesUnrolled.lf @@ -8,358 +8,358 @@ target C { fast: true, Build-Type: Debug } reactor Count(start: int(2)) { - timer t(0, 1 sec); - output out: int; - state c: int(start); - reaction(t) -> out {= - lf_set(out, self->c++); - =} + timer t(0, 1 sec); + output out: int; + state c: int(start); + reaction(t) -> out {= + lf_set(out, self->c++); + =} } reactor Filter0 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter1() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter1() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter1 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter2() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter2() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter2 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter3() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter3() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter3 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter4() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter4() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter4 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter5() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter5() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter5 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter6() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter6() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter6 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter7() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter7() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter7 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter8() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter8() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter8 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter9() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter9() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter9 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - end = new Filter10() - - end.result -> result - - reaction(n_in) -> end.n_in {= - if (n_in->value % self->p != 0) { - lf_set(end.n_in, n_in->value); - } - =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + end = new Filter10() + + end.result -> result + + reaction(n_in) -> end.n_in {= + if (n_in->value % self->p != 0) { + lf_set(end.n_in, n_in->value); + } + =} + } } reactor Filter10 { - input n_in: int - output n_out: int - output result: int - input initialize: int - - reset state p: int(0) - - initial mode End { - reaction(n_in) -> result, reset(Filter) {= - lf_set(result, n_in->value); - lf_set_mode(Filter); - assert(self->p == 0); // This reaction can only be triggered once - self->p = n_in->value; - =} - } - - mode Filter { - // end = new Filter11() - - // end.result -> result - - // reaction(n_in) -> end.n_in {= - // if (n_in->value % self->p != 0) { - // lf_set(end.n_in, n_in->value); - // } - // =} - } + input n_in: int + output n_out: int + output result: int + input initialize: int + + reset state p: int(0) + + initial mode End { + reaction(n_in) -> result, reset(Filter) {= + lf_set(result, n_in->value); + lf_set_mode(Filter); + assert(self->p == 0); // This reaction can only be triggered once + self->p = n_in->value; + =} + } + + mode Filter { + // end = new Filter11() + + // end.result -> result + + // reaction(n_in) -> end.n_in {= + // if (n_in->value % self->p != 0) { + // lf_set(end.n_in, n_in->value); + // } + // =} + } } reactor Print(stop_after:int(10000)) { - input in:int; - state count:int(1); - reaction(in) {= - printf("Prime %d: %d\n", self->count++, in->value); - if (self->count > self->stop_after) lf_request_stop(); - =} + input in:int; + state count:int(1); + reaction(in) {= + printf("Prime %d: %d\n", self->count++, in->value); + if (self->count > self->stop_after) lf_request_stop(); + =} } main reactor SieveOfErastothenesUnrolled { - count = new Count() - filter = new Filter0() - print = new Print(stop_after=10) + count = new Count() + filter = new Filter0() + print = new Print(stop_after=10) - count.out -> filter.n_in - filter.result -> print.in + count.out -> filter.n_in + filter.result -> print.in } diff --git a/experimental/C/src/Safety/Cutter.lf b/experimental/C/src/Safety/Cutter.lf index 2e2ed0b0..7e49e8e3 100644 --- a/experimental/C/src/Safety/Cutter.lf +++ b/experimental/C/src/Safety/Cutter.lf @@ -12,80 +12,80 @@ * priority and time-sensitive operations are monitored for deadline violations. */ target C { - workers: 2, - keepalive: true, - files: [ - "/lib/c/reactor-c/util/sensor_simulator.c", - "/lib/c/reactor-c/util/sensor_simulator.h" - ], - cmake-include: ["/lib/c/reactor-c/util/sensor_simulator.cmake"], - build-type: RelWithDebInfo // Release with debug info + workers: 2, + keepalive: true, + files: [ + "/lib/c/reactor-c/util/sensor_simulator.c", + "/lib/c/reactor-c/util/sensor_simulator.h" + ], + cmake-include: ["/lib/c/reactor-c/util/sensor_simulator.cmake"], + build-type: RelWithDebInfo // Release with debug info } preamble {= - #include "sensor_simulator.h" - char* messages[] = {"Cutter Simulator"}; - int num_messages = 1; + #include "sensor_simulator.h" + char* messages[] = {"Cutter Simulator"}; + int num_messages = 1; =} reactor ButtonController(key: char = '\0', description: string = "Button") { - physical action pushed: char* - output push: bool - timer t(1 ms) // Give time for the startup in main to have occurred. + physical action pushed: char* + output push: bool + timer t(1 ms) // Give time for the startup in main to have occurred. - reaction(t) -> pushed {= - register_sensor_key(self->key, pushed); - if (self->key == '\0') { - lf_print("%s with any key", self->description); - } else { - lf_print("%s with '%c'", self->description, self->key); - } - =} + reaction(t) -> pushed {= + register_sensor_key(self->key, pushed); + if (self->key == '\0') { + lf_print("%s with any key", self->description); + } else { + lf_print("%s with '%c'", self->description, self->key); + } + =} - reaction(pushed) -> push {= lf_set(push, true); =} + reaction(pushed) -> push {= lf_set(push, true); =} } reactor MachineController { - input emergencyStop: bool - input run: bool + input emergencyStop: bool + input run: bool - initial mode Off { - reaction(run) -> reset(Running) {= lf_set_mode(Running); =} - } + initial mode Off { + reaction(run) -> reset(Running) {= lf_set_mode(Running); =} + } - mode Running { - timer t(0, 100 msec) - reaction(emergencyStop) -> reset(Off) {= - if (emergencyStop) lf_set_mode(Off); - =} deadline(1 msec) {= - lf_print("DEADLINE VIOLATION!"); - lf_set_mode(Off); - =} + mode Running { + timer t(0, 100 msec) + reaction(emergencyStop) -> reset(Off) {= + if (emergencyStop) lf_set_mode(Off); + =} deadline(1 msec) {= + lf_print("DEADLINE VIOLATION!"); + lf_set_mode(Off); + =} - // With centralized coordination, time cannot advance to trigger this - // reaction unless null messages have arrived on emergencyStop. - reaction(t) -> reset(Off) {= show_tick("*"); =} deadline(10 msec) {= - lf_set_mode(Off); - =} - } + // With centralized coordination, time cannot advance to trigger this + // reaction unless null messages have arrived on emergencyStop. + reaction(t) -> reset(Off) {= show_tick("*"); =} deadline(10 msec) {= + lf_set_mode(Off); + =} + } } main reactor { - eStop = new ButtonController(key = 'e', description = "Emergency stop") - run = new ButtonController(key = 'r', description = "Run") - m = new MachineController() + eStop = new ButtonController(key = 'e', description = "Emergency stop") + run = new ButtonController(key = 'r', description = "Run") + m = new MachineController() - eStop.push -> m.emergencyStop - run.push -> m.run + eStop.push -> m.emergencyStop + run.push -> m.run - physical action stop: char* + physical action stop: char* - reaction(startup) -> stop {= - // This has to be done exactly once. - start_sensor_simulator(messages, num_messages, 16, NULL, LOG_LEVEL_INFO); - register_sensor_key('x', stop); - lf_print("Exit with 'x'"); - =} + reaction(startup) -> stop {= + // This has to be done exactly once. + start_sensor_simulator(messages, num_messages, 16, NULL, LOG_LEVEL_INFO); + register_sensor_key('x', stop); + lf_print("Exit with 'x'"); + =} - reaction(stop) {= lf_request_stop(); =} + reaction(stop) {= lf_request_stop(); =} } diff --git a/experimental/C/src/SolarUAV/SolarUAV.lf b/experimental/C/src/SolarUAV/SolarUAV.lf index a6852bb7..ab8fc3ab 100644 --- a/experimental/C/src/SolarUAV/SolarUAV.lf +++ b/experimental/C/src/SolarUAV/SolarUAV.lf @@ -9,19 +9,19 @@ * * There are 3 main paths in the LF program: * * Flight control path: Its goal is to maintain the UAV stable and moving, - so it does not crash. It is hard real-time and it includes the following - reactors: IMUSensor, GPSSensor, KalmanFilter, Controller and Actuator. + so it does not crash. It is hard real-time and it includes the following + reactors: IMUSensor, GPSSensor, KalmanFilter, Controller and Actuator. * * Mission critical path: This path identifies the reactors that are realtive - to the mission success, which is covering the entire target zone. To this - end, the Planner will compute where to go, based on target zone and the - coverage. This is hard real-time as well, and the reactors involved are: - IMUSensor, GPSSensor, Gimbal, Camera, BatterySensor, ComIn, ImageStitching, - Planner, Controller, and Actuator. + to the mission success, which is covering the entire target zone. To this + end, the Planner will compute where to go, based on target zone and the + coverage. This is hard real-time as well, and the reactors involved are: + IMUSensor, GPSSensor, Gimbal, Camera, BatterySensor, ComIn, ImageStitching, + Planner, Controller, and Actuator. * * Non mission critical path: Processing the stitched images should only happen - whenever there are empty time slots and enough power. Reactors in this - path are: ImageProcessing and ComOut and this is soft real-time. - FIXME: Reactions in this path can/need to be preambtable. LF semantics, - however, say that reactions are atomic... + whenever there are empty time slots and enough power. Reactors in this + path are: ImageProcessing and ComOut and this is soft real-time. + FIXME: Reactions in this path can/need to be preambtable. LF semantics, + however, say that reactions are atomic... * * @author Mirco Theile * @author Chadlia Jerad @@ -30,8 +30,8 @@ */ target C { - keepalive: true - // FIXME: add the structures. Currently everything is either int or void. + keepalive: true + // FIXME: add the structures. Currently everything is either int or void. }; /** @@ -40,234 +40,234 @@ target C { // Sensing reactors reactor IMUSensor (period:time(20ms)){ - timer t(0, period) - output imuOutput: int // struct 9 values double (64bits) + timer t(0, period) + output imuOutput: int // struct 9 values double (64bits) - reaction(t) -> imuOutput {= - lf_set(imuOutput, 5); - =} + reaction(t) -> imuOutput {= + lf_set(imuOutput, 5); + =} } reactor GPSSensor (period:time(50ms)){ - timer t(0, period) - output gpsOutput : int - - reaction(t) -> gpsOutput {= - // - =} + timer t(0, period) + output gpsOutput : int + + reaction(t) -> gpsOutput {= + // + =} } // Kalman filter reactor KalmanFilter { - input imuInput: int - input gpsInput: int - output inertialData: int // Filtred sensor data everything + input imuInput: int + input gpsInput: int + output inertialData: int // Filtred sensor data everything - reaction(imuInput, gpsInput) -> inertialData {= - // lf_set(inertialData, 5); - =} + reaction(imuInput, gpsInput) -> inertialData {= + // lf_set(inertialData, 5); + =} } // The Planner will periodically call an AI agent to process the controlTarget. reactor Planner { - input targetZoneIn: int - input inertialData: int - input coverageIn : void - input weatherForecast : int - input batteryState : int - output controlTraget: int - timer t(0, 100ms) - state targetZone: int - state coverage: void - state stateValues: void // All remaining stuff - - reaction(inertialData) {= - // Update state stateValues - =} - - reaction(batteryState) {= - // Update state stateValues - =} - - reaction(weatherForecast) {= - // Update state stateValues - =} - - reaction(targetZoneIn) {= - // Update state targetZone - =} - - reaction(coverageIn) {= - // Update state coverage - =} deadline (20ms) {= - lf_print("help coverage"); - =} - - reaction(t) -> controlTraget {= - // This is the main task: compute where to go based on target zone - // and coverage - =} + input targetZoneIn: int + input inertialData: int + input coverageIn : void + input weatherForecast : int + input batteryState : int + output controlTraget: int + timer t(0, 100ms) + state targetZone: int + state coverage: void + state stateValues: void // All remaining stuff + + reaction(inertialData) {= + // Update state stateValues + =} + + reaction(batteryState) {= + // Update state stateValues + =} + + reaction(weatherForecast) {= + // Update state stateValues + =} + + reaction(targetZoneIn) {= + // Update state targetZone + =} + + reaction(coverageIn) {= + // Update state coverage + =} deadline (20ms) {= + lf_print("help coverage"); + =} + + reaction(t) -> controlTraget {= + // This is the main task: compute where to go based on target zone + // and coverage + =} } // Controller reactor Controller { - input inertialData: int - input controlTraget: int - output actuation: int - state previousTarget : int - - reaction(inertialData) -> actuation {= - =} - - reaction(controlTraget) {= - // Just updates the - previousTarget = controlTraget; - =} deadline (100ms) {= - lf_print("help"); - =} + input inertialData: int + input controlTraget: int + output actuation: int + state previousTarget : int + + reaction(inertialData) -> actuation {= + =} + + reaction(controlTraget) {= + // Just updates the + previousTarget = controlTraget; + =} deadline (100ms) {= + lf_print("help"); + =} } reactor Actuator { - input actuation : int - reaction(actuation) {= - =} + input actuation : int + reaction(actuation) {= + =} } /** * Camera actuation control */ reactor Gimbal{ - input imuData: int - logical action gimbalDone - - reaction(imuData) -> gimbalDone {= - lf_schedule(gimbalDone, 0); - =} - - reaction (gimbalDone) {==} deadline (10ms) {= - // - =} + input imuData: int + logical action gimbalDone + + reaction(imuData) -> gimbalDone {= + lf_schedule(gimbalDone, 0); + =} + + reaction (gimbalDone) {==} deadline (10ms) {= + // + =} } /** * Communication modules with the base station */ reactor ComIn { - output targetZone: int - output weatherForecast: int - timer t(0, 50ms); - reaction(t) -> targetZone {= - // polling commands form base station - // and then updating the wayPoints - =} + output targetZone: int + output weatherForecast: int + timer t(0, 50ms); + reaction(t) -> targetZone {= + // polling commands form base station + // and then updating the wayPoints + =} } reactor ComOut { - input inertialData: int - input result: int - - reaction(inertialData, result) {= - // sendiong packets - =} + input inertialData: int + input result: int + + reaction(inertialData, result) {= + // sendiong packets + =} } /** * Mission */ reactor Camera { - timer t(20msecs, 20msecs) - output frame: void + timer t(20msecs, 20msecs) + output frame: void - reaction (t) -> frame {= - frame.set(); // send a "frame" - =} + reaction (t) -> frame {= + frame.set(); // send a "frame" + =} } reactor ImageStitching { - input inertialData: int - input frame : void - output coverage: void - output stitchedImageChunk : void - state stitchedImage: void - - state latestInertialData : int - timer t(0, 1s) - - reaction(frame) -> coverage {= - // Do batch processing and only set the output - // set stitched image state - =} - - reaction(t) -> stitchedImageChunk {= - // - =} - reaction(inertialData) {= - latestInertialData = inertialData; - =} + input inertialData: int + input frame : void + output coverage: void + output stitchedImageChunk : void + state stitchedImage: void + + state latestInertialData : int + timer t(0, 1s) + + reaction(frame) -> coverage {= + // Do batch processing and only set the output + // set stitched image state + =} + + reaction(t) -> stitchedImageChunk {= + // + =} + reaction(inertialData) {= + latestInertialData = inertialData; + =} } reactor ImageProcessing { - input stitchedImageChunk: void - input batteryState: int - input weatherForecast: int - output result : int - state processedImage: void - - reaction(stitchedImageChunk) -> result {= - // This is the result from the processing - // update processedImage - =} + input stitchedImageChunk: void + input batteryState: int + input weatherForecast: int + output result : int + state processedImage: void + + reaction(stitchedImageChunk) -> result {= + // This is the result from the processing + // update processedImage + =} } /** * Battery management */ reactor BatterySensor { - output batteryState : int - timer t(0, 100ms) - reaction(t) -> batteryState {= + output batteryState : int + timer t(0, 100ms) + reaction(t) -> batteryState {= - =} + =} } /** * Main reactor */ main reactor { - // Instantiations - imuSensor = new IMUSensor() - gpsSensor = new GPSSensor() - kalmanFilter = new KalmanFilter() - planner = new Planner() - controller = new Controller() - actuator = new Actuator() - gimbal = new Gimbal() - comIn = new ComIn() - comOut = new ComOut() - camera = new Camera() - imageStitching = new ImageStitching() - imageProcessing = new ImageProcessing() - batterySensor = new BatterySensor() - - // Connections - imuSensor.imuOutput -> kalmanFilter.imuInput - gpsSensor.gpsOutput -> kalmanFilter.gpsInput - kalmanFilter.inertialData -> planner.inertialData - kalmanFilter.inertialData -> controller.inertialData - planner.controlTraget -> controller.controlTraget - controller.actuation -> actuator.actuation - imuSensor.imuOutput -> gimbal.imuData - kalmanFilter.inertialData -> comOut.inertialData - comIn.targetZone -> planner.targetZoneIn - comIn.weatherForecast -> planner.weatherForecast - camera.frame -> imageStitching.frame - kalmanFilter.inertialData -> imageStitching.inertialData - imageStitching.coverage -> planner.coverageIn - imageStitching.stitchedImageChunk -> imageProcessing.stitchedImageChunk - imageProcessing.result -> comOut.result - batterySensor.batteryState -> planner.batteryState - batterySensor.batteryState -> imageProcessing.batteryState - comIn.weatherForecast -> imageProcessing.weatherForecast + // Instantiations + imuSensor = new IMUSensor() + gpsSensor = new GPSSensor() + kalmanFilter = new KalmanFilter() + planner = new Planner() + controller = new Controller() + actuator = new Actuator() + gimbal = new Gimbal() + comIn = new ComIn() + comOut = new ComOut() + camera = new Camera() + imageStitching = new ImageStitching() + imageProcessing = new ImageProcessing() + batterySensor = new BatterySensor() + + // Connections + imuSensor.imuOutput -> kalmanFilter.imuInput + gpsSensor.gpsOutput -> kalmanFilter.gpsInput + kalmanFilter.inertialData -> planner.inertialData + kalmanFilter.inertialData -> controller.inertialData + planner.controlTraget -> controller.controlTraget + controller.actuation -> actuator.actuation + imuSensor.imuOutput -> gimbal.imuData + kalmanFilter.inertialData -> comOut.inertialData + comIn.targetZone -> planner.targetZoneIn + comIn.weatherForecast -> planner.weatherForecast + camera.frame -> imageStitching.frame + kalmanFilter.inertialData -> imageStitching.inertialData + imageStitching.coverage -> planner.coverageIn + imageStitching.stitchedImageChunk -> imageProcessing.stitchedImageChunk + imageProcessing.result -> comOut.result + batterySensor.batteryState -> planner.batteryState + batterySensor.batteryState -> imageProcessing.batteryState + comIn.weatherForecast -> imageProcessing.weatherForecast } diff --git a/experimental/C/src/SpatAnalysis/MQTTPublisher.lf b/experimental/C/src/SpatAnalysis/MQTTPublisher.lf index cc2df191..f42a1b7e 100644 --- a/experimental/C/src/SpatAnalysis/MQTTPublisher.lf +++ b/experimental/C/src/SpatAnalysis/MQTTPublisher.lf @@ -34,150 +34,150 @@ target C; * @see MQTTSubscriber. */ reactor MQTTPublisher ( - topic:string("DefaultTopic"), - address:string("tcp://localhost:1883"), - clientID:string("DefaultPublisher"), - include_physical_timestamp:int(0) + topic:string("DefaultTopic"), + address:string("tcp://localhost:1883"), + clientID:string("DefaultPublisher"), + include_physical_timestamp:int(0) ) { - preamble {= - #include "MQTTClient.h" - #include "core/util.h" - - // Timeout for completion of message sending in milliseconds. - #define TIMEOUT 10000L - - // Connection options for the client. - // Making this global means that all instances of this reactor have - // the same connection options. - MQTTClient_connectOptions pub_connect_options = MQTTClient_connectOptions_initializer; + preamble {= + #include "MQTTClient.h" + #include "core/util.h" + + // Timeout for completion of message sending in milliseconds. + #define TIMEOUT 10000L + + // Connection options for the client. + // Making this global means that all instances of this reactor have + // the same connection options. + MQTTClient_connectOptions pub_connect_options = MQTTClient_connectOptions_initializer; - // Struct type used to keep track of messages in flight between reactions. - typedef struct inflight_t { - bool message_in_flight; - MQTTClient_deliveryToken delivery_token; - char* message; - } inflight_t; - - // Callback invoked once delivery is complete. - void pub_delivered(void *inflight, MQTTClient_deliveryToken dt) { - // printf("DEBUG: Message with token value %d delivery confirmed\n", dt); - ((inflight_t*)inflight)->message_in_flight = false; - free(((inflight_t*)inflight)->message); - } - // Callback invoked if the connection is lost. - void pub_connection_lost(void *context, char *cause) { - printf("\nMQTTPublisher: Connection lost\n"); - printf(" cause: %s\n", cause); - } - =} - /** - * Input type char* instead of string is used for dynamically - * allocated character arrays (as opposed to static constant strings). - */ - input in:char*; + // Struct type used to keep track of messages in flight between reactions. + typedef struct inflight_t { + bool message_in_flight; + MQTTClient_deliveryToken delivery_token; + char* message; + } inflight_t; - /** State variable that keeps track of a message in flight. */ - state inflight:inflight_t({={false, 0, NULL}=}); + // Callback invoked once delivery is complete. + void pub_delivered(void *inflight, MQTTClient_deliveryToken dt) { + // printf("DEBUG: Message with token value %d delivery confirmed\n", dt); + ((inflight_t*)inflight)->message_in_flight = false; + free(((inflight_t*)inflight)->message); + } + // Callback invoked if the connection is lost. + void pub_connection_lost(void *context, char *cause) { + printf("\nMQTTPublisher: Connection lost\n"); + printf(" cause: %s\n", cause); + } + =} + /** + * Input type char* instead of string is used for dynamically + * allocated character arrays (as opposed to static constant strings). + */ + input in:char*; + + /** State variable that keeps track of a message in flight. */ + state inflight:inflight_t({={false, 0, NULL}=}); + + /** The client object. */ + state client:MQTTClient({=NULL=}); + + /** The message object. */ + state mqtt_msg:MQTTClient_message({=MQTTClient_message_initializer=}); + + /** Connect to the broker. Exit if this fails. */ + reaction(startup){= + MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); + pub_connect_options.keepAliveInterval = 20; + pub_connect_options.cleansession = 1; + + // Set up callback functions. + // Second to last argument should be a pointer to a function + // to handle notification of delivery of a message. + // But this reactor isn't sending any messages. + // Second argument is a pointer to context that will be passed to pub_delivered, + // which in this case is a pointer to the inflight state variable. + MQTTClient_setCallbacks(self->client, &self->inflight, pub_connection_lost, NULL, pub_delivered); - /** The client object. */ - state client:MQTTClient({=NULL=}); + // Connect to the broker. + int rc; // response code. + if ((rc = MQTTClient_connect(self->client, &pub_connect_options)) != MQTTCLIENT_SUCCESS) { + fprintf(stderr, "MQTTPublisher: Failed to connect to MQTT broker.\n"); + fprintf(stderr, "Perhaps one is not running? Return code: %d\n", rc); + exit(EXIT_FAILURE); + } + // printf("DEBUG: MQTTPublisher connected to broker.\n"); + =} + + /** + * React to an input by sending a message with the value of the input as the payload. + * If delivery has not yet completed for a previously sent message, then wait for + * it to complete before proceeding (blocking this reaction). + * This copies the message from the input into a buffer, so the input can + * freed upon return of this reaction (LF will automatically decrement its + * reference count). + */ + reaction(in) {= + if(self->inflight.message_in_flight) { + // Wait for message delivery to be complete. + // printf("DEBUG: Waiting for publication of previous message\n"); + int rc = MQTTClient_waitForCompletion(self->client, self->inflight.delivery_token, TIMEOUT); + if (rc != MQTTCLIENT_SUCCESS) { + fprintf(stderr, "ERROR: Message delivery failed with error code %d.\n", rc); + fprintf(stderr, "Message: %s\n", in->value); + fprintf(stderr, "On topic '%s' for publisher with ClientID: %s\n", self->topic, self->clientID); + } + } + //printf("DEBUG: Publishing message: %s\n", in->value); + // printf("DEBUG: on topic '%s' for publisher with ClientID: %s\n", self->topic, self->clientID); - /** The message object. */ - state mqtt_msg:MQTTClient_message({=MQTTClient_message_initializer=}); + // Allocate memory for a copy of the message. + // The length includes the null-terminator of the string and 8 bytes for the timestamp. + int length = strlen(in->value) + 1 + sizeof(instant_t); + if (self->include_physical_timestamp) { + length += sizeof(instant_t); + } + self->inflight.message = malloc(sizeof(char) * length); + memcpy(self->inflight.message, in->value, strlen(in->value) + 1); - /** Connect to the broker. Exit if this fails. */ - reaction(startup){= - MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); - pub_connect_options.keepAliveInterval = 20; - pub_connect_options.cleansession = 1; + // Append the current timestamp to the message. + // This is always last, after the physical timestamp if it is included. + encode_ll(lf_time_logical(), + (unsigned char*)(self->inflight.message + length - sizeof(instant_t)) + ); + // printf("DEBUG: Timestamp of sending message: %lld.\n", *timestamp); - // Set up callback functions. - // Second to last argument should be a pointer to a function - // to handle notification of delivery of a message. - // But this reactor isn't sending any messages. - // Second argument is a pointer to context that will be passed to pub_delivered, - // which in this case is a pointer to the inflight state variable. - MQTTClient_setCallbacks(self->client, &self->inflight, pub_connection_lost, NULL, pub_delivered); - - // Connect to the broker. - int rc; // response code. - if ((rc = MQTTClient_connect(self->client, &pub_connect_options)) != MQTTCLIENT_SUCCESS) { - fprintf(stderr, "MQTTPublisher: Failed to connect to MQTT broker.\n"); - fprintf(stderr, "Perhaps one is not running? Return code: %d\n", rc); - exit(EXIT_FAILURE); - } - // printf("DEBUG: MQTTPublisher connected to broker.\n"); - =} + self->mqtt_msg.payload = self->inflight.message; + self->mqtt_msg.payloadlen = length; - /** - * React to an input by sending a message with the value of the input as the payload. - * If delivery has not yet completed for a previously sent message, then wait for - * it to complete before proceeding (blocking this reaction). - * This copies the message from the input into a buffer, so the input can - * freed upon return of this reaction (LF will automatically decrement its - * reference count). - */ - reaction(in) {= - if(self->inflight.message_in_flight) { - // Wait for message delivery to be complete. - // printf("DEBUG: Waiting for publication of previous message\n"); - int rc = MQTTClient_waitForCompletion(self->client, self->inflight.delivery_token, TIMEOUT); - if (rc != MQTTCLIENT_SUCCESS) { - fprintf(stderr, "ERROR: Message delivery failed with error code %d.\n", rc); - fprintf(stderr, "Message: %s\n", in->value); - fprintf(stderr, "On topic '%s' for publisher with ClientID: %s\n", self->topic, self->clientID); - } - } - //printf("DEBUG: Publishing message: %s\n", in->value); - // printf("DEBUG: on topic '%s' for publisher with ClientID: %s\n", self->topic, self->clientID); - - // Allocate memory for a copy of the message. - // The length includes the null-terminator of the string and 8 bytes for the timestamp. - int length = strlen(in->value) + 1 + sizeof(instant_t); - if (self->include_physical_timestamp) { - length += sizeof(instant_t); - } - self->inflight.message = malloc(sizeof(char) * length); - memcpy(self->inflight.message, in->value, strlen(in->value) + 1); - - // Append the current timestamp to the message. - // This is always last, after the physical timestamp if it is included. - encode_ll(lf_time_logical(), - (unsigned char*)(self->inflight.message + length - sizeof(instant_t)) - ); - // printf("DEBUG: Timestamp of sending message: %lld.\n", *timestamp); - - self->mqtt_msg.payload = self->inflight.message; - self->mqtt_msg.payloadlen = length; - - // QoS 2 means that the message will be delivered exactly once. - self->mqtt_msg.qos = 2; - - // Retained messages are held by the server and sent to future new subscribers. - // Specify that this message should not be retained. - // It will be sent only to subscribers currently subscribed. - self->mqtt_msg.retained = 0; - - // As close as possible to the publishing of the message, insert - // the physical timestamp if it has been requested. - if (self->include_physical_timestamp) { - encode_ll(lf_time_physical(), - (unsigned char*)(self->inflight.message + length - 2 * sizeof(instant_t)) - ); - } - //For Dashboard, echo physical time stamp - else { - printf("EVENT: sender_pts: %lld\n", lf_time_physical()); - } - - MQTTClient_publishMessage(self->client, self->topic, &self->mqtt_msg, &self->inflight.delivery_token); - self->inflight.message_in_flight = true; - =} + // QoS 2 means that the message will be delivered exactly once. + self->mqtt_msg.qos = 2; + + // Retained messages are held by the server and sent to future new subscribers. + // Specify that this message should not be retained. + // It will be sent only to subscribers currently subscribed. + self->mqtt_msg.retained = 0; + + // As close as possible to the publishing of the message, insert + // the physical timestamp if it has been requested. + if (self->include_physical_timestamp) { + encode_ll(lf_time_physical(), + (unsigned char*)(self->inflight.message + length - 2 * sizeof(instant_t)) + ); + } + //For Dashboard, echo physical time stamp + else { + printf("EVENT: sender_pts: %lld\n", lf_time_physical()); + } - /** Disconnect the client. */ - reaction(shutdown) {= - printf("MQTTPublisher: Client ID %s disconnecting.\n", self->clientID); - MQTTClient_disconnect(self->client, 10000); - MQTTClient_destroy(&self->client); - =} + MQTTClient_publishMessage(self->client, self->topic, &self->mqtt_msg, &self->inflight.delivery_token); + self->inflight.message_in_flight = true; + =} + + /** Disconnect the client. */ + reaction(shutdown) {= + printf("MQTTPublisher: Client ID %s disconnecting.\n", self->clientID); + MQTTClient_disconnect(self->client, 10000); + MQTTClient_destroy(&self->client); + =} } diff --git a/experimental/C/src/SpatAnalysis/MQTTSubscriber.lf b/experimental/C/src/SpatAnalysis/MQTTSubscriber.lf index 37bf2224..9de8435f 100644 --- a/experimental/C/src/SpatAnalysis/MQTTSubscriber.lf +++ b/experimental/C/src/SpatAnalysis/MQTTSubscriber.lf @@ -57,210 +57,210 @@ target C; */ reactor MQTTSubscriber ( - address:string("tcp://localhost:1883"), - clientID:string("DefaultSubscriber"), - topic:string("DefaultTopic"), - offset:time(0 msec) + address:string("tcp://localhost:1883"), + clientID:string("DefaultSubscriber"), + topic:string("DefaultTopic"), + offset:time(0 msec) ) { - preamble {= - #include "MQTTClient.h" - #include "core/util.h" - #include - - #define QOS 2 - #define TIMEOUT 10000L + preamble {= + #include "MQTTClient.h" + #include "core/util.h" + #include + + #define QOS 2 + #define TIMEOUT 10000L + + // Connection options for the client. + // Making this global means that all instances of this reactor have + // the same connection options. + MQTTClient_connectOptions sub_connect_options = MQTTClient_connectOptions_initializer; - // Connection options for the client. - // Making this global means that all instances of this reactor have - // the same connection options. - MQTTClient_connectOptions sub_connect_options = MQTTClient_connectOptions_initializer; - - // Callback function invoked by MQTT when a message arrives. - int message_arrived( - void *incoming_message, - char *topicName, - int topicLen, - MQTTClient_message *message - ) { - instant_t receive_physical_time = lf_time_physical(); - // If a physical timestamp was sent, report the transport time. - size_t string_length = strlen((char*)message->payload); // Assumes null-terminated string. - if (message->payloadlen == string_length + 1 + 2*sizeof(instant_t)) { - instant_t* physical_timestamp = (instant_t*)((char*)message->payload + string_length + 1); - // printf("DEBUG: MQTTReceiver.message_arrived: Received message after measured latency of %lld nsec (assuming synchronized clocks).\n", receive_physical_time - *physical_timestamp); - } - - // printf("DEBUG: MQTTSubscriber: Message arrived on topic %s: %s\n", topicName, (char*)message->payload); - - // Extract the timestamp and calculate delay from current_time to that timestamp. - // Note that if this subscriber's current time is ahead of current time - // at the publisher (something that should not happen even in a distributed - // implementation), then this extra delay may be negative. If it becomes - // less than -offset, then the lf_schedule_copy() function will complain - // by printing a warning to stderr and revise the timestamp to current time. - // First acquire the mutex lock. Otherwise, logical time could have a big - // jump between this calculation and the call to lf_schedule, resulting in a long delay. - - // NOTE: Since lf_schedule_copy also acquires this lock, we assume here that the - // pthreads library correctly implements recursive mutex locks to unlock all - // locks held by the current thread when waiting for signals. - pthread_mutex_lock(&mutex); + // Callback function invoked by MQTT when a message arrives. + int message_arrived( + void *incoming_message, + char *topicName, + int topicLen, + MQTTClient_message *message + ) { + instant_t receive_physical_time = lf_time_physical(); + // If a physical timestamp was sent, report the transport time. + size_t string_length = strlen((char*)message->payload); // Assumes null-terminated string. + if (message->payloadlen == string_length + 1 + 2*sizeof(instant_t)) { + instant_t* physical_timestamp = (instant_t*)((char*)message->payload + string_length + 1); + // printf("DEBUG: MQTTReceiver.message_arrived: Received message after measured latency of %lld nsec (assuming synchronized clocks).\n", receive_physical_time - *physical_timestamp); + } + + // printf("DEBUG: MQTTSubscriber: Message arrived on topic %s: %s\n", topicName, (char*)message->payload); - instant_t timestamp = extract_ll((unsigned char*)message->payload + message->payloadlen - sizeof(instant_t)); - interval_t delay = timestamp - lf_time_logical(); - - // Schedule the event. Since incoming_message is a physical action, - // the offset specified in the second argument will be added to current_time - // and to the min_delay in the action and then compared to physical time. - // If the sum is greater than physical time - // (i.e. if the offset + min_delay is large enough), then the event will be scheduled at - // exactly the logical time at the publisher plus the offset. - // Otherwise, it will be scheduled at the current physical time. - // The incoming message is in dynamically allocated memory. - // We copy the message using lf_schedule_copy() because, unfortunately, Paho MQTT uses its own - // version of malloc() and free() (defined in Heap.h and Heap.c). - // We could modify Paho MQTT to use the generic malloc() and free(), - // and then we could use lf_schedule_value() to avoid the copy. - // Note that the last 8 bytes of the message are the sender's timestamp. - // We include that in the copy so that the reaction to the physical action - // can measure the latency. - lf_schedule_copy(incoming_message, delay, (char*)message->payload, message->payloadlen); + // Extract the timestamp and calculate delay from current_time to that timestamp. + // Note that if this subscriber's current time is ahead of current time + // at the publisher (something that should not happen even in a distributed + // implementation), then this extra delay may be negative. If it becomes + // less than -offset, then the lf_schedule_copy() function will complain + // by printing a warning to stderr and revise the timestamp to current time. + // First acquire the mutex lock. Otherwise, logical time could have a big + // jump between this calculation and the call to lf_schedule, resulting in a long delay. + + // NOTE: Since lf_schedule_copy also acquires this lock, we assume here that the + // pthreads library correctly implements recursive mutex locks to unlock all + // locks held by the current thread when waiting for signals. + pthread_mutex_lock(&mutex); + + instant_t timestamp = extract_ll((unsigned char*)message->payload + message->payloadlen - sizeof(instant_t)); + interval_t delay = timestamp - lf_time_logical(); - pthread_mutex_unlock(&mutex); + // Schedule the event. Since incoming_message is a physical action, + // the offset specified in the second argument will be added to current_time + // and to the min_delay in the action and then compared to physical time. + // If the sum is greater than physical time + // (i.e. if the offset + min_delay is large enough), then the event will be scheduled at + // exactly the logical time at the publisher plus the offset. + // Otherwise, it will be scheduled at the current physical time. + // The incoming message is in dynamically allocated memory. + // We copy the message using lf_schedule_copy() because, unfortunately, Paho MQTT uses its own + // version of malloc() and free() (defined in Heap.h and Heap.c). + // We could modify Paho MQTT to use the generic malloc() and free(), + // and then we could use lf_schedule_value() to avoid the copy. + // Note that the last 8 bytes of the message are the sender's timestamp. + // We include that in the copy so that the reaction to the physical action + // can measure the latency. + lf_schedule_copy(incoming_message, delay, (char*)message->payload, message->payloadlen); + + pthread_mutex_unlock(&mutex); - // MQTTClient_freeMessage() also frees the memory allocated to the payload, - // which is why we have to copy the message here. - MQTTClient_freeMessage(&message); - MQTTClient_free(topicName); - - // Return true to indicate that the message has been successfully handled. - return 1; - } - - /** Callback invoked if the connection is lost. */ - void sub_connection_lost(void *incoming_message, char *cause) { - printf("\nConnection lost\n"); - printf(" cause: %s\n", cause); - } - =} + // MQTTClient_freeMessage() also frees the memory allocated to the payload, + // which is why we have to copy the message here. + MQTTClient_freeMessage(&message); + MQTTClient_free(topicName); + + // Return true to indicate that the message has been successfully handled. + return 1; + } - /** - * Output for sending the incoming MQTT message. - * Use type char* rather than string because it is not - * a static string, but rather dynamically allocated memory. - */ - output message:char*; + /** Callback invoked if the connection is lost. */ + void sub_connection_lost(void *incoming_message, char *cause) { + printf("\nConnection lost\n"); + printf(" cause: %s\n", cause); + } + =} + + /** + * Output for sending the incoming MQTT message. + * Use type char* rather than string because it is not + * a static string, but rather dynamically allocated memory. + */ + output message:char*; - /** - * Action that is triggered when there is an incoming MQTT message. - * Use a physical action here so that the logical time when the action - * is triggered is the physical time of arrival of the message if - * the offset is 0. If the offset is larger than the communication - * latency plus the clock synchronization error, - * then the incoming message will have have a timestamp deterministically - * larger than the sender's timestamp by the offset. - */ - physical action incoming_message(offset):char*; + /** + * Action that is triggered when there is an incoming MQTT message. + * Use a physical action here so that the logical time when the action + * is triggered is the physical time of arrival of the message if + * the offset is 0. If the offset is larger than the communication + * latency plus the clock synchronization error, + * then the incoming message will have have a timestamp deterministically + * larger than the sender's timestamp by the offset. + */ + physical action incoming_message(offset):char*; + + /** + * State variable storing the MQTT client created for each instance of this reactor. + */ + state client:MQTTClient({=NULL=}); + + /** + * Maximum observed latency from the originator of the message to here. + */ + state max_latency:time(0); + + /** + * Sum of all observed latencies. + */ + state latencies:time(0); + + /** + * Count of messages. + */ + state count:int(0); + + reaction(startup) -> incoming_message {= + MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); + sub_connect_options.keepAliveInterval = 20; + sub_connect_options.cleansession = 1; - /** - * State variable storing the MQTT client created for each instance of this reactor. - */ - state client:MQTTClient({=NULL=}); + // Set up callback functions. + // Last argument should be a pointer to a function to + // handle notification of delivery of a sent message. + // But this reactor isn't sending any messages. + MQTTClient_setCallbacks(self->client, incoming_message, sub_connection_lost, message_arrived, NULL); - /** - * Maximum observed latency from the originator of the message to here. - */ - state max_latency:time(0); + // Connect to the broker. + int rc; // response code. + if ((rc = MQTTClient_connect(self->client, &sub_connect_options)) != MQTTCLIENT_SUCCESS) { + fprintf(stderr, "MQTTSubscriber: Failed to connect to MQTT broker.\n"); + fprintf(stderr, "Perhaps one is not running? Return code: %d\n", rc); + exit(EXIT_FAILURE); + } - /** - * Sum of all observed latencies. - */ - state latencies:time(0); + MQTTClient_subscribe(self->client, self->topic, QOS); + =} + + reaction(incoming_message) -> message {= + self->count++; - /** - * Count of messages. - */ - state count:int(0); - - reaction(startup) -> incoming_message {= - MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); - sub_connect_options.keepAliveInterval = 20; - sub_connect_options.cleansession = 1; - - // Set up callback functions. - // Last argument should be a pointer to a function to - // handle notification of delivery of a sent message. - // But this reactor isn't sending any messages. - MQTTClient_setCallbacks(self->client, incoming_message, sub_connection_lost, message_arrived, NULL); + // The incoming_message action contains a token that we can just forward. + // The allocated memory will be freed when the token's reference count hits 0. + // Note that this token will still contain the sender's timestamp. + lf_set_token(message, incoming_message->token); - // Connect to the broker. - int rc; // response code. - if ((rc = MQTTClient_connect(self->client, &sub_connect_options)) != MQTTCLIENT_SUCCESS) { - fprintf(stderr, "MQTTSubscriber: Failed to connect to MQTT broker.\n"); - fprintf(stderr, "Perhaps one is not running? Return code: %d\n", rc); - exit(EXIT_FAILURE); - } - - MQTTClient_subscribe(self->client, self->topic, QOS); - =} + // Get the sender's timestamp. + instant_t* timestamp = (instant_t*)( + incoming_message->token->value + incoming_message->token->length - sizeof(instant_t) + ); + //printf("DEBUG: Received message carrying timestamp %lld.\n", self->count, *timestamp); - reaction(incoming_message) -> message {= - self->count++; - - // The incoming_message action contains a token that we can just forward. - // The allocated memory will be freed when the token's reference count hits 0. - // Note that this token will still contain the sender's timestamp. - lf_set_token(message, incoming_message->token); - - // Get the sender's timestamp. - instant_t* timestamp = (instant_t*)( - incoming_message->token->value + incoming_message->token->length - sizeof(instant_t) - ); - //printf("DEBUG: Received message carrying timestamp %lld.\n", self->count, *timestamp); - - // If the offset is 0, then the latency will be a measure of - // the physical latency between creation of the message at the sender - // and its receipt here, offset by the clock synchronization error, - // assuming that the sender sent the message at a physical time matching its - // logical timestamp. - instant_t logical_timestamp = lf_time_logical(); - interval_t latency = logical_timestamp - *timestamp; - // printf("DEBUG: MQTTSubscriber.reaction: Received timestamp is larger than sent timestamp by: %lld.\n", latency); - printf("EVENT: message_id: %d\n", self->count); - printf("EVENT: sender_lts: %lld\n", *timestamp); - printf("EVENT: receiver_lts: %lld\n", logical_timestamp); - printf("EVENT: logical_latency: %lld\n", latency); - - // If a physical timestamp was sent, use that to collect - // latency stats instead of the logical time increment. - size_t string_length = strlen(incoming_message->value); // Assumes null-terminated string. - if (incoming_message->token->length == string_length + 1 + 2*sizeof(instant_t)) { - instant_t receive_physical_time = lf_time_physical(); - instant_t physical_timestamp = extract_ll((unsigned char*)(incoming_message->value + string_length + 1)); - latency = receive_physical_time - physical_timestamp; - printf("EVENT: sender_pts: %lld\n", physical_timestamp); - printf("EVENT: receiver_pts: %lld\n", receive_physical_time); - printf("EVENT: physical_latency: %lld\n", latency); - //printf("EVENT: MQTTReceiver.reaction: Reacted to message after measured latency of %lld nsec (assuming synchronized clocks).\n", latency); - } - else{ //physical timestamp was not sent, echo physical timestamp for dashboard - printf("EVENT: receiver_pts: %lld\n", lf_time_physical()); - } - - self->latencies += latency; - - if (latency > self->max_latency) { - self->max_latency = latency; - } - =} + // If the offset is 0, then the latency will be a measure of + // the physical latency between creation of the message at the sender + // and its receipt here, offset by the clock synchronization error, + // assuming that the sender sent the message at a physical time matching its + // logical timestamp. + instant_t logical_timestamp = lf_time_logical(); + interval_t latency = logical_timestamp - *timestamp; + // printf("DEBUG: MQTTSubscriber.reaction: Received timestamp is larger than sent timestamp by: %lld.\n", latency); + printf("EVENT: message_id: %d\n", self->count); + printf("EVENT: sender_lts: %lld\n", *timestamp); + printf("EVENT: receiver_lts: %lld\n", logical_timestamp); + printf("EVENT: logical_latency: %lld\n", latency); + + // If a physical timestamp was sent, use that to collect + // latency stats instead of the logical time increment. + size_t string_length = strlen(incoming_message->value); // Assumes null-terminated string. + if (incoming_message->token->length == string_length + 1 + 2*sizeof(instant_t)) { + instant_t receive_physical_time = lf_time_physical(); + instant_t physical_timestamp = extract_ll((unsigned char*)(incoming_message->value + string_length + 1)); + latency = receive_physical_time - physical_timestamp; + printf("EVENT: sender_pts: %lld\n", physical_timestamp); + printf("EVENT: receiver_pts: %lld\n", receive_physical_time); + printf("EVENT: physical_latency: %lld\n", latency); + //printf("EVENT: MQTTReceiver.reaction: Reacted to message after measured latency of %lld nsec (assuming synchronized clocks).\n", latency); + } + else{ //physical timestamp was not sent, echo physical timestamp for dashboard + printf("EVENT: receiver_pts: %lld\n", lf_time_physical()); + } + + self->latencies += latency; - reaction(shutdown) {= - printf("MQTTSubscriber: Maximum latency measured at receiver (in nsec): %lld.\n", self->max_latency); - if (self->count > 0) { - printf("MQTTSubscriber: Average latency measured at receiver (in nsec): %lld.\n", self->latencies/self->count); - } - printf("MQTTSubscriber: Client ID %s disconnecting.\n", self->clientID); - MQTTClient_disconnect(self->client, 10000); - MQTTClient_destroy(&self->client); - =} + if (latency > self->max_latency) { + self->max_latency = latency; + } + =} + + reaction(shutdown) {= + printf("MQTTSubscriber: Maximum latency measured at receiver (in nsec): %lld.\n", self->max_latency); + if (self->count > 0) { + printf("MQTTSubscriber: Average latency measured at receiver (in nsec): %lld.\n", self->latencies/self->count); + } + printf("MQTTSubscriber: Client ID %s disconnecting.\n", self->clientID); + MQTTClient_disconnect(self->client, 10000); + MQTTClient_destroy(&self->client); + =} } diff --git a/experimental/C/src/SpatAnalysis/influxWrite.lf b/experimental/C/src/SpatAnalysis/influxWrite.lf index b679f3ba..f345845d 100644 --- a/experimental/C/src/SpatAnalysis/influxWrite.lf +++ b/experimental/C/src/SpatAnalysis/influxWrite.lf @@ -17,125 +17,125 @@ target TypeScript; main reactor { - preamble{= - // @ts-ignore - import * as Influx from "influx"; - import * as os from "os"; + preamble{= + // @ts-ignore + import * as Influx from "influx"; + import * as os from "os"; - import * as fs from "fs"; - import * as path from "path"; - - const databaseName = "LF_EVENTS"; - const measurementName = "event_times"; - const traceFilePath = "../../my_trace"; - - - const influx = new Influx.InfluxDB({ - host: "localhost", - database: databaseName, - schema: [ - { - measurement: measurementName, - fields: { - message_id: Influx.FieldType.INTEGER, - sender_lts: Influx.FieldType.FLOAT, - receiver_lts: Influx.FieldType.FLOAT, - logical_latency: Influx.FieldType.INTEGER, - sender_pts: Influx.FieldType.FLOAT, - receiver_pts: Influx.FieldType.FLOAT, - physical_latency: Influx.FieldType.INTEGER, - residual: Influx.FieldType.INTEGER, - phase: Influx.FieldType.INTEGER, - deadline_miss: Influx.FieldType.INTEGER, - }, - tags: [] - }, - ], - }); - =} + import * as fs from "fs"; + import * as path from "path"; - reaction(startup){= - influx - .getDatabaseNames() - // @ts-ignore - .then((names) => { - if (!names.includes(databaseName)) { - return influx.createDatabase(databaseName); - } - }) - .then(() => { - console.log("Created new database if not exists"); - - }) - // @ts-ignore - .catch((err) => { - console.error(`Error creating Influx database!`); - }); - =} - - timer t(0, 0 msec); + const databaseName = "LF_EVENTS"; + const measurementName = "event_times"; + const traceFilePath = "../../my_trace"; - reaction(t) {= - - - let fieldRecord: {[key:string] : number} = {}; - - var lines; - try { - - const data = fs.readFileSync(path.resolve(__dirname, traceFilePath), "utf8") - lines = data.split(/\r?\n/); - - - lines.forEach((line) => { - if(line.indexOf("EVENT") < 0){ - return; - } - var mod_line = line.replace("EVENT: ", ""); - var split_line = mod_line.split(": "); - //console.log(split_line); - var _timestamp = ""; - if(split_line[0] == "sender_pts") - _timestamp = split_line[1]; - fieldRecord[split_line[0]] = parseInt(split_line[1]); - if(split_line[0] == "deadline_miss"){ - influx - .writePoints([ - { - measurement: measurementName, - tags: {}, - fields: fieldRecord, - timestamp: _timestamp, - }, - ]) - // @ts-ignore - .catch((err) => { - console.error(`Error saving data to InfluxDB! ${err.stack}`); - }); - } - }); - } catch (err) { - console.error(err) - } - =} + + const influx = new Influx.InfluxDB({ + host: "localhost", + database: databaseName, + schema: [ + { + measurement: measurementName, + fields: { + message_id: Influx.FieldType.INTEGER, + sender_lts: Influx.FieldType.FLOAT, + receiver_lts: Influx.FieldType.FLOAT, + logical_latency: Influx.FieldType.INTEGER, + sender_pts: Influx.FieldType.FLOAT, + receiver_pts: Influx.FieldType.FLOAT, + physical_latency: Influx.FieldType.INTEGER, + residual: Influx.FieldType.INTEGER, + phase: Influx.FieldType.INTEGER, + deadline_miss: Influx.FieldType.INTEGER, + }, + tags: [] + }, + ], + }); + =} + + reaction(startup){= + influx + .getDatabaseNames() + // @ts-ignore + .then((names) => { + if (!names.includes(databaseName)) { + return influx.createDatabase(databaseName); + } + }) + .then(() => { + console.log("Created new database if not exists"); + + }) + // @ts-ignore + .catch((err) => { + console.error(`Error creating Influx database!`); + }); + =} + + timer t(0, 0 msec); + + reaction(t) {= + - reaction(shutdown){= - influx - .query( - ` - select * from measurementName - where host = ${Influx.escape.stringLit(os.hostname())} - order by time desc - limit 10 - ` - ) - // @ts-ignore - .then((result) => { - console.log(result); - }) - // @ts-ignore - .catch((err) => { - console.log(err.stack); - }); - =} + let fieldRecord: {[key:string] : number} = {}; + + var lines; + try { + + const data = fs.readFileSync(path.resolve(__dirname, traceFilePath), "utf8") + lines = data.split(/\r?\n/); + + + lines.forEach((line) => { + if(line.indexOf("EVENT") < 0){ + return; + } + var mod_line = line.replace("EVENT: ", ""); + var split_line = mod_line.split(": "); + //console.log(split_line); + var _timestamp = ""; + if(split_line[0] == "sender_pts") + _timestamp = split_line[1]; + fieldRecord[split_line[0]] = parseInt(split_line[1]); + if(split_line[0] == "deadline_miss"){ + influx + .writePoints([ + { + measurement: measurementName, + tags: {}, + fields: fieldRecord, + timestamp: _timestamp, + }, + ]) + // @ts-ignore + .catch((err) => { + console.error(`Error saving data to InfluxDB! ${err.stack}`); + }); + } + }); + } catch (err) { + console.error(err) + } + =} + + reaction(shutdown){= + influx + .query( + ` + select * from measurementName + where host = ${Influx.escape.stringLit(os.hostname())} + order by time desc + limit 10 + ` + ) + // @ts-ignore + .then((result) => { + console.log(result); + }) + // @ts-ignore + .catch((err) => { + console.log(err.stack); + }); + =} } diff --git a/experimental/C/src/SpatAnalysis/spatReceiver.lf b/experimental/C/src/SpatAnalysis/spatReceiver.lf index 601d315f..1b2415fd 100644 --- a/experimental/C/src/SpatAnalysis/spatReceiver.lf +++ b/experimental/C/src/SpatAnalysis/spatReceiver.lf @@ -2,16 +2,16 @@ * Example PTIDES application for connected vehicle application * Demo Tile: RSU Coordinated Speed alignment * This is the vehicle side application that - * - Requests RSU side application to provide speed recommendations based on its SPAT + * - Requests RSU side application to provide speed recommendations based on its SPAT * - Uses MQTT pub/sub * @author Ravi Akella */ target C { - threads: 1, // Must use threaded implementation so lf_schedule is thread safe. - flags: "-I/usr/local/include -L/usr/local/lib -g -lpaho-mqtt3c src-gen/core/util.c ", - timeout: 60 secs, - keepalive: true + threads: 1, // Must use threaded implementation so lf_schedule is thread safe. + flags: "-I/usr/local/include -L/usr/local/lib -g -lpaho-mqtt3c src-gen/core/util.c ", + timeout: 60 secs, + keepalive: true }; @@ -24,41 +24,41 @@ import MQTTSubscriber from "MQTTSubscriber.lf"; * @input message The message. */ reactor recvSpatMessage { - preamble {= - #define BILLION 1000000000LL - typedef struct spat_status{ - int signal_phase; //integer code for signal 1-Red, 2-Green, 3-Yellow - long long unsigned residual; // remaining time for phase change - } spat_status; - =} - - input message:spat_status*; - reaction(message) {= - printf("Received Phase=\"%d\", residual=%llu.\n", - message->value->signal_phase, message->value->residual - ); - =} + preamble {= + #define BILLION 1000000000LL + typedef struct spat_status{ + int signal_phase; //integer code for signal 1-Red, 2-Green, 3-Yellow + long long unsigned residual; // remaining time for phase change + } spat_status; + =} + + input message:spat_status*; + reaction(message) {= + printf("Received Phase=\"%d\", residual=%llu.\n", + message->value->signal_phase, message->value->residual + ); + =} } reactor recvSpatMessage2 { - input message:char*; - state signal_phase:int(0); - state residual:time(0 nsec); + input message:char*; + state signal_phase:int(0); + state residual:time(0 nsec); + + reaction(message) {= + /*printf("PrintMessage: At (elapsed) time %lld, subscriber receives: %s\n", + lf_time_logical_elapsed(), + message->value + );*/ - reaction(message) {= - /*printf("PrintMessage: At (elapsed) time %lld, subscriber receives: %s\n", - lf_time_logical_elapsed(), - message->value - );*/ - - sscanf(message->value, "%d %llu", &self->signal_phase, &self->residual); - printf("EVENT: residual: %llu\n", self->residual); - printf("EVENT: phase: %d\n", self->signal_phase); - printf("spatReceiver: At phy time %lld, Phase:%d residual:%llu nsec.\n", lf_time_physical(), self->signal_phase, self->residual); - =} deadline (10 msec) {= - printf("EVENT: deadline_miss: true\n"); - =} + sscanf(message->value, "%d %llu", &self->signal_phase, &self->residual); + printf("EVENT: residual: %llu\n", self->residual); + printf("EVENT: phase: %d\n", self->signal_phase); + printf("spatReceiver: At phy time %lld, Phase:%d residual:%llu nsec.\n", lf_time_physical(), self->signal_phase, self->residual); + =} deadline (10 msec) {= + printf("EVENT: deadline_miss: true\n"); + =} } @@ -68,26 +68,26 @@ reactor recvSpatMessage2 { * @input message The message. */ reactor PrintMessage { - input message:char*; - reaction(message) {= - printf("PrintMessage: At (elapsed) time %lld, subscriber receives: %s\n", - lf_time_logical_elapsed(), - message->value - ); - =} + input message:char*; + reaction(message) {= + printf("PrintMessage: At (elapsed) time %lld, subscriber receives: %s\n", + lf_time_logical_elapsed(), + message->value + ); + =} } main reactor spatReceiver { - - rsuMsg = new recvSpatMessage2(); - sub = new MQTTSubscriber( - //address = "tcp://host.docker.internal:1883", - address = "tcp://localhost:1883", - clientID = "Vehicle 760", - topic = "spat/rsu101", - offset = 10 msec - ); - - - sub.message->rsuMsg.message; + + rsuMsg = new recvSpatMessage2(); + sub = new MQTTSubscriber( + //address = "tcp://host.docker.internal:1883", + address = "tcp://localhost:1883", + clientID = "Vehicle 760", + topic = "spat/rsu101", + offset = 10 msec + ); + + + sub.message->rsuMsg.message; } diff --git a/experimental/C/src/SpatAnalysis/spatRecommender.lf b/experimental/C/src/SpatAnalysis/spatRecommender.lf index 43b73777..3d3901ef 100644 --- a/experimental/C/src/SpatAnalysis/spatRecommender.lf +++ b/experimental/C/src/SpatAnalysis/spatRecommender.lf @@ -2,124 +2,124 @@ * Example PTIDES application for connected vehicle application * Demo Tile: RSU Coordinated Speed alignment * This is the Road side unit application that - * - Accepts requests to provide speed recommendations (based on its SPAT) from vehicles + * - Accepts requests to provide speed recommendations (based on its SPAT) from vehicles * - Uses MQTT pub/sub * @author Ravi Akella */ target C { - - threads: 1, // Must use threaded implementation so lf_schedule is thread safe. - flags: "-I/usr/local/include -L/usr/local/lib -g -lpaho-mqtt3c src-gen/core/util.c", - timeout: 60 secs, - keepalive: true + + threads: 1, // Must use threaded implementation so lf_schedule is thread safe. + flags: "-I/usr/local/include -L/usr/local/lib -g -lpaho-mqtt3c src-gen/core/util.c", + timeout: 60 secs, + keepalive: true }; import MQTTPublisher from "MQTTPublisher.lf"; reactor phaseChanger{ + + preamble {= + #define BILLION 1000000000LL + typedef struct spat_status{ + int signal_phase; //integer code for signal 1-Red, 2-Green, 3-Yellow + long long unsigned residual; // remaining time for phase change + } spat_status; - preamble {= - #define BILLION 1000000000LL - typedef struct spat_status{ - int signal_phase; //integer code for signal 1-Red, 2-Green, 3-Yellow - long long unsigned residual; // remaining time for phase change - } spat_status; - - =} + =} + + input in:int; + output out:char*; + output outStruct:spat_status; + + state status:spat_status(1, 0); //(signal_phase, residual time)) + + //Internal state variables + state phase_start_time:time(0 msec); + state phase_duration:time(0 msec); + + logical action red:int; + logical action green:int; + logical action yellow:int; + + reaction(startup) -> red {= + lf_schedule_int(red, MSEC(100), 1); + self->phase_start_time = lf_time_logical(); + =} + + reaction(red) -> green, out, outStruct {= + self->status.signal_phase = red->value; + self->phase_duration = MSEC(5000); + self->phase_start_time = lf_time_logical(); - input in:int; - output out:char*; - output outStruct:spat_status; + self->status.residual = self->phase_duration; + int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(out, length); + // Populate the output string and increment the count. + snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); + printf("spatRecommender: At phy time %lld, phase change message: %s\n", + lf_time_physical(), + out->value + ); - state status:spat_status(1, 0); //(signal_phase, residual time)) - - //Internal state variables - state phase_start_time:time(0 msec); - state phase_duration:time(0 msec); - - logical action red:int; - logical action green:int; - logical action yellow:int; - - reaction(startup) -> red {= - lf_schedule_int(red, MSEC(100), 1); - self->phase_start_time = lf_time_logical(); - =} - reaction(red) -> green, out, outStruct {= - self->status.signal_phase = red->value; - self->phase_duration = MSEC(5000); - self->phase_start_time = lf_time_logical(); - - self->status.residual = self->phase_duration; - int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(out, length); - // Populate the output string and increment the count. - snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); - printf("spatRecommender: At phy time %lld, phase change message: %s\n", - lf_time_physical(), - out->value - ); - - - lf_set(outStruct, self->status); - lf_schedule_int(green, self->phase_duration, 2); - =} - reaction(green) -> yellow, out {= - self->status.signal_phase = green->value; - self->phase_duration = MSEC(3000); - self->phase_start_time = lf_time_logical(); - - self->status.residual = self->phase_duration; - int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(out, length); - // Populate the output string and increment the count. - snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); - printf("spatRecommender: At phy time %lld, phase change message: %s\n", - lf_time_physical(), - out->value - ); - - lf_schedule_int(yellow, self->phase_duration, 3); - =} + lf_set(outStruct, self->status); + lf_schedule_int(green, self->phase_duration, 2); + =} + reaction(green) -> yellow, out {= + self->status.signal_phase = green->value; + self->phase_duration = MSEC(3000); + self->phase_start_time = lf_time_logical(); - reaction(yellow)->red, out {= - self->status.signal_phase = yellow->value; - self->phase_duration = MSEC(1000); - self->phase_start_time = lf_time_logical(); + self->status.residual = self->phase_duration; + int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(out, length); + // Populate the output string and increment the count. + snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); + printf("spatRecommender: At phy time %lld, phase change message: %s\n", + lf_time_physical(), + out->value + ); + + lf_schedule_int(yellow, self->phase_duration, 3); + =} + + reaction(yellow)->red, out {= + self->status.signal_phase = yellow->value; + self->phase_duration = MSEC(1000); + self->phase_start_time = lf_time_logical(); - self->status.residual = self->phase_duration; - int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(out, length); - // Populate the output string and increment the count. - snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); - printf("spatRecommender: At phy time %lld, phase change message: %s\n", - lf_time_physical(), - out->value - ); - - lf_schedule_int(red, self->phase_duration, 1); - =} + self->status.residual = self->phase_duration; + int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(out, length); + // Populate the output string and increment the count. + snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); + printf("spatRecommender: At phy time %lld, phase change message: %s\n", + lf_time_physical(), + out->value + ); - timer t(0, 500 msec); - reaction(t)->out{= + lf_schedule_int(red, self->phase_duration, 1); + =} + + timer t(0, 500 msec); + reaction(t)->out{= - self->status.residual = self->phase_duration - lf_time_logical() + self->phase_start_time; - int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; - // Dynamically allocate memory for the output. - SET_NEW_ARRAY(out, length); - // Populate the output string and increment the count. - snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); - printf("spatRecommender: At phy time %lld, publish message: %s\n", - lf_time_physical(), - out->value - ); - =} + self->status.residual = self->phase_duration - lf_time_logical() + self->phase_start_time; + int length = snprintf(NULL, 0, "%d %llu", self->status.signal_phase, self->status.residual) + 1; + // Dynamically allocate memory for the output. + SET_NEW_ARRAY(out, length); + // Populate the output string and increment the count. + snprintf(out->value, length, "%d %llu", self->status.signal_phase, self->status.residual); + printf("spatRecommender: At phy time %lld, publish message: %s\n", + lf_time_physical(), + out->value + ); + =} } /** @@ -128,38 +128,38 @@ reactor phaseChanger{ * @input message The message. */ reactor recvSpatMessage { - input message:char*; - reaction(message) {= - printf("Received (phase, residual in nsec) %s:\n", message->value); - - /*printf("Received Phase=\"%d\", residual=%llu.\n", - message->signal_phase, message->residual - ); - */ - =} + input message:char*; + reaction(message) {= + printf("Received (phase, residual in nsec) %s:\n", message->value); + + /*printf("Received Phase=\"%d\", residual=%llu.\n", + message->signal_phase, message->residual + ); + */ + =} } // expected parameter is for testing. reactor Print { - input in:char*; - - reaction(in) {= - printf("Received: name = %d, value = %lld\n", in->value.signal_phase, in->value.residual); - =} + input in:char*; + + reaction(in) {= + printf("Received: name = %d, value = %lld\n", in->value.signal_phase, in->value.residual); + =} } main reactor spatRecommender { - signal = new phaseChanger(); + signal = new phaseChanger(); - pub = new MQTTPublisher( - topic = "spat/rsu101", - //address = "tcp://host.docker.internal:1883", - address = "tcp://localhost:1883", - clientID = "Intersection Signal 101", - include_physical_timestamp = 0 - ); - - signal.out -> pub.in; - + pub = new MQTTPublisher( + topic = "spat/rsu101", + //address = "tcp://host.docker.internal:1883", + address = "tcp://localhost:1883", + clientID = "Intersection Signal 101", + include_physical_timestamp = 0 + ); + + signal.out -> pub.in; + } diff --git a/experimental/C/src/context-manager/ContextManager.lf b/experimental/C/src/context-manager/ContextManager.lf index 0976b8ff..1523d611 100644 --- a/experimental/C/src/context-manager/ContextManager.lf +++ b/experimental/C/src/context-manager/ContextManager.lf @@ -22,131 +22,131 @@ * different services. */ target C { - timeout: 1s + timeout: 1s } import Random from "../../lib/Random.lf" preamble {= - #include // FIXME: should not be needed. Should get from Random.lf. - - // Pair of numbers to be added. - typedef struct pair_t { - int first; - int second; - } pair_t; - - // Request structure, input data and the context. - typedef struct request_t { - pair_t data; - int context; - } request_t; - - // Tagged request. Service should include the tag in its response. - typedef struct tagged_request_t { - request_t request; - tag_t tag; - } tagged_request_t; + #include // FIXME: should not be needed. Should get from Random.lf. + + // Pair of numbers to be added. + typedef struct pair_t { + int first; + int second; + } pair_t; - // Response data type, the sum plus the context. - typedef struct response_t { - int data; - int context; - } response_t; + // Request structure, input data and the context. + typedef struct request_t { + pair_t data; + int context; + } request_t; + + // Tagged request. Service should include the tag in its response. + typedef struct tagged_request_t { + request_t request; + tag_t tag; + } tagged_request_t; + + // Response data type, the sum plus the context. + typedef struct response_t { + int data; + int context; + } response_t; - // Tagged response. - typedef struct tagged_response_t { - response_t response; - tag_t tag; - } tagged_response_t; + // Tagged response. + typedef struct tagged_response_t { + response_t response; + tag_t tag; + } tagged_response_t; =} // Interface definition for the service. reactor ServiceInterface { - input in:tagged_request_t - output out:tagged_response_t + input in:tagged_request_t + output out:tagged_response_t } -reactor AddService extends ServiceInterface, Random { - logical action pending:tagged_request_t - - reaction(in) -> pending {= - // Random delay between 100 and 500 msec. - interval_t delay = MSEC(1) * (random() % 400 + 100); - lf_schedule_copy(pending, delay, &in->value, 1); - =} - reaction(pending) -> out {= - tagged_response_t response; - response.response.data = pending->value.request.data.first + pending->value.request.data.second; - response.response.context = pending->value.request.context; - response.tag = pending->value.tag; - lf_set(out, response); - =} +reactor AddService extends ServiceInterface, Random { + logical action pending:tagged_request_t + + reaction(in) -> pending {= + // Random delay between 100 and 500 msec. + interval_t delay = MSEC(1) * (random() % 400 + 100); + lf_schedule_copy(pending, delay, &in->value, 1); + =} + reaction(pending) -> out {= + tagged_response_t response; + response.response.data = pending->value.request.data.first + pending->value.request.data.second; + response.response.context = pending->value.request.context; + response.tag = pending->value.tag; + lf_set(out, response); + =} } reactor Manager { - input request:request_t - output response:response_t + input request:request_t + output response:response_t - state pending:tagged_request_t[10] = {= { 0 } =} - - service = new AddService() - - reaction(startup) {= - // Initialize brute force hashtable. - for(int i = 0; i < 10; i++) { - self->pending[i].tag = NEVER_TAG; - } - =} + state pending:tagged_request_t[10] = {= { 0 } =} + + service = new AddService() + + reaction(startup) {= + // Initialize brute force hashtable. + for(int i = 0; i < 10; i++) { + self->pending[i].tag = NEVER_TAG; + } + =} - reaction(request) -> service.in {= - // Find an empty slot in the pending requests. BRUTE FORCE. - for(int i = 0; i < 10; i++) { - if (lf_tag_compare(self->pending[i].tag, NEVER_TAG) == 0) { - // Found one. - self->pending[i].request = request->value; - self->pending[i].tag = lf_tag(); - lf_set(service.in, self->pending[i]); - return; - } - } - lf_print_error("Maximum number of pending requests exceeded."); - =} - - reaction(service.out) -> response {= - // Find the pending requests. BRUTE FORCE. - for(int i = 0; i < 10; i++) { - if (lf_tag_compare(self->pending[i].tag, service.out->value.tag) == 0) { - // Found it. Reset so slot can be reused. - self->pending[i].tag = NEVER_TAG; - lf_set(response, service.out->value.response); - return; - } - } - lf_print_error("Pending requests not found."); - =} + reaction(request) -> service.in {= + // Find an empty slot in the pending requests. BRUTE FORCE. + for(int i = 0; i < 10; i++) { + if (lf_tag_compare(self->pending[i].tag, NEVER_TAG) == 0) { + // Found one. + self->pending[i].request = request->value; + self->pending[i].tag = lf_tag(); + lf_set(service.in, self->pending[i]); + return; + } + } + lf_print_error("Maximum number of pending requests exceeded."); + =} + + reaction(service.out) -> response {= + // Find the pending requests. BRUTE FORCE. + for(int i = 0; i < 10; i++) { + if (lf_tag_compare(self->pending[i].tag, service.out->value.tag) == 0) { + // Found it. Reset so slot can be reused. + self->pending[i].tag = NEVER_TAG; + lf_set(response, service.out->value.response); + return; + } + } + lf_print_error("Pending requests not found."); + =} } reactor Client { - timer t(0, 100ms) - state counter:int = 0 - m = new Manager(); + timer t(0, 100ms) + state counter:int = 0 + m = new Manager(); + + reaction(t) -> m.request {= + lf_print("Client asks what %d + 42 is.", self->counter); - reaction(t) -> m.request {= - lf_print("Client asks what %d + 42 is.", self->counter); - - pair_t to_sum = { - .first = self->counter, - .second = 42 - }; - request_t request = { - .data = to_sum, - .context = self->counter - }; - lf_set(m.request, request); - self->counter++; - =} - - reaction(m.response) {= - lf_print("Result: %d + 42 = %d.", m.response->value.context, m.response->value.data); - =} + pair_t to_sum = { + .first = self->counter, + .second = 42 + }; + request_t request = { + .data = to_sum, + .context = self->counter + }; + lf_set(m.request, request); + self->counter++; + =} + + reaction(m.response) {= + lf_print("Result: %d + 42 = %d.", m.response->value.context, m.response->value.data); + =} } main reactor { - c = new Client(); -} \ No newline at end of file + c = new Client(); +} diff --git a/experimental/C/src/context-manager/ContextManager2.lf b/experimental/C/src/context-manager/ContextManager2.lf index b74e2a22..5575a720 100644 --- a/experimental/C/src/context-manager/ContextManager2.lf +++ b/experimental/C/src/context-manager/ContextManager2.lf @@ -3,164 +3,164 @@ * is shared among multiple clients. */ target C { - timeout: 1s + timeout: 1s } import Random from "../../lib/Random.lf" preamble {= - #include // FIXME: should not be needed. Should get from Random.lf. - - // Pair of numbers to be added. - typedef struct pair_t { - int first; - int second; - } pair_t; - - // Request structure, input data and the context. - typedef struct request_t { - pair_t data; - int context; - } request_t; - - // Tagged request. Service should include the tag in its response. - typedef struct tagged_request_t { - request_t request; - tag_t tag; - int channel; - } tagged_request_t; + #include // FIXME: should not be needed. Should get from Random.lf. + + // Pair of numbers to be added. + typedef struct pair_t { + int first; + int second; + } pair_t; - // Response data type, the sum plus the context. - typedef struct response_t { - int data; - int context; - } response_t; + // Request structure, input data and the context. + typedef struct request_t { + pair_t data; + int context; + } request_t; + + // Tagged request. Service should include the tag in its response. + typedef struct tagged_request_t { + request_t request; + tag_t tag; + int channel; + } tagged_request_t; + + // Response data type, the sum plus the context. + typedef struct response_t { + int data; + int context; + } response_t; - // Tagged response. - typedef struct tagged_response_t { - response_t response; - tag_t tag; - int channel; - } tagged_response_t; + // Tagged response. + typedef struct tagged_response_t { + response_t response; + tag_t tag; + int channel; + } tagged_response_t; =} // Interface definition for client reactor ClientInterface { - output request:request_t - input response:response_t + output request:request_t + input response:response_t } // Mirror image of client interface for manager. reactor ManagerInterface { - input request:request_t - output response:response_t - output trequest:tagged_request_t - input tresponse:tagged_response_t + input request:request_t + output response:response_t + output trequest:tagged_request_t + input tresponse:tagged_response_t } // Interface definition for the service. reactor ServiceInterface(width:int = 2) { - input[width] in:tagged_request_t - output[width] out:tagged_response_t + input[width] in:tagged_request_t + output[width] out:tagged_response_t } -reactor AddService extends ServiceInterface, Random { - logical action pending:tagged_request_t - - reaction(pending) -> out {= - tagged_response_t response; - response.response.data = pending->value.request.data.first + pending->value.request.data.second; - response.response.context = pending->value.request.context; - response.tag = pending->value.tag; - lf_set(out[pending->value.channel], response); - =} - reaction(in) -> pending {= - for (int i = 0; i < in_width; i++) { - if (in[i]->is_present) { - // Record the input channel. - in[i]->value.channel = i; - // Random delay between 100 and 500 msec. - interval_t delay = MSEC(1) * (random() % 400 + 100); - lf_schedule_copy(pending, delay, &in[i]->value, 1); - } - } - =} +reactor AddService extends ServiceInterface, Random { + logical action pending:tagged_request_t + + reaction(pending) -> out {= + tagged_response_t response; + response.response.data = pending->value.request.data.first + pending->value.request.data.second; + response.response.context = pending->value.request.context; + response.tag = pending->value.tag; + lf_set(out[pending->value.channel], response); + =} + reaction(in) -> pending {= + for (int i = 0; i < in_width; i++) { + if (in[i]->is_present) { + // Record the input channel. + in[i]->value.channel = i; + // Random delay between 100 and 500 msec. + interval_t delay = MSEC(1) * (random() % 400 + 100); + lf_schedule_copy(pending, delay, &in[i]->value, 1); + } + } + =} } reactor Manager extends ManagerInterface { - state pending:tagged_request_t[10] = {= { 0 } =} - - reaction(startup) {= - // Initialize brute force hashtable. - for(int i = 0; i < 10; i++) { - self->pending[i].tag = NEVER_TAG; - } - =} - - reaction(request) -> trequest {= - // Find an empty slot in the pending requests. BRUTE FORCE. - for(int i = 0; i < 10; i++) { - if (lf_tag_compare(self->pending[i].tag, NEVER_TAG) == 0) { - // Found one. - self->pending[i].request = request->value; - self->pending[i].tag = lf_tag(); - lf_set(trequest, self->pending[i]); - return; - } - } - lf_print_error("Maximum number of pending requests exceeded."); - =} + state pending:tagged_request_t[10] = {= { 0 } =} - reaction(tresponse) -> response {= - // Find the pending requests. BRUTE FORCE. - for(int i = 0; i < 10; i++) { - if (lf_tag_compare(self->pending[i].tag, tresponse->value.tag) == 0) { - // Found it. Reset so slot can be reused. - self->pending[i].tag = NEVER_TAG; - lf_set(response, tresponse->value.response); - return; - } - } - lf_print_error("Pending requests not found."); - =} + reaction(startup) {= + // Initialize brute force hashtable. + for(int i = 0; i < 10; i++) { + self->pending[i].tag = NEVER_TAG; + } + =} + + reaction(request) -> trequest {= + // Find an empty slot in the pending requests. BRUTE FORCE. + for(int i = 0; i < 10; i++) { + if (lf_tag_compare(self->pending[i].tag, NEVER_TAG) == 0) { + // Found one. + self->pending[i].request = request->value; + self->pending[i].tag = lf_tag(); + lf_set(trequest, self->pending[i]); + return; + } + } + lf_print_error("Maximum number of pending requests exceeded."); + =} + + reaction(tresponse) -> response {= + // Find the pending requests. BRUTE FORCE. + for(int i = 0; i < 10; i++) { + if (lf_tag_compare(self->pending[i].tag, tresponse->value.tag) == 0) { + // Found it. Reset so slot can be reused. + self->pending[i].tag = NEVER_TAG; + lf_set(response, tresponse->value.response); + return; + } + } + lf_print_error("Pending requests not found."); + =} } reactor Client(start:int = 0) extends ClientInterface { + + timer t(0, 100ms) + state counter:int = start + + reaction(t) -> request {= + lf_print("Client asks what %d + 42 is.", self->counter); - timer t(0, 100ms) - state counter:int = start - - reaction(t) -> request {= - lf_print("Client asks what %d + 42 is.", self->counter); - - pair_t to_sum = { - .first = self->counter, - .second = 42 - }; - request_t req = { - .data = to_sum, - .context = self->counter - }; - lf_set(request, req); - self->counter++; - =} - - reaction(response) {= - lf_print("Result: %d + 42 = %d.", response->value.context, response->value.data); - =} + pair_t to_sum = { + .first = self->counter, + .second = 42 + }; + request_t req = { + .data = to_sum, + .context = self->counter + }; + lf_set(request, req); + self->counter++; + =} + + reaction(response) {= + lf_print("Result: %d + 42 = %d.", response->value.context, response->value.data); + =} } main reactor { - c1 = new Client(); - c2 = new Client(start = 10); - m1 = new Manager(); - m2 = new Manager(); - service = new AddService() - - c1.request -> m1.request - m1.response -> c1.response - c2.request -> m2.request - m2.response -> c2.response - // FIXME: The following more compact notation fails. - // c1.request, m1.response -> m1.request, c1.response - // c2.request, m2.response -> m2.request, c2.response - m1.trequest, m2.trequest -> service.in - service.out -> m1.tresponse, m2.tresponse -} \ No newline at end of file + c1 = new Client(); + c2 = new Client(start = 10); + m1 = new Manager(); + m2 = new Manager(); + service = new AddService() + + c1.request -> m1.request + m1.response -> c1.response + c2.request -> m2.request + m2.response -> c2.response + // FIXME: The following more compact notation fails. + // c1.request, m1.response -> m1.request, c1.response + // c2.request, m2.response -> m2.request, c2.response + m1.trequest, m2.trequest -> service.in + service.out -> m1.tresponse, m2.tresponse +} diff --git a/experimental/C/src/protection-relay/ProtectionRelay.lf b/experimental/C/src/protection-relay/ProtectionRelay.lf index 7a58d259..9566b998 100644 --- a/experimental/C/src/protection-relay/ProtectionRelay.lf +++ b/experimental/C/src/protection-relay/ProtectionRelay.lf @@ -41,106 +41,106 @@ target C @label("Acquisition stage") reactor AgARGA { - output DataBufferI:double[4] - timer t(0, 555 us) - reaction(t) -> DataBufferI {= - // Output something. - =} + output DataBufferI:double[4] + timer t(0, 555 us) + reaction(t) -> DataBufferI {= + // Output something. + =} } @label("Accumulate") reactor AgCumulRMS { - input currents:double[4] - output VS_TRS_Cumu1:double[4] - output VS_TRS_Cumu2:double[4] - reaction(currents) -> VS_TRS_Cumu1, VS_TRS_Cumu2 {= - // Calculate sum and the square sum of num_items data items. - // Outputs updated every time, but only used every 1800 times. - =} + input currents:double[4] + output VS_TRS_Cumu1:double[4] + output VS_TRS_Cumu2:double[4] + reaction(currents) -> VS_TRS_Cumu1, VS_TRS_Cumu2 {= + // Calculate sum and the square sum of num_items data items. + // Outputs updated every time, but only used every 1800 times. + =} } // The AgRMS component does not seem to be used in protection. // Is this just for UI displays? @label("RMS") reactor AgRMS { - timer t(0, 999ms) // Activate periodically every 1800 samples (1800*555us) - input sum:double[4] - input squared_sum:double[4] - input VS_Mod2:double // FIXME: Is this an input or an output? - reaction(t) sum, squared_sum, VS_Mod2 {= - // Calculate root mean square of the four currents - =} + timer t(0, 999ms) // Activate periodically every 1800 samples (1800*555us) + input sum:double[4] + input squared_sum:double[4] + input VS_Mod2:double // FIXME: Is this an input or an output? + reaction(t) sum, squared_sum, VS_Mod2 {= + // Calculate root mean square of the four currents + =} } @label("Crest value") reactor AgCrete { - input currents:double[4] - output V_DETC:double[4] - reaction(currents) -> V_DETC {= - // Compute crest values. Update output on each input. - =} + input currents:double[4] + output V_DETC:double[4] + reaction(currents) -> V_DETC {= + // Compute crest values. Update output on each input. + =} } @label("Average") reactor AgMoy(num_values:int(3)) { - input currents:double[4] - output V_TRS_Cumu1Filtree:double[4] - reaction(currents) -> V_TRS_Cumu1Filtree {= - // Average num_values - =} + input currents:double[4] + output V_TRS_Cumu1Filtree:double[4] + reaction(currents) -> V_TRS_Cumu1Filtree {= + // Average num_values + =} } @label("Magnitude of fundamental and harmonics") reactor AgTRS(num_values:int(12)) { - timer t(0, 13320us) // Computes an output every 24 periods of 555us. - input average:double[4] - input crest:double[4] - state avghistory:double[12]({= [0] =}) - output V_mod2Imax:double[4] - output VS_Mod2:double[4] - reaction(average) {= - // Record the input, update avghistory - =} - reaction(t) crest -> V_mod2Imax {= - // Use last avghistory inputs and the most - // recent value of crest to calculate, - // but activate every 2*num_values inputs. - // ??? "Nevertheless, its clock ticks every num_values inputs. - =} + timer t(0, 13320us) // Computes an output every 24 periods of 555us. + input average:double[4] + input crest:double[4] + state avghistory:double[12]({= [0] =}) + output V_mod2Imax:double[4] + output VS_Mod2:double[4] + reaction(average) {= + // Record the input, update avghistory + =} + reaction(t) crest -> V_mod2Imax {= + // Use last avghistory inputs and the most + // recent value of crest to calculate, + // but activate every 2*num_values inputs. + // ??? "Nevertheless, its clock ticks every num_values inputs. + =} } reactor Ag50 { - timer t(0, 13320us) // Computes an output every 24 periods of 555us. - // Or should this be 666us (12 times 555)? - input in:double[4] - reaction(t) in {= - // Calculate whether to open the relay. - =} deadline(26640us) {= - // Deadline missed! - =} + timer t(0, 13320us) // Computes an output every 24 periods of 555us. + // Or should this be 666us (12 times 555)? + input in:double[4] + reaction(t) in {= + // Calculate whether to open the relay. + =} deadline(26640us) {= + // Deadline missed! + =} } reactor Ag51 { - timer t(0, 13320us) // Computes an output every 24 periods of 555us. - // Or should this be 666us (12 times 555)? - input in:double[4] - reaction(t) in {= - // Calculate whether to open the relay. - =} deadline(26640us) {= - // Deadline missed! - =} + timer t(0, 13320us) // Computes an output every 24 periods of 555us. + // Or should this be 666us (12 times 555)? + input in:double[4] + reaction(t) in {= + // Calculate whether to open the relay. + =} deadline(26640us) {= + // Deadline missed! + =} } main reactor { - acquisition = new AgARGA() - accumulate = new AgCumulRMS() - rms = new AgRMS() - crest = new AgCrete() - average = new AgMoy() - magnitude = new AgTRS() - protection1 = new Ag50() - protection2 = new Ag51() - - acquisition.DataBufferI -> accumulate.currents - accumulate.VS_TRS_Cumu1 -> rms.sum - accumulate.VS_TRS_Cumu2 -> rms.squared_sum - acquisition.DataBufferI -> crest.currents - acquisition.DataBufferI -> average.currents - average.V_TRS_Cumu1Filtree -> magnitude.average - crest.V_DETC -> magnitude.crest - magnitude.V_mod2Imax -> protection1.in - magnitude.V_mod2Imax -> protection2.in - magnitude.VS_Mod2 -> rms.VS_Mod2 -} \ No newline at end of file + acquisition = new AgARGA() + accumulate = new AgCumulRMS() + rms = new AgRMS() + crest = new AgCrete() + average = new AgMoy() + magnitude = new AgTRS() + protection1 = new Ag50() + protection2 = new Ag51() + + acquisition.DataBufferI -> accumulate.currents + accumulate.VS_TRS_Cumu1 -> rms.sum + accumulate.VS_TRS_Cumu2 -> rms.squared_sum + acquisition.DataBufferI -> crest.currents + acquisition.DataBufferI -> average.currents + average.V_TRS_Cumu1Filtree -> magnitude.average + crest.V_DETC -> magnitude.crest + magnitude.V_mod2Imax -> protection1.in + magnitude.V_mod2Imax -> protection2.in + magnitude.VS_Mod2 -> rms.VS_Mod2 +} diff --git a/experimental/C/src/soafee/soafee.lf b/experimental/C/src/soafee/soafee.lf index 9ea65a95..5bea9054 100644 --- a/experimental/C/src/soafee/soafee.lf +++ b/experimental/C/src/soafee/soafee.lf @@ -1,124 +1,124 @@ target C{ - keepalive: true + keepalive: true }; reactor Display(width: int(4)){ - //Center/HUD/Cluster indicating direction of obstacle - input[width] obstacleList:int - output angleOfObstacle:int[] - - reaction(startup) -> angleOfObstacle {= - for (int i = 0; i < 4; i++) { - angleOfObstacle->value[i] = 0; - } - SET_PRESENT(angleOfObstacle); - =} - - reaction(obstacleList)->angleOfObstacle{= - //TODO: Handling of graphics - //0 - left front (135) - //1 - right front (45) - //2 - left rear (225) - //3 - right rear (315) - for (int i = 0; i < angleOfObstacle->length; i++) { - angleOfObstacle->value[i] = 0; - } - - for (int i = 0; i < obstacleList_width; i++) { - if (obstacleList[i]->is_present && obstacleList[i]->value) { - - //lf_set(angleOfObstacle[i], 1); - - lf_sleep(MSEC(100)); - } - } - =} + //Center/HUD/Cluster indicating direction of obstacle + input[width] obstacleList:int + output angleOfObstacle:int[] + + reaction(startup) -> angleOfObstacle {= + for (int i = 0; i < 4; i++) { + angleOfObstacle->value[i] = 0; + } + SET_PRESENT(angleOfObstacle); + =} + + reaction(obstacleList)->angleOfObstacle{= + //TODO: Handling of graphics + //0 - left front (135) + //1 - right front (45) + //2 - left rear (225) + //3 - right rear (315) + for (int i = 0; i < angleOfObstacle->length; i++) { + angleOfObstacle->value[i] = 0; + } + + for (int i = 0; i < obstacleList_width; i++) { + if (obstacleList[i]->is_present && obstacleList[i]->value) { + + //lf_set(angleOfObstacle[i], 1); + + lf_sleep(MSEC(100)); + } + } + =} } reactor Speaker(width: int(4)){ - //3D sound generation - input[width] obstacleList:int - output alert:bool - reaction(obstacleList)->alert{= - //Handling of device audio - instant_t processing_delay = MSEC(30); - lf_sleep(processing_delay); - - for (int i = 0; i < obstacleList_width; i++) { - if (obstacleList[i]->is_present && obstacleList[i]->value){ - lf_set(alert, true); - } - else{ - lf_set(alert, false); - } - } - =} + //3D sound generation + input[width] obstacleList:int + output alert:bool + reaction(obstacleList)->alert{= + //Handling of device audio + instant_t processing_delay = MSEC(30); + lf_sleep(processing_delay); + + for (int i = 0; i < obstacleList_width; i++) { + if (obstacleList[i]->is_present && obstacleList[i]->value){ + lf_set(alert, true); + } + else{ + lf_set(alert, false); + } + } + =} } reactor DistanceSensor(bank_index: int(0)){ - timer t(0, 10 ms); - physical action pa; - output range:int; //in meters - - reaction(t)->pa{= - //Schedule scan - lf_schedule(pa, MSEC(5000)); - =} - - reaction(pa)->range{= - int distance = rand() % 25; - lf_set(range, distance); - =} + timer t(0, 10 ms); + physical action pa; + output range:int; //in meters + + reaction(t)->pa{= + //Schedule scan + lf_schedule(pa, MSEC(5000)); + =} + + reaction(pa)->range{= + int distance = rand() % 25; + lf_set(range, distance); + =} } reactor Monitor{ - mutable input display_event:int[]; - input speaker_event:bool; - - reaction(display_event){= - for (int i = 0; i < display_event->length; i++) { - lf_print("Display: %d", display_event->value[i]); - } - =} - - reaction(speaker_event){= - lf_print("Speaker: %d", speaker_event->value); - =} + mutable input display_event:int[]; + input speaker_event:bool; + + reaction(display_event){= + for (int i = 0; i < display_event->length; i++) { + lf_print("Display: %d", display_event->value[i]); + } + =} + + reaction(speaker_event){= + lf_print("Speaker: %d", speaker_event->value); + =} } reactor ProcessSignals(width: int(4)){ - input[width] rangeList:int - output[width] obstacles:int - - reaction(rangeList)->obstacles{= - for (int i = 0; i < rangeList_width; i++) { - //0 - left front - //1 - right front - //2 - left rear - //3 - right rear - if (rangeList[i]->is_present){ - if (rangeList[i]->value < 10){ - lf_set(obstacles[i], 1); - } - else{ - lf_set(obstacles[i], 0); - } - } + input[width] rangeList:int + output[width] obstacles:int + + reaction(rangeList)->obstacles{= + for (int i = 0; i < rangeList_width; i++) { + //0 - left front + //1 - right front + //2 - left rear + //3 - right rear + if (rangeList[i]->is_present){ + if (rangeList[i]->value < 10){ + lf_set(obstacles[i], 1); } - =} + else{ + lf_set(obstacles[i], 0); + } + } + } + =} } main reactor { - #speaker = new Speaker() - display = new Display() - ds = new[4] DistanceSensor() - ps = new ProcessSignals(width=4) - ds.range -> ps.rangeList - - #ps.obstacles -> display.obstacleList - #ps.obstacles -> speaker.obstacleList - - #monitor = new Monitor() - #display.angleOfObstacle -> monitor.display_event - #speaker.alert -> monitor.speaker_event -} \ No newline at end of file + #speaker = new Speaker() + display = new Display() + ds = new[4] DistanceSensor() + ps = new ProcessSignals(width=4) + ds.range -> ps.rangeList + + #ps.obstacles -> display.obstacleList + #ps.obstacles -> speaker.obstacleList + + #monitor = new Monitor() + #display.angleOfObstacle -> monitor.display_event + #speaker.alert -> monitor.speaker_event +} diff --git a/experimental/Python/src/FurutaPendulum/FurutaPendulumStabilize.lf b/experimental/Python/src/FurutaPendulum/FurutaPendulumStabilize.lf index d83b4f03..c41eb033 100644 --- a/experimental/Python/src/FurutaPendulum/FurutaPendulumStabilize.lf +++ b/experimental/Python/src/FurutaPendulum/FurutaPendulumStabilize.lf @@ -3,12 +3,12 @@ # the stabilize-mode control law. # # Need to install matplotlib library: -# pip install matplotlib +# pip install matplotlib # # @author Edward A. Lee target Python { - timeout: 8 secs, - fast: true + timeout: 8 secs, + fast: true } import PendulumSimulation from "PendulumSimulationRK4.lf" @@ -16,60 +16,60 @@ import PendulumSimulation from "PendulumSimulationRK4.lf" # import PendulumSimulation from "PendulumSimulationEuler.lf" preamble {= - import matplotlib.pyplot as plt + import matplotlib.pyplot as plt =} reactor Plotter { - input data - state times({=[]=}) - state values({=[]=}) - reaction(data) {= - self.times.append(lf.time.logical_elapsed()/1e9) - self.values.append(data.value) - =} - reaction(shutdown) {= - fig, axs = plt.subplots() - axs.set_title("Pendulum Angle") - axs.plot(self.times, self.values, color="C0") - axs.set_xlabel("Time (secs)") - axs.set_ylabel("Angle (radians)") - plt.show() - =} + input data + state times({=[]=}) + state values({=[]=}) + reaction(data) {= + self.times.append(lf.time.logical_elapsed()/1e9) + self.values.append(data.value) + =} + reaction(shutdown) {= + fig, axs = plt.subplots() + axs.set_title("Pendulum Angle") + axs.plot(self.times, self.values, color="C0") + axs.set_xlabel("Time (secs)") + axs.set_ylabel("Angle (radians)") + plt.show() + =} } reactor PendulumController( - g1(1.7), - g2(0.3), - g3(0.03), - g4(0.06) + g1(1.7), + g2(0.3), + g3(0.03), + g4(0.06) ) { - input theta - input d_theta - input phi - input d_phi + input theta + input d_theta + input phi + input d_phi - output control + output control - reaction(theta, d_theta, phi, d_phi) -> control {= - control.set(1.0 * ( - theta.value * self.g1 - + d_theta.value * self.g2 - + phi.value * self.g3 - + d_phi.value * self.g4 - )) - =} + reaction(theta, d_theta, phi, d_phi) -> control {= + control.set(1.0 * ( + theta.value * self.g1 + + d_theta.value * self.g2 + + phi.value * self.g3 + + d_phi.value * self.g4 + )) + =} } main reactor { - timer d(3 sec); // One-shot disturbance at 3 seconds. + timer d(3 sec); // One-shot disturbance at 3 seconds. - p = new PendulumSimulation(initial_theta = 0.1) - c = new PendulumController() - plt = new Plotter() - p.theta, p.d_theta, p.phi, p.d_phi - -> c.theta, c.d_theta, c.phi, c.d_phi - c.control -> p.u - p.theta -> plt.data + p = new PendulumSimulation(initial_theta = 0.1) + c = new PendulumController() + plt = new Plotter() + p.theta, p.d_theta, p.phi, p.d_phi + -> c.theta, c.d_theta, c.phi, c.d_phi + c.control -> p.u + p.theta -> plt.data - reaction(d) -> p.d {= - p.d.set(1.0) - =} -} \ No newline at end of file + reaction(d) -> p.d {= + p.d.set(1.0) + =} +} diff --git a/experimental/Python/src/FurutaPendulum/PendulumSimulationEuler.lf b/experimental/Python/src/FurutaPendulum/PendulumSimulationEuler.lf index f9fa48c4..fa596b7b 100644 --- a/experimental/Python/src/FurutaPendulum/PendulumSimulationEuler.lf +++ b/experimental/Python/src/FurutaPendulum/PendulumSimulationEuler.lf @@ -36,126 +36,126 @@ target Python; * @author Edward A. Lee */ preamble {= - import math + import math =} reactor PendulumSimulation( - initial_theta(-3.14159), # Initial pendulum angle. - sample_period(5 msec), # Sample period. - g(9.81), # Acceleration of gravity. - alpha(0.00260569), - beta(0.05165675), - gamma(9.7055e-4), - epsilon(0.08103060) + initial_theta(-3.14159), # Initial pendulum angle. + sample_period(5 msec), # Sample period. + g(9.81), # Acceleration of gravity. + alpha(0.00260569), + beta(0.05165675), + gamma(9.7055e-4), + epsilon(0.08103060) ){ - input u # Control input. - input d # Impulsive disturbance - - output theta # Pendulum angle. - output d_theta # Pendulum angular velocity. - output phi # Arm angle. - output d_phi # Arm angular velocity. - - state x(0.0, 0.0, 0.0, 0.0) - state first(True) - state latest_u(0.0) - - timer t(0, sample_period) - - reaction(d) {= - # NOTE: Here, we effectively shift the disturbance to be - # simultaneous with the next sample. A more accurate model - # would apply the disturbance immediately. - # The update of the state vector in the reaction below - # should first be factored out to a method, when methods - # are implemented in C. - self.x[1] = self.x[1] + d.value - =} + input u # Control input. + input d # Impulsive disturbance + + output theta # Pendulum angle. + output d_theta # Pendulum angular velocity. + output phi # Arm angle. + output d_phi # Arm angular velocity. + + state x(0.0, 0.0, 0.0, 0.0) + state first(True) + state latest_u(0.0) + + timer t(0, sample_period) + + reaction(d) {= + # NOTE: Here, we effectively shift the disturbance to be + # simultaneous with the next sample. A more accurate model + # would apply the disturbance immediately. + # The update of the state vector in the reaction below + # should first be factored out to a method, when methods + # are implemented in C. + self.x[1] = self.x[1] + d.value + =} - reaction(t) -> theta, d_theta, phi, d_phi {= - if not(self.first): - # Update the state. - x0_dot = self.x[1] - x1_dot = (1.0/( - self.alpha * self.beta - + math.pow(self.alpha * math.sin(self.x[0]), 2.0) - - math.pow(self.gamma * math.cos(self.x[0]), 2.0) - ) * ( - (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) - * math.pow(self.x[3], 2.0) - * math.sin(self.x[0]) - * math.cos(self.x[0]) - - - math.pow(self.gamma * self.x[1], 2.0) - * math.sin(self.x[0]) - * math.cos(self.x[0]) - + - 2.0 - * self.alpha - * self.gamma\ - * self.x[1] - * self.x[3] - * math.sin(self.x[0]) - * math.pow(math.cos(self.x[0]), 2.0) - - - self.gamma - * math.cos(self.x[0]) - * self.g - * self.latest_u - + - (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) - * self.epsilon / self.alpha * math.sin(self.x[0]) - ) - ) - x2_dot = self.x[3] - x3_dot = ((1.0 / ( - self.alpha * self.beta - + math.pow(self.alpha * math.sin(self.x[0]), 2.0) - - math.pow(self.gamma * math.cos(self.x[0]), 2.0) - )) * ( - -self.gamma - * self.alpha - * math.pow(self.x[3], 2.0) - * math.sin(self.x[0]) - * math.pow(math.cos(self.x[1]), 2.0) - - - self.gamma - * self.epsilon - * math.sin(self.x[0]) - * math.cos(self.x[0]) - + - self.gamma - * self.alpha - * math.pow(self.x[1], 2.0) - * math.sin(self.x[0]) - - - 2 - * math.pow(self.alpha, 2.0) - * self.x[1] - * self.x[3] - * math.sin(self.x[0]) - * math.cos(self.x[0]) - + - self.alpha - * self.g - * self.latest_u - ) - ) - sample_period = self.sample_period * 1e-9 - self.x[0] += x0_dot * sample_period - self.x[1] += x1_dot * sample_period - self.x[2] += x2_dot * sample_period - self.x[3] += x3_dot * sample_period - else: - self.x[0] = self.initial_theta - self.first = False - - # Output the state. - theta.set(self.x[0]) - d_theta.set(self.x[1]) - phi.set(self.x[2]) - d_phi.set(self.x[3]) - =} - reaction(u) {= - self.latest_u = u.value - =} + reaction(t) -> theta, d_theta, phi, d_phi {= + if not(self.first): + # Update the state. + x0_dot = self.x[1] + x1_dot = (1.0/( + self.alpha * self.beta + + math.pow(self.alpha * math.sin(self.x[0]), 2.0) + - math.pow(self.gamma * math.cos(self.x[0]), 2.0) + ) * ( + (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) + * math.pow(self.x[3], 2.0) + * math.sin(self.x[0]) + * math.cos(self.x[0]) + - + math.pow(self.gamma * self.x[1], 2.0) + * math.sin(self.x[0]) + * math.cos(self.x[0]) + + + 2.0 + * self.alpha + * self.gamma\ + * self.x[1] + * self.x[3] + * math.sin(self.x[0]) + * math.pow(math.cos(self.x[0]), 2.0) + - + self.gamma + * math.cos(self.x[0]) + * self.g + * self.latest_u + + + (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) + * self.epsilon / self.alpha * math.sin(self.x[0]) + ) + ) + x2_dot = self.x[3] + x3_dot = ((1.0 / ( + self.alpha * self.beta + + math.pow(self.alpha * math.sin(self.x[0]), 2.0) + - math.pow(self.gamma * math.cos(self.x[0]), 2.0) + )) * ( + -self.gamma + * self.alpha + * math.pow(self.x[3], 2.0) + * math.sin(self.x[0]) + * math.pow(math.cos(self.x[1]), 2.0) + - + self.gamma + * self.epsilon + * math.sin(self.x[0]) + * math.cos(self.x[0]) + + + self.gamma + * self.alpha + * math.pow(self.x[1], 2.0) + * math.sin(self.x[0]) + - + 2 + * math.pow(self.alpha, 2.0) + * self.x[1] + * self.x[3] + * math.sin(self.x[0]) + * math.cos(self.x[0]) + + + self.alpha + * self.g + * self.latest_u + ) + ) + sample_period = self.sample_period * 1e-9 + self.x[0] += x0_dot * sample_period + self.x[1] += x1_dot * sample_period + self.x[2] += x2_dot * sample_period + self.x[3] += x3_dot * sample_period + else: + self.x[0] = self.initial_theta + self.first = False + + # Output the state. + theta.set(self.x[0]) + d_theta.set(self.x[1]) + phi.set(self.x[2]) + d_phi.set(self.x[3]) + =} + reaction(u) {= + self.latest_u = u.value + =} } diff --git a/experimental/Python/src/FurutaPendulum/PendulumSimulationRK4.lf b/experimental/Python/src/FurutaPendulum/PendulumSimulationRK4.lf index 6e190bea..890031af 100644 --- a/experimental/Python/src/FurutaPendulum/PendulumSimulationRK4.lf +++ b/experimental/Python/src/FurutaPendulum/PendulumSimulationRK4.lf @@ -36,161 +36,161 @@ target Python; * @author Edward A. Lee, Yiwei Yu */ preamble {= - import math + import math =} reactor PendulumSimulation( - initial_theta(-3.14159), # Initial pendulum angle. - sample_period(5 msec), # Sample period. - g(9.81), # Acceleration of gravity. - alpha(0.00260569), - beta(0.05165675), - gamma(9.7055e-4), - epsilon(0.08103060) + initial_theta(-3.14159), # Initial pendulum angle. + sample_period(5 msec), # Sample period. + g(9.81), # Acceleration of gravity. + alpha(0.00260569), + beta(0.05165675), + gamma(9.7055e-4), + epsilon(0.08103060) ){ - input u # Control input. - input d # Impulsive disturbance - - output theta # Pendulum angle. - output d_theta # Pendulum angular velocity. - output phi # Arm angle. - output d_phi # Arm angular velocity. - - state x(0.0, 0.0, 0.0, 0.0) - state first(True) - state latest_u(0.0) - state elapsed_time(0) + input u # Control input. + input d # Impulsive disturbance + + output theta # Pendulum angle. + output d_theta # Pendulum angular velocity. + output phi # Arm angle. + output d_phi # Arm angular velocity. + + state x(0.0, 0.0, 0.0, 0.0) + state first(True) + state latest_u(0.0) + state elapsed_time(0) - timer t(0, sample_period) + timer t(0, sample_period) - # Based on the dynamic model of furuta pendulum, - # define the differential equations - method calculate_derivatives(x){= - x0_dot = self.x[1] - x1_dot = (1.0/( - self.alpha * self.beta - + math.pow(self.alpha * math.sin(self.x[0]), 2.0) - - math.pow(self.gamma * math.cos(self.x[0]), 2.0) - ) * ( - (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) - * math.pow(self.x[3], 2.0) - * math.sin(self.x[0]) - * math.cos(self.x[0]) - - - math.pow(self.gamma * self.x[1], 2.0) - * math.sin(self.x[0]) - * math.cos(self.x[0]) - + - 2.0 - * self.alpha - * self.gamma\ - * self.x[1] - * self.x[3] - * math.sin(self.x[0]) - * math.pow(math.cos(self.x[0]), 2.0) - - - self.gamma - * math.cos(self.x[0]) - * self.g - * self.latest_u - + - (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) - * self.epsilon / self.alpha * math.sin(self.x[0]) - ) - ) - x2_dot = self.x[3] - x3_dot = ((1.0 / ( - self.alpha * self.beta - + math.pow(self.alpha * math.sin(self.x[0]), 2.0) - - math.pow(self.gamma * math.cos(self.x[0]), 2.0) - )) * ( - -self.gamma - * self.alpha - * math.pow(self.x[3], 2.0) - * math.sin(self.x[0]) - * math.pow(math.cos(self.x[1]), 2.0) - - - self.gamma - * self.epsilon - * math.sin(self.x[0]) - * math.cos(self.x[0]) - + - self.gamma - * self.alpha - * math.pow(self.x[1], 2.0) - * math.sin(self.x[0]) - - - 2 - * math.pow(self.alpha, 2.0) - * self.x[1] - * self.x[3] - * math.sin(self.x[0]) - * math.cos(self.x[0]) - + - self.alpha - * self.g - * self.latest_u - ) - ) - return [x0_dot, x1_dot, x2_dot, x3_dot] - =} + # Based on the dynamic model of furuta pendulum, + # define the differential equations + method calculate_derivatives(x){= + x0_dot = self.x[1] + x1_dot = (1.0/( + self.alpha * self.beta + + math.pow(self.alpha * math.sin(self.x[0]), 2.0) + - math.pow(self.gamma * math.cos(self.x[0]), 2.0) + ) * ( + (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) + * math.pow(self.x[3], 2.0) + * math.sin(self.x[0]) + * math.cos(self.x[0]) + - + math.pow(self.gamma * self.x[1], 2.0) + * math.sin(self.x[0]) + * math.cos(self.x[0]) + + + 2.0 + * self.alpha + * self.gamma\ + * self.x[1] + * self.x[3] + * math.sin(self.x[0]) + * math.pow(math.cos(self.x[0]), 2.0) + - + self.gamma + * math.cos(self.x[0]) + * self.g + * self.latest_u + + + (self.alpha * self.beta + math.pow(self.alpha * math.sin(self.x[0]), 2.0)) + * self.epsilon / self.alpha * math.sin(self.x[0]) + ) + ) + x2_dot = self.x[3] + x3_dot = ((1.0 / ( + self.alpha * self.beta + + math.pow(self.alpha * math.sin(self.x[0]), 2.0) + - math.pow(self.gamma * math.cos(self.x[0]), 2.0) + )) * ( + -self.gamma + * self.alpha + * math.pow(self.x[3], 2.0) + * math.sin(self.x[0]) + * math.pow(math.cos(self.x[1]), 2.0) + - + self.gamma + * self.epsilon + * math.sin(self.x[0]) + * math.cos(self.x[0]) + + + self.gamma + * self.alpha + * math.pow(self.x[1], 2.0) + * math.sin(self.x[0]) + - + 2 + * math.pow(self.alpha, 2.0) + * self.x[1] + * self.x[3] + * math.sin(self.x[0]) + * math.cos(self.x[0]) + + + self.alpha + * self.g + * self.latest_u + ) + ) + return [x0_dot, x1_dot, x2_dot, x3_dot] + =} - # Method used to update the state - method update_state() {= - # The time interval is defined as the time elapsed - # since last time we output the state - time_interval = (lf.time.logical_elapsed() - self.elapsed_time) * 1e-9 - self.elapsed_time = lf.time.logical_elapsed() + # Method used to update the state + method update_state() {= + # The time interval is defined as the time elapsed + # since last time we output the state + time_interval = (lf.time.logical_elapsed() - self.elapsed_time) * 1e-9 + self.elapsed_time = lf.time.logical_elapsed() - # RK4 Operation, return the updated x - def RK4_ODE(x): - h = time_interval - k1 = self.calculate_derivatives(x) - for i in range(len(x)): - x[i] += k1[i] * h/2. - k2 = self.calculate_derivatives(x) - for i in range(len(x)): - x[i] += k2[i] * h/2. - k3 = self.calculate_derivatives(x) - for i in range(len(x)): - x[i] += k3[i] * h - k4 = self.calculate_derivatives(x) - for i in range(len(x)): - x[i] += (h/6.)*(k1[i] + 2*k2[i] + 2*k3[i] + k4[i]) - return x + # RK4 Operation, return the updated x + def RK4_ODE(x): + h = time_interval + k1 = self.calculate_derivatives(x) + for i in range(len(x)): + x[i] += k1[i] * h/2. + k2 = self.calculate_derivatives(x) + for i in range(len(x)): + x[i] += k2[i] * h/2. + k3 = self.calculate_derivatives(x) + for i in range(len(x)): + x[i] += k3[i] * h + k4 = self.calculate_derivatives(x) + for i in range(len(x)): + x[i] += (h/6.)*(k1[i] + 2*k2[i] + 2*k3[i] + k4[i]) + return x - # Update x using RK4 - self.x = RK4_ODE(self.x) - =} + # Update x using RK4 + self.x = RK4_ODE(self.x) + =} - # Method used to output the state - method output_state(theta, d_theta, phi, d_phi) {= - theta.set(self.x[0]) - d_theta.set(self.x[1]) - phi.set(self.x[2]) - d_phi.set(self.x[3]) - =} - - # The update will be triggered when either a disturbance is introduced or a sample - # period of time has elapsed. - # See SmallTest.lf for how disturbance updates works in a simpler forward euler - # example - reaction(d) {= - self.update_state() - self.x[1] += d.value - =} + # Method used to output the state + method output_state(theta, d_theta, phi, d_phi) {= + theta.set(self.x[0]) + d_theta.set(self.x[1]) + phi.set(self.x[2]) + d_phi.set(self.x[3]) + =} + + # The update will be triggered when either a disturbance is introduced or a sample + # period of time has elapsed. + # See SmallTest.lf for how disturbance updates works in a simpler forward euler + # example + reaction(d) {= + self.update_state() + self.x[1] += d.value + =} - reaction(t) -> theta, d_theta, phi, d_phi {= - if not (self.first): - self.update_state() - else: - self.x[0] = self.initial_theta - self.first = False - # Output the state. - self.output_state(theta, d_theta, phi, d_phi) - =} + reaction(t) -> theta, d_theta, phi, d_phi {= + if not (self.first): + self.update_state() + else: + self.x[0] = self.initial_theta + self.first = False + # Output the state. + self.output_state(theta, d_theta, phi, d_phi) + =} - reaction(u) {= - self.latest_u = u.value - =} - + reaction(u) {= + self.latest_u = u.value + =} + } diff --git a/experimental/Python/src/FurutaPendulum/SmallTest.lf b/experimental/Python/src/FurutaPendulum/SmallTest.lf index d25a074b..5c5a8bd6 100644 --- a/experimental/Python/src/FurutaPendulum/SmallTest.lf +++ b/experimental/Python/src/FurutaPendulum/SmallTest.lf @@ -13,105 +13,105 @@ * simulation. */ target Python { - timeout: 8 secs, - fast: true + timeout: 8 secs, + fast: true } preamble {= - import math - import matplotlib.pyplot as plt + import math + import matplotlib.pyplot as plt =} reactor Simulation( - sample_period(1 secs), # Sample period. - g(9.81) # Acceleration of gravity. + sample_period(1 secs), # Sample period. + g(9.81) # Acceleration of gravity. ){ - input u # Control input. - input d # Impulsive disturbance - output result # Output value for y axis - output d_result # Rate of change of y - - state x(0.0, 1.0) - state first(False) - state latest_u(0.0) - state elapsed_time(0) + input u # Control input. + input d # Impulsive disturbance + output result # Output value for y axis + output d_result # Rate of change of y + + state x(0.0, 1.0) + state first(False) + state latest_u(0.0) + state elapsed_time(0) - timer t(0, sample_period) + timer t(0, sample_period) - # Method used to update the state - method update_state() {= - # The time interval is defined as the time elapsed - # since last time we output the state - time_interval = (lf.time.logical_elapsed() - self.elapsed_time)/1e9 - self.elapsed_time = lf.time.logical_elapsed() + # Method used to update the state + method update_state() {= + # The time interval is defined as the time elapsed + # since last time we output the state + time_interval = (lf.time.logical_elapsed() - self.elapsed_time)/1e9 + self.elapsed_time = lf.time.logical_elapsed() - # Perform Simple Euler, return the updated x - def Simple_Euler(x): - h = time_interval - x[0] = x[0] + h*x[1] - return x + # Perform Simple Euler, return the updated x + def Simple_Euler(x): + h = time_interval + x[0] = x[0] + h*x[1] + return x - # Update x using RK4 - self.x = Simple_Euler(self.x) - =} + # Update x using RK4 + self.x = Simple_Euler(self.x) + =} - # Method used to output the state - method output_state(result, d_result) {= - result.set(self.x[0]) - d_result.set(self.x[1]) - =} - - # The update will be triggered when either a disturbance is introduced or a sample - # period of time has elapsed. - reaction(d) {= - self.update_state() - self.x[1] += d.value - =} + # Method used to output the state + method output_state(result, d_result) {= + result.set(self.x[0]) + d_result.set(self.x[1]) + =} + + # The update will be triggered when either a disturbance is introduced or a sample + # period of time has elapsed. + reaction(d) {= + self.update_state() + self.x[1] += d.value + =} - reaction(t) -> result, d_result {= - self.update_state() - # Output the state. - self.output_state(result, d_result) - =} - + reaction(t) -> result, d_result {= + self.update_state() + # Output the state. + self.output_state(result, d_result) + =} + } reactor Plotter { - input data - state times({=[]=}) - state values({=[]=}) - reaction(data) {= - self.times.append(lf.time.logical_elapsed()/1e9) - self.values.append(data.value) - =} - reaction(shutdown) {= - fig, axs = plt.subplots() - axs.set_title("Small Test Graph") - axs.plot(self.times, self.values, color="C0") - # Show a stem graph pointing to the x-axis - axs.stem(self.times, self.values) - # Label x,y coordinates of each point - for x,y in zip(self.times,self.values): - label = f"({x},{y})" - plt.annotate(label, # this is the text - (x,y), # these are the coordinates to position the label - textcoords="offset points", # how to position the text - xytext=(0,10), # distance from text to points (x,y) - ha="center") # horizontal alignment can be left, right or center - axs.set_xlabel("Time (secs)") - axs.set_ylabel("Result") - plt.show() - =} + input data + state times({=[]=}) + state values({=[]=}) + reaction(data) {= + self.times.append(lf.time.logical_elapsed()/1e9) + self.values.append(data.value) + =} + reaction(shutdown) {= + fig, axs = plt.subplots() + axs.set_title("Small Test Graph") + axs.plot(self.times, self.values, color="C0") + # Show a stem graph pointing to the x-axis + axs.stem(self.times, self.values) + # Label x,y coordinates of each point + for x,y in zip(self.times,self.values): + label = f"({x},{y})" + plt.annotate(label, # this is the text + (x,y), # these are the coordinates to position the label + textcoords="offset points", # how to position the text + xytext=(0,10), # distance from text to points (x,y) + ha="center") # horizontal alignment can be left, right or center + axs.set_xlabel("Time (secs)") + axs.set_ylabel("Result") + plt.show() + =} } main reactor { - timer d(3500 msec); // One-shot disturbance at 3 seconds. + timer d(3500 msec); // One-shot disturbance at 3 seconds. - p = new Simulation() - plt = new Plotter() - p.result -> plt.data + p = new Simulation() + plt = new Plotter() + p.result -> plt.data - reaction(d) -> p.d {= - p.d.set(1) - =} + reaction(d) -> p.d {= + p.d.set(1) + =} } diff --git a/experimental/Python/src/GeneralModels/bankedsimius.lf b/experimental/Python/src/GeneralModels/bankedsimius.lf index 578c9b11..48d6bf6e 100644 --- a/experimental/Python/src/GeneralModels/bankedsimius.lf +++ b/experimental/Python/src/GeneralModels/bankedsimius.lf @@ -3,86 +3,86 @@ target Python {}; preamble {= - - specs = [ - { - - }, - { - - }, - { - - } - ] + + specs = [ + { + + }, + { + + }, + { + + } + ] =} ### Sensory reactor Sensory { - - state _reading - - output out0 - - timer t(0, 100msec) - - reaction(startup) {= - - =} - - reaction(t) -> out0 {= - - out0.set() - =} + + state _reading + + output out0 + + timer t(0, 100msec) + + reaction(startup) {= + + =} + + reaction(t) -> out0 {= + + out0.set() + =} } ### Brain reactor Brain { - - state _active - - input in0 - input in1 - input in2 - - output out0 - output out1 - output out2 - - reaction(startup) {= - - =} - - reaction(in0, in1, in2) -> out0, out1, out2 {= - - out0.set() - out1.set() - out2.set() - =} + + state _active + + input in0 + input in1 + input in2 + + output out0 + output out1 + output out2 + + reaction(startup) {= + + =} + + reaction(in0, in1, in2) -> out0, out1, out2 {= + + out0.set() + out1.set() + out2.set() + =} } ### Actuators reactor Movers { - - state _position - - input in0 - - reaction(startup) {= - - =} - - reaction(in0) {= - - =} + + state _position + + input in0 + + reaction(startup) {= + + =} + + reaction(in0) {= + + =} } main reactor { - - nerves = new[3] Sensory() - brain = new Brain() - movers = new[3] Movers() - - nerves.out0 -> brain.in0, brain.in1, brain.in2 - brain.out0, brain.out1, brain.out2 -> movers.in0 -} \ No newline at end of file + + nerves = new[3] Sensory() + brain = new Brain() + movers = new[3] Movers() + + nerves.out0 -> brain.in0, brain.in1, brain.in2 + brain.out0, brain.out1, brain.out2 -> movers.in0 +} diff --git a/experimental/Python/src/GeneralModels/headedswarm.lf b/experimental/Python/src/GeneralModels/headedswarm.lf index 34f84de4..0a5e054d 100644 --- a/experimental/Python/src/GeneralModels/headedswarm.lf +++ b/experimental/Python/src/GeneralModels/headedswarm.lf @@ -5,100 +5,100 @@ target Python{}; preamble {= - + =} reactor Observer { - timer tick(0, 100msec) - - input in0 - input in1 - input in2 - input in3 - input in4 - + timer tick(0, 100msec) + + input in0 + input in1 + input in2 + input in3 + input in4 + + + state _node_zero + state _node_one + state _node_two + state _node_three + state _node_four + + output nodes + + reaction(tick) -> nodes {= - state _node_zero - state _node_one - state _node_two - state _node_three - state _node_four + =} + + reaction(in0, in1, in2, in3, in4) {= - output nodes - - reaction(tick) -> nodes {= - - =} - - reaction(in0, in1, in2, in3, in4) {= - - =} + =} } ### Unit reactor Node(speed(100msec), index(0)) { + + state _node_index + state _node_zero + state _node_one + state _node_two + state _node_three + + input in0 + input in1 + input in2 + input in3 + input in4 + + output out0 + output out1 + output out2 + output out3 + output head + +// initial mode One { +// reaction(in4) {= +// Two.set() +// =} +// } mode Two { + timer tick(0, speed) - state _node_index - state _node_zero - state _node_one - state _node_two - state _node_three - - input in0 - input in1 - input in2 - input in3 - input in4 + reaction(tick) -> out0, out1, out2, out3 {= + + =} - output out0 - output out1 - output out2 - output out3 - output head - -// initial mode One { -// reaction(in4) {= -// Two.set() -// =} -// } mode Two { - timer tick(0, speed) - - reaction(tick) -> out0, out1, out2, out3 {= - - =} - - reaction(in0, in1, in2, in3){= - - =} -// } + reaction(in0, in1, in2, in3){= + + =} +// } } main reactor { - node0 = new Node() - node1 = new Node(index = 1) - node2 = new Node(index = 2) - node3 = new Node(index = 3) - node4 = new Node(index = 4) + node0 = new Node() + node1 = new Node(index = 1) + node2 = new Node(index = 2) + node3 = new Node(index = 3) + node4 = new Node(index = 4) + + head = new Observer() + + (head.nodes)+ -> node0.in4, node1.in4, node2.in4, node3.in4, node4.in4 + + node0.head, node1.head, node2.head, node3.head, node4.head -> + head.in0, head.in1, head.in2, head.in3, head.in4 + + node0.out0, node0.out1, node0.out2, node0.out3 -> + node1.in0, node2.in0, node3.in0, node4.in0 - head = new Observer() + node1.out0, node1.out1, node1.out2, node1.out3 -> + node0.in0, node2.in1, node3.in1, node4.in1 + + node2.out0, node2.out1, node2.out2, node2.out3 -> + node0.in1, node1.in1, node3.in2, node4.in2 - (head.nodes)+ -> node0.in4, node1.in4, node2.in4, node3.in4, node4.in4 + node3.out0, node3.out1, node3.out2, node3.out3 -> + node0.in2, node1.in2, node2.in2, node4.in3 - node0.head, node1.head, node2.head, node3.head, node4.head -> - head.in0, head.in1, head.in2, head.in3, head.in4 - - node0.out0, node0.out1, node0.out2, node0.out3 -> - node1.in0, node2.in0, node3.in0, node4.in0 - - node1.out0, node1.out1, node1.out2, node1.out3 -> - node0.in0, node2.in1, node3.in1, node4.in1 - - node2.out0, node2.out1, node2.out2, node2.out3 -> - node0.in1, node1.in1, node3.in2, node4.in2 - - node3.out0, node3.out1, node3.out2, node3.out3 -> - node0.in2, node1.in2, node2.in2, node4.in3 - - node4.out0, node4.out1, node4.out2, node4.out3 -> - node0.in3, node1.in3, node2.in3, node3.in3 -} \ No newline at end of file + node4.out0, node4.out1, node4.out2, node4.out3 -> + node0.in3, node1.in3, node2.in3, node3.in3 +} diff --git a/experimental/Python/src/GeneralModels/queenant.lf b/experimental/Python/src/GeneralModels/queenant.lf index 7d81fcf7..d8bdac69 100644 --- a/experimental/Python/src/GeneralModels/queenant.lf +++ b/experimental/Python/src/GeneralModels/queenant.lf @@ -6,73 +6,73 @@ preamble {==} ### Ant reactor Ant { - timer t(0, 100msec) - state _standing(0) - - input in0 - - output out0 - - reaction(startup) -> out0 {= - if self._standing == 0: - out0.set(self._standing) - self._standing += 1 - =} - - reaction(t) -> out0 {= - out0.set("Replace") - =} - - reaction(in0) {= - if in0.is_present: - print("Input 0 is present.") - =} + timer t(0, 100msec) + state _standing(0) + + input in0 + + output out0 + + reaction(startup) -> out0 {= + if self._standing == 0: + out0.set(self._standing) + self._standing += 1 + =} + + reaction(t) -> out0 {= + out0.set("Replace") + =} + + reaction(in0) {= + if in0.is_present: + print("Input 0 is present.") + =} } reactor QueenAnt { - - state _holding - - input in0 - input in1 - input in2 - input in3 - input in4 - - output out0 - output out1 - output out2 - output out3 - output out4 - - reaction(in0, in1, in2, in3, in4) -> out0, out1, out2, out3, out4 {= - if in0.is_present: - print("Input 0 is present") - if in1.is_present: - print("Input 1 is present") - if in2.is_present: - print("Input 2 is present") - if in3.is_present: - print("Input 3 is present") - if in4.is_present: - print("Input 4 is present") - out0.set("Replace") - out1.set("Replace") - out2.set("Replace") - out3.set("Replace") - out4.set("Replace") - =} + + state _holding + + input in0 + input in1 + input in2 + input in3 + input in4 + + output out0 + output out1 + output out2 + output out3 + output out4 + + reaction(in0, in1, in2, in3, in4) -> out0, out1, out2, out3, out4 {= + if in0.is_present: + print("Input 0 is present") + if in1.is_present: + print("Input 1 is present") + if in2.is_present: + print("Input 2 is present") + if in3.is_present: + print("Input 3 is present") + if in4.is_present: + print("Input 4 is present") + out0.set("Replace") + out1.set("Replace") + out2.set("Replace") + out3.set("Replace") + out4.set("Replace") + =} } main reactor { - - ants = new[5] Ant() - queen = new QueenAnt() - - queen.out0, queen.out1, queen.out2, queen.out3, queen.out4 -> - ants.in0 - - ants.out0 -> queen.in0, queen.in1, queen.in2, queen.in3, queen.in4 + + ants = new[5] Ant() + queen = new QueenAnt() + + queen.out0, queen.out1, queen.out2, queen.out3, queen.out4 -> + ants.in0 + + ants.out0 -> queen.in0, queen.in1, queen.in2, queen.in3, queen.in4 } diff --git a/experimental/Python/src/GeneralModels/simius.lf b/experimental/Python/src/GeneralModels/simius.lf index 8fa623c7..3df899bc 100644 --- a/experimental/Python/src/GeneralModels/simius.lf +++ b/experimental/Python/src/GeneralModels/simius.lf @@ -3,106 +3,106 @@ */ target Python { - + }; preamble {= - + =} ### Sensory reactor Sense { - - state _touch - state _vision - state _hearing - state _position - - output collision - output view - output sound - output placement - - timer t(0, 100msec) - - reaction(t) -> collision, view, sound, placement {= - collision.set(self._touch) - view.set(self._vision) - sound.set(self._sound) - placement.set(self._position) - =} + + state _touch + state _vision + state _hearing + state _position + + output collision + output view + output sound + output placement + + timer t(0, 100msec) + + reaction(t) -> collision, view, sound, placement {= + collision.set(self._touch) + view.set(self._vision) + sound.set(self._sound) + placement.set(self._position) + =} } ### Decision Maker reactor Brain { - - input collision - input view - input sound - input placement - - output legs - output torso - output arms - - reaction(collision, view, sound, placement) -> legs, torso, arms {= - if collision.is_present: - print("collision") - if view.is_present: - print("view") - if sound.is_present: - print("sound") - if placement.is_present: - print("placement") - - var leg_change - var torso_change - var arms_change - legs.set(leg_change) - torso.set(torso_change) - arms.set(arms_change) - =} + + input collision + input view + input sound + input placement + + output legs + output torso + output arms + + reaction(collision, view, sound, placement) -> legs, torso, arms {= + if collision.is_present: + print("collision") + if view.is_present: + print("view") + if sound.is_present: + print("sound") + if placement.is_present: + print("placement") + + var leg_change + var torso_change + var arms_change + legs.set(leg_change) + torso.set(torso_change) + arms.set(arms_change) + =} } ### Body reactor Body { - - state _energy - - input legs - input torso - input arms - - reaction(startup) {= - - =} - - reaction(legs) {= - - =} - - reaction(torso) {= - - =} - - reaction(arms) {= - - =} - + + state _energy + + input legs + input torso + input arms + + reaction(startup) {= + + =} + + reaction(legs) {= + + =} + + reaction(torso) {= + + =} + + reaction(arms) {= + + =} + } main reactor { - - sensory = new Sense() - brainy = new Brain() - corpo = new Body() - - sensory.collision, sensory.view, - sensory.sound, sensory.placement -> - brainy.collision, brainy.view, - brainy.sound, brainy.placement - - brainy.legs, brainy.torso, brainy.arms -> - corpo.legs, corpo.torso, corpo.arms + + sensory = new Sense() + brainy = new Brain() + corpo = new Body() + + sensory.collision, sensory.view, + sensory.sound, sensory.placement -> + brainy.collision, brainy.view, + brainy.sound, brainy.placement + + brainy.legs, brainy.torso, brainy.arms -> + corpo.legs, corpo.torso, corpo.arms } diff --git a/experimental/Python/src/GeneralModels/swarm.lf b/experimental/Python/src/GeneralModels/swarm.lf index a646d214..5090b6ec 100644 --- a/experimental/Python/src/GeneralModels/swarm.lf +++ b/experimental/Python/src/GeneralModels/swarm.lf @@ -3,62 +3,62 @@ target Python{}; preamble {= - + =} ### Unit reactor Node(speed(100msec), index(0)) { + + state _head_index(0) + state _node_index + + input in0 + input in1 + input in2 + input in3 + input in4 + + output out0 + output out1 + output out2 + output out3 + +// initial mode One { +// reaction(in4) {= +// Two.set() +// =} +// } mode Two { + timer tick(0, speed) - state _head_index(0) - state _node_index - - input in0 - input in1 - input in2 - input in3 - input in4 - - output out0 - output out1 - output out2 - output out3 + reaction(tick) -> out0, out1, out2, out3 {= + + =} -// initial mode One { -// reaction(in4) {= -// Two.set() -// =} -// } mode Two { - timer tick(0, speed) - - reaction(tick) -> out0, out1, out2, out3 {= - - =} - - reaction(in0, in1, in2, in3){= - - =} -// } + reaction(in0, in1, in2, in3){= + + =} +// } } main reactor { - node0 = new Node() - node1 = new Node(index = 1) - node2 = new Node(index = 2) - node3 = new Node(index = 3) - node4 = new Node(index = 4) + node0 = new Node() + node1 = new Node(index = 1) + node2 = new Node(index = 2) + node3 = new Node(index = 3) + node4 = new Node(index = 4) - node0.out0, node0.out1, node0.out2, node0.out3 -> - node1.in0, node2.in0, node3.in0, node4.in0 - - node1.out0, node1.out1, node1.out2, node1.out3 -> - node0.in0, node2.in1, node3.in1, node4.in1 + node0.out0, node0.out1, node0.out2, node0.out3 -> + node1.in0, node2.in0, node3.in0, node4.in0 - node2.out0, node2.out1, node2.out2, node2.out3 -> - node0.in1, node1.in1, node3.in2, node4.in2 - - node3.out0, node3.out1, node3.out2, node3.out3 -> - node0.in2, node1.in2, node2.in2, node4.in3 - - node4.out0, node4.out1, node4.out2, node4.out3 -> - node0.in3, node1.in3, node2.in3, node3.in3 -} \ No newline at end of file + node1.out0, node1.out1, node1.out2, node1.out3 -> + node0.in0, node2.in1, node3.in1, node4.in1 + + node2.out0, node2.out1, node2.out2, node2.out3 -> + node0.in1, node1.in1, node3.in2, node4.in2 + + node3.out0, node3.out1, node3.out2, node3.out3 -> + node0.in2, node1.in2, node2.in2, node4.in3 + + node4.out0, node4.out1, node4.out2, node4.out3 -> + node0.in3, node1.in3, node2.in3, node3.in3 +} diff --git a/experimental/Python/src/GeneralModels/tester.lf b/experimental/Python/src/GeneralModels/tester.lf index 76d070ee..70a50364 100644 --- a/experimental/Python/src/GeneralModels/tester.lf +++ b/experimental/Python/src/GeneralModels/tester.lf @@ -7,211 +7,211 @@ preamble {==} ### Input Setup Reactor reactor Setup { - state _function - - input results - - output out0 - output out1 - output out2 - output out3 - output out4 - output out5 - output out6 - output out7 - output out8 - output out9 - output out10 - output out11 - output out12 - output out13 - output out14 - output out15 - output intended - reaction(startup) {==} + state _function + + input results + + output out0 + output out1 + output out2 + output out3 + output out4 + output out5 + output out6 + output out7 + output out8 + output out9 + output out10 + output out11 + output out12 + output out13 + output out14 + output out15 + output intended + reaction(startup) {==} } ### Input Layer reactor Input { - state _reading - state _weight - state _bias - - input data - input back_in0 - input back_in1 - input back_in2 - input back_in3 - input back_in4 - input back_in5 - input back_in6 - output out0 - output out1 - output out2 - output out3 - output out4 - output out5 - output out6 + state _reading + state _weight + state _bias + + input data + input back_in0 + input back_in1 + input back_in2 + input back_in3 + input back_in4 + input back_in5 + input back_in6 + output out0 + output out1 + output out2 + output out3 + output out4 + output out5 + output out6 + + initial mode forward_prop { - initial mode forward_prop { - - reaction(startup) {==} + reaction(startup) {==} - reaction(data) -> out0, out1, out2, out3, out4, out5, out6 {==} + reaction(data) -> out0, out1, out2, out3, out4, out5, out6 {==} - } mode back_prop { + } mode back_prop { - reaction(back_in0, back_in1, back_in2, back_in3, back_in4, back_in5, back_in6) {==} + reaction(back_in0, back_in1, back_in2, back_in3, back_in4, back_in5, back_in6) {==} - } + } } ### Hidden Layer 0 reactor HL0 { - state _reading - state _weight - state _bias - - input in0 - output back_out0 - input in1 - output back_out1 - input in2 - output back_out2 - input in3 - output back_out3 - input in4 - output back_out4 - input in5 - output back_out5 - input in6 - output back_out6 - input in7 - output back_out7 - input in8 - output back_out8 - input in9 - output back_out9 - input in10 - output back_out10 - input in11 - output back_out11 - input in12 - output back_out12 - input in13 - output back_out13 - input in14 - output back_out14 - input in15 - output back_out15 - output out0 - input back_in0 - output out1 - input back_in1 - output out2 - input back_in2 - output out3 - input back_in3 - output out4 - input back_in4 - output out5 - input back_in5 - output out6 - input back_in6 - - initial mode forward_prop { - - reaction(startup) {==} - - reaction(in0, in1, in2, in3, in4, in5, in6, in7, in8, in9, in10, in11, in12, in13, in14, in15) -> out0, out1, out2, out3, out4, out5, out6 {==} - - } mode back_prop { - - reaction(back_in0, back_in1, back_in2, back_in3, back_in4, back_in5, back_in6) -> back_out0, back_out1, back_out2, back_out3, back_out4, back_out5, back_out6, back_out7, back_out8, back_out9, back_out10, back_out11, back_out12, back_out13, back_out14, back_out15 {==} - - } + state _reading + state _weight + state _bias + + input in0 + output back_out0 + input in1 + output back_out1 + input in2 + output back_out2 + input in3 + output back_out3 + input in4 + output back_out4 + input in5 + output back_out5 + input in6 + output back_out6 + input in7 + output back_out7 + input in8 + output back_out8 + input in9 + output back_out9 + input in10 + output back_out10 + input in11 + output back_out11 + input in12 + output back_out12 + input in13 + output back_out13 + input in14 + output back_out14 + input in15 + output back_out15 + output out0 + input back_in0 + output out1 + input back_in1 + output out2 + input back_in2 + output out3 + input back_in3 + output out4 + input back_in4 + output out5 + input back_in5 + output out6 + input back_in6 + + initial mode forward_prop { + + reaction(startup) {==} + + reaction(in0, in1, in2, in3, in4, in5, in6, in7, in8, in9, in10, in11, in12, in13, in14, in15) -> out0, out1, out2, out3, out4, out5, out6 {==} + + } mode back_prop { + + reaction(back_in0, back_in1, back_in2, back_in3, back_in4, back_in5, back_in6) -> back_out0, back_out1, back_out2, back_out3, back_out4, back_out5, back_out6, back_out7, back_out8, back_out9, back_out10, back_out11, back_out12, back_out13, back_out14, back_out15 {==} + + } } ### Hidden Layer 1 reactor HL1 { - state _reading - state _weight - state _bias - - input in0 - output back_out0 - input in1 - output back_out1 - input in2 - output back_out2 - input in3 - output back_out3 - input in4 - output back_out4 - input in5 - output back_out5 - input in6 - output back_out6 - output out0 - input back_in0 - output out1 - input back_in1 - output out2 - input back_in2 - output out3 - input back_in3 - - initial mode forward_prop { - - reaction(startup) {==} - - reaction(in0, in1, in2, in3, in4, in5, in6) -> out0, out1, out2, out3 {==} - - } mode back_prop { - - reaction(back_in0, back_in1, back_in2, back_in3) -> back_out0, back_out1, back_out2, back_out3, back_out4, back_out5, back_out6 {==} - - } + state _reading + state _weight + state _bias + + input in0 + output back_out0 + input in1 + output back_out1 + input in2 + output back_out2 + input in3 + output back_out3 + input in4 + output back_out4 + input in5 + output back_out5 + input in6 + output back_out6 + output out0 + input back_in0 + output out1 + input back_in1 + output out2 + input back_in2 + output out3 + input back_in3 + + initial mode forward_prop { + + reaction(startup) {==} + + reaction(in0, in1, in2, in3, in4, in5, in6) -> out0, out1, out2, out3 {==} + + } mode back_prop { + + reaction(back_in0, back_in1, back_in2, back_in3) -> back_out0, back_out1, back_out2, back_out3, back_out4, back_out5, back_out6 {==} + + } } ### Output Layer reactor Output { - state _reading - state _weight - state _bias + state _reading + state _weight + state _bias - input in0 - input in1 - input in2 - input in3 - input in4 - input in5 - input in6 - input back_in + input in0 + input in1 + input in2 + input in3 + input in4 + input in5 + input in6 + input back_in - output result - output back_out0 - output back_out1 - output back_out2 - output back_out3 - output back_out4 - output back_out5 - output back_out6 + output result + output back_out0 + output back_out1 + output back_out2 + output back_out3 + output back_out4 + output back_out5 + output back_out6 - initial mode forward_prop { + initial mode forward_prop { - reaction(in0, in1, in2, in3, in4, in5, in6) -> result {==} + reaction(in0, in1, in2, in3, in4, in5, in6) -> result {==} - } mode back_prop { + } mode back_prop { - reaction(back_in) -> back_out0, back_out1, back_out2, back_out3, back_out4, back_out5, back_out6 {==} + reaction(back_in) -> back_out0, back_out1, back_out2, back_out3, back_out4, back_out5, back_out6 {==} - } + } } ### Reactor Processor @@ -231,142 +231,142 @@ output back_out2 output back_out3 output resultToSystem - initial mode forward_prop { + initial mode forward_prop { - reaction(in0, in1, in2, in3) -> resultToSystem {==} + reaction(in0, in1, in2, in3) -> resultToSystem {==} - } mode back_prop { + } mode back_prop { - reaction(startup) {==} + reaction(startup) {==} - } + } } ### Main Reactor main reactor { - setup = new Setup() - - input0= new Input() - input1= new Input() - input2= new Input() - input3= new Input() - input4= new Input() - input5= new Input() - input6= new Input() - input7= new Input() - input8= new Input() - input9= new Input() - input10= new Input() - input11= new Input() - input12= new Input() - input13= new Input() - input14= new Input() - input15= new Input() - hl0_0 = new HL0() - hl0_1 = new HL0() - hl0_2 = new HL0() - hl0_3 = new HL0() - hl0_4 = new HL0() - hl0_5 = new HL0() - hl0_6 = new HL0() - hl1_0 = new HL1() - hl1_1 = new HL1() - hl1_2 = new HL1() - hl1_3 = new HL1() - hl1_4 = new HL1() - hl1_5 = new HL1() - hl1_6 = new HL1() - output0 = new Output() - - output1 = new Output() - - output2 = new Output() - - output3 = new Output() - - processor = new Processor() - - setup.out0 -> input0.data - setup.out1 -> input1.data - setup.out2 -> input2.data - setup.out3 -> input3.data - setup.out4 -> input4.data - setup.out5 -> input5.data - setup.out6 -> input6.data - setup.out7 -> input7.data - setup.out8 -> input8.data - setup.out9 -> input9.data - setup.out10 -> input10.data - setup.out11 -> input11.data - setup.out12 -> input12.data - setup.out13 -> input13.data - setup.out14 -> input14.data - setup.out15 -> input15.data - input0.out0, input1.out0, input2.out0, input3.out0, input4.out0, input5.out0, input6.out0, input7.out0, input8.out0, input9.out0, input10.out0, input11.out0, input12.out0, input13.out0, input14.out0, input15.out0 -> hl0_0.in0, hl0_0.in1, hl0_0.in2, hl0_0.in3, hl0_0.in4, hl0_0.in5, hl0_0.in6, hl0_0.in7, hl0_0.in8, hl0_0.in9, hl0_0.in10, hl0_0.in11, hl0_0.in12, hl0_0.in13, hl0_0.in14, hl0_0.in15 - input0.out1, input1.out1, input2.out1, input3.out1, input4.out1, input5.out1, input6.out1, input7.out1, input8.out1, input9.out1, input10.out1, input11.out1, input12.out1, input13.out1, input14.out1, input15.out1 -> hl0_1.in0, hl0_1.in1, hl0_1.in2, hl0_1.in3, hl0_1.in4, hl0_1.in5, hl0_1.in6, hl0_1.in7, hl0_1.in8, hl0_1.in9, hl0_1.in10, hl0_1.in11, hl0_1.in12, hl0_1.in13, hl0_1.in14, hl0_1.in15 - input0.out2, input1.out2, input2.out2, input3.out2, input4.out2, input5.out2, input6.out2, input7.out2, input8.out2, input9.out2, input10.out2, input11.out2, input12.out2, input13.out2, input14.out2, input15.out2 -> hl0_2.in0, hl0_2.in1, hl0_2.in2, hl0_2.in3, hl0_2.in4, hl0_2.in5, hl0_2.in6, hl0_2.in7, hl0_2.in8, hl0_2.in9, hl0_2.in10, hl0_2.in11, hl0_2.in12, hl0_2.in13, hl0_2.in14, hl0_2.in15 - input0.out3, input1.out3, input2.out3, input3.out3, input4.out3, input5.out3, input6.out3, input7.out3, input8.out3, input9.out3, input10.out3, input11.out3, input12.out3, input13.out3, input14.out3, input15.out3 -> hl0_3.in0, hl0_3.in1, hl0_3.in2, hl0_3.in3, hl0_3.in4, hl0_3.in5, hl0_3.in6, hl0_3.in7, hl0_3.in8, hl0_3.in9, hl0_3.in10, hl0_3.in11, hl0_3.in12, hl0_3.in13, hl0_3.in14, hl0_3.in15 - input0.out4, input1.out4, input2.out4, input3.out4, input4.out4, input5.out4, input6.out4, input7.out4, input8.out4, input9.out4, input10.out4, input11.out4, input12.out4, input13.out4, input14.out4, input15.out4 -> hl0_4.in0, hl0_4.in1, hl0_4.in2, hl0_4.in3, hl0_4.in4, hl0_4.in5, hl0_4.in6, hl0_4.in7, hl0_4.in8, hl0_4.in9, hl0_4.in10, hl0_4.in11, hl0_4.in12, hl0_4.in13, hl0_4.in14, hl0_4.in15 - input0.out5, input1.out5, input2.out5, input3.out5, input4.out5, input5.out5, input6.out5, input7.out5, input8.out5, input9.out5, input10.out5, input11.out5, input12.out5, input13.out5, input14.out5, input15.out5 -> hl0_5.in0, hl0_5.in1, hl0_5.in2, hl0_5.in3, hl0_5.in4, hl0_5.in5, hl0_5.in6, hl0_5.in7, hl0_5.in8, hl0_5.in9, hl0_5.in10, hl0_5.in11, hl0_5.in12, hl0_5.in13, hl0_5.in14, hl0_5.in15 - input0.out6, input1.out6, input2.out6, input3.out6, input4.out6, input5.out6, input6.out6, input7.out6, input8.out6, input9.out6, input10.out6, input11.out6, input12.out6, input13.out6, input14.out6, input15.out6 -> hl0_6.in0, hl0_6.in1, hl0_6.in2, hl0_6.in3, hl0_6.in4, hl0_6.in5, hl0_6.in6, hl0_6.in7, hl0_6.in8, hl0_6.in9, hl0_6.in10, hl0_6.in11, hl0_6.in12, hl0_6.in13, hl0_6.in14, hl0_6.in15 - hl0_0.out0, hl0_1.out0, hl0_2.out0, hl0_3.out0, hl0_4.out0, hl0_5.out0, hl0_6.out0 -> hl1_0.in0, hl1_0.in1, hl1_0.in2, hl1_0.in3, hl1_0.in4, hl1_0.in5, hl1_0.in6 - hl0_0.out1, hl0_1.out1, hl0_2.out1, hl0_3.out1, hl0_4.out1, hl0_5.out1, hl0_6.out1 -> hl1_1.in0, hl1_1.in1, hl1_1.in2, hl1_1.in3, hl1_1.in4, hl1_1.in5, hl1_1.in6 - hl0_0.out2, hl0_1.out2, hl0_2.out2, hl0_3.out2, hl0_4.out2, hl0_5.out2, hl0_6.out2 -> hl1_2.in0, hl1_2.in1, hl1_2.in2, hl1_2.in3, hl1_2.in4, hl1_2.in5, hl1_2.in6 - hl0_0.out3, hl0_1.out3, hl0_2.out3, hl0_3.out3, hl0_4.out3, hl0_5.out3, hl0_6.out3 -> hl1_3.in0, hl1_3.in1, hl1_3.in2, hl1_3.in3, hl1_3.in4, hl1_3.in5, hl1_3.in6 - hl0_0.out4, hl0_1.out4, hl0_2.out4, hl0_3.out4, hl0_4.out4, hl0_5.out4, hl0_6.out4 -> hl1_4.in0, hl1_4.in1, hl1_4.in2, hl1_4.in3, hl1_4.in4, hl1_4.in5, hl1_4.in6 - hl0_0.out5, hl0_1.out5, hl0_2.out5, hl0_3.out5, hl0_4.out5, hl0_5.out5, hl0_6.out5 -> hl1_5.in0, hl1_5.in1, hl1_5.in2, hl1_5.in3, hl1_5.in4, hl1_5.in5, hl1_5.in6 - hl0_0.out6, hl0_1.out6, hl0_2.out6, hl0_3.out6, hl0_4.out6, hl0_5.out6, hl0_6.out6 -> hl1_6.in0, hl1_6.in1, hl1_6.in2, hl1_6.in3, hl1_6.in4, hl1_6.in5, hl1_6.in6 - hl1_0.out0, hl1_1.out0, hl1_2.out0, hl1_3.out0, hl1_4.out0, hl1_5.out0, hl1_6.out0 -> output0.in0, output0.in1, output0.in2, output0.in3, output0.in4, output0.in5, output0.in6 - hl1_0.out1, hl1_1.out1, hl1_2.out1, hl1_3.out1, hl1_4.out1, hl1_5.out1, hl1_6.out1 -> output1.in0, output1.in1, output1.in2, output1.in3, output1.in4, output1.in5, output1.in6 - hl1_0.out2, hl1_1.out2, hl1_2.out2, hl1_3.out2, hl1_4.out2, hl1_5.out2, hl1_6.out2 -> output2.in0, output2.in1, output2.in2, output2.in3, output2.in4, output2.in5, output2.in6 - hl1_0.out3, hl1_1.out3, hl1_2.out3, hl1_3.out3, hl1_4.out3, hl1_5.out3, hl1_6.out3 -> output3.in0, output3.in1, output3.in2, output3.in3, output3.in4, output3.in5, output3.in6 - output0.result -> processor.in0 - output1.result -> processor.in1 - output2.result -> processor.in2 - output3.result -> processor.in3 - - setup.intended -> processor.intended - - processor.back_out0 -> output0.back_in - processor.back_out1 -> output1.back_in - processor.back_out2 -> output2.back_in - processor.back_out3 -> output3.back_in - hl0_0.back_out0, hl0_0.back_out1, hl0_0.back_out2, hl0_0.back_out3, hl0_0.back_out4, hl0_0.back_out5, hl0_0.back_out6, hl0_0.back_out7, hl0_0.back_out8, hl0_0.back_out9, hl0_0.back_out10, hl0_0.back_out11, hl0_0.back_out12, hl0_0.back_out13, hl0_0.back_out14, hl0_0.back_out15 -> input0.back_in0, input1.back_in0, input2.back_in0, input3.back_in0, input4.back_in0, input5.back_in0, input6.back_in0, input7.back_in0, input8.back_in0, input9.back_in0, input10.back_in0, input11.back_in0, input12.back_in0, input13.back_in0, input14.back_in0, input15.back_in0 - - hl0_1.back_out0, hl0_1.back_out1, hl0_1.back_out2, hl0_1.back_out3, hl0_1.back_out4, hl0_1.back_out5, hl0_1.back_out6, hl0_1.back_out7, hl0_1.back_out8, hl0_1.back_out9, hl0_1.back_out10, hl0_1.back_out11, hl0_1.back_out12, hl0_1.back_out13, hl0_1.back_out14, hl0_1.back_out15 -> input0.back_in1, input1.back_in1, input2.back_in1, input3.back_in1, input4.back_in1, input5.back_in1, input6.back_in1, input7.back_in1, input8.back_in1, input9.back_in1, input10.back_in1, input11.back_in1, input12.back_in1, input13.back_in1, input14.back_in1, input15.back_in1 - - hl0_2.back_out0, hl0_2.back_out1, hl0_2.back_out2, hl0_2.back_out3, hl0_2.back_out4, hl0_2.back_out5, hl0_2.back_out6, hl0_2.back_out7, hl0_2.back_out8, hl0_2.back_out9, hl0_2.back_out10, hl0_2.back_out11, hl0_2.back_out12, hl0_2.back_out13, hl0_2.back_out14, hl0_2.back_out15 -> input0.back_in2, input1.back_in2, input2.back_in2, input3.back_in2, input4.back_in2, input5.back_in2, input6.back_in2, input7.back_in2, input8.back_in2, input9.back_in2, input10.back_in2, input11.back_in2, input12.back_in2, input13.back_in2, input14.back_in2, input15.back_in2 - - hl0_3.back_out0, hl0_3.back_out1, hl0_3.back_out2, hl0_3.back_out3, hl0_3.back_out4, hl0_3.back_out5, hl0_3.back_out6, hl0_3.back_out7, hl0_3.back_out8, hl0_3.back_out9, hl0_3.back_out10, hl0_3.back_out11, hl0_3.back_out12, hl0_3.back_out13, hl0_3.back_out14, hl0_3.back_out15 -> input0.back_in3, input1.back_in3, input2.back_in3, input3.back_in3, input4.back_in3, input5.back_in3, input6.back_in3, input7.back_in3, input8.back_in3, input9.back_in3, input10.back_in3, input11.back_in3, input12.back_in3, input13.back_in3, input14.back_in3, input15.back_in3 - - hl0_4.back_out0, hl0_4.back_out1, hl0_4.back_out2, hl0_4.back_out3, hl0_4.back_out4, hl0_4.back_out5, hl0_4.back_out6, hl0_4.back_out7, hl0_4.back_out8, hl0_4.back_out9, hl0_4.back_out10, hl0_4.back_out11, hl0_4.back_out12, hl0_4.back_out13, hl0_4.back_out14, hl0_4.back_out15 -> input0.back_in4, input1.back_in4, input2.back_in4, input3.back_in4, input4.back_in4, input5.back_in4, input6.back_in4, input7.back_in4, input8.back_in4, input9.back_in4, input10.back_in4, input11.back_in4, input12.back_in4, input13.back_in4, input14.back_in4, input15.back_in4 - - hl0_5.back_out0, hl0_5.back_out1, hl0_5.back_out2, hl0_5.back_out3, hl0_5.back_out4, hl0_5.back_out5, hl0_5.back_out6, hl0_5.back_out7, hl0_5.back_out8, hl0_5.back_out9, hl0_5.back_out10, hl0_5.back_out11, hl0_5.back_out12, hl0_5.back_out13, hl0_5.back_out14, hl0_5.back_out15 -> input0.back_in5, input1.back_in5, input2.back_in5, input3.back_in5, input4.back_in5, input5.back_in5, input6.back_in5, input7.back_in5, input8.back_in5, input9.back_in5, input10.back_in5, input11.back_in5, input12.back_in5, input13.back_in5, input14.back_in5, input15.back_in5 - - hl0_6.back_out0, hl0_6.back_out1, hl0_6.back_out2, hl0_6.back_out3, hl0_6.back_out4, hl0_6.back_out5, hl0_6.back_out6, hl0_6.back_out7, hl0_6.back_out8, hl0_6.back_out9, hl0_6.back_out10, hl0_6.back_out11, hl0_6.back_out12, hl0_6.back_out13, hl0_6.back_out14, hl0_6.back_out15 -> input0.back_in6, input1.back_in6, input2.back_in6, input3.back_in6, input4.back_in6, input5.back_in6, input6.back_in6, input7.back_in6, input8.back_in6, input9.back_in6, input10.back_in6, input11.back_in6, input12.back_in6, input13.back_in6, input14.back_in6, input15.back_in6 - - hl1_0.back_out0, hl1_0.back_out1, hl1_0.back_out2, hl1_0.back_out3, hl1_0.back_out4, hl1_0.back_out5, hl1_0.back_out6 -> hl0_0.back_in0, hl0_1.back_in0, hl0_2.back_in0, hl0_3.back_in0, hl0_4.back_in0, hl0_5.back_in0, hl0_6.back_in0 - - hl1_1.back_out0, hl1_1.back_out1, hl1_1.back_out2, hl1_1.back_out3, hl1_1.back_out4, hl1_1.back_out5, hl1_1.back_out6 -> hl0_0.back_in1, hl0_1.back_in1, hl0_2.back_in1, hl0_3.back_in1, hl0_4.back_in1, hl0_5.back_in1, hl0_6.back_in1 - - hl1_2.back_out0, hl1_2.back_out1, hl1_2.back_out2, hl1_2.back_out3, hl1_2.back_out4, hl1_2.back_out5, hl1_2.back_out6 -> hl0_0.back_in2, hl0_1.back_in2, hl0_2.back_in2, hl0_3.back_in2, hl0_4.back_in2, hl0_5.back_in2, hl0_6.back_in2 - - hl1_3.back_out0, hl1_3.back_out1, hl1_3.back_out2, hl1_3.back_out3, hl1_3.back_out4, hl1_3.back_out5, hl1_3.back_out6 -> hl0_0.back_in3, hl0_1.back_in3, hl0_2.back_in3, hl0_3.back_in3, hl0_4.back_in3, hl0_5.back_in3, hl0_6.back_in3 - - hl1_4.back_out0, hl1_4.back_out1, hl1_4.back_out2, hl1_4.back_out3, hl1_4.back_out4, hl1_4.back_out5, hl1_4.back_out6 -> hl0_0.back_in4, hl0_1.back_in4, hl0_2.back_in4, hl0_3.back_in4, hl0_4.back_in4, hl0_5.back_in4, hl0_6.back_in4 - - hl1_5.back_out0, hl1_5.back_out1, hl1_5.back_out2, hl1_5.back_out3, hl1_5.back_out4, hl1_5.back_out5, hl1_5.back_out6 -> hl0_0.back_in5, hl0_1.back_in5, hl0_2.back_in5, hl0_3.back_in5, hl0_4.back_in5, hl0_5.back_in5, hl0_6.back_in5 - - hl1_6.back_out0, hl1_6.back_out1, hl1_6.back_out2, hl1_6.back_out3, hl1_6.back_out4, hl1_6.back_out5, hl1_6.back_out6 -> hl0_0.back_in6, hl0_1.back_in6, hl0_2.back_in6, hl0_3.back_in6, hl0_4.back_in6, hl0_5.back_in6, hl0_6.back_in6 - - output0.back_out0, output0.back_out1, output0.back_out2, output0.back_out3, output0.back_out4, output0.back_out5, output0.back_out6 -> hl1_0.back_in0, hl1_1.back_in0, hl1_2.back_in0, hl1_3.back_in0, hl1_4.back_in0, hl1_5.back_in0, hl1_6.back_in0 - - output1.back_out0, output1.back_out1, output1.back_out2, output1.back_out3, output1.back_out4, output1.back_out5, output1.back_out6 -> hl1_0.back_in1, hl1_1.back_in1, hl1_2.back_in1, hl1_3.back_in1, hl1_4.back_in1, hl1_5.back_in1, hl1_6.back_in1 - - output2.back_out0, output2.back_out1, output2.back_out2, output2.back_out3, output2.back_out4, output2.back_out5, output2.back_out6 -> hl1_0.back_in2, hl1_1.back_in2, hl1_2.back_in2, hl1_3.back_in2, hl1_4.back_in2, hl1_5.back_in2, hl1_6.back_in2 - - output3.back_out0, output3.back_out1, output3.back_out2, output3.back_out3, output3.back_out4, output3.back_out5, output3.back_out6 -> hl1_0.back_in3, hl1_1.back_in3, hl1_2.back_in3, hl1_3.back_in3, hl1_4.back_in3, hl1_5.back_in3, hl1_6.back_in3 - - + setup = new Setup() + + input0= new Input() + input1= new Input() + input2= new Input() + input3= new Input() + input4= new Input() + input5= new Input() + input6= new Input() + input7= new Input() + input8= new Input() + input9= new Input() + input10= new Input() + input11= new Input() + input12= new Input() + input13= new Input() + input14= new Input() + input15= new Input() + hl0_0 = new HL0() + hl0_1 = new HL0() + hl0_2 = new HL0() + hl0_3 = new HL0() + hl0_4 = new HL0() + hl0_5 = new HL0() + hl0_6 = new HL0() + hl1_0 = new HL1() + hl1_1 = new HL1() + hl1_2 = new HL1() + hl1_3 = new HL1() + hl1_4 = new HL1() + hl1_5 = new HL1() + hl1_6 = new HL1() + output0 = new Output() + + output1 = new Output() + + output2 = new Output() + + output3 = new Output() + + processor = new Processor() + + setup.out0 -> input0.data + setup.out1 -> input1.data + setup.out2 -> input2.data + setup.out3 -> input3.data + setup.out4 -> input4.data + setup.out5 -> input5.data + setup.out6 -> input6.data + setup.out7 -> input7.data + setup.out8 -> input8.data + setup.out9 -> input9.data + setup.out10 -> input10.data + setup.out11 -> input11.data + setup.out12 -> input12.data + setup.out13 -> input13.data + setup.out14 -> input14.data + setup.out15 -> input15.data + input0.out0, input1.out0, input2.out0, input3.out0, input4.out0, input5.out0, input6.out0, input7.out0, input8.out0, input9.out0, input10.out0, input11.out0, input12.out0, input13.out0, input14.out0, input15.out0 -> hl0_0.in0, hl0_0.in1, hl0_0.in2, hl0_0.in3, hl0_0.in4, hl0_0.in5, hl0_0.in6, hl0_0.in7, hl0_0.in8, hl0_0.in9, hl0_0.in10, hl0_0.in11, hl0_0.in12, hl0_0.in13, hl0_0.in14, hl0_0.in15 + input0.out1, input1.out1, input2.out1, input3.out1, input4.out1, input5.out1, input6.out1, input7.out1, input8.out1, input9.out1, input10.out1, input11.out1, input12.out1, input13.out1, input14.out1, input15.out1 -> hl0_1.in0, hl0_1.in1, hl0_1.in2, hl0_1.in3, hl0_1.in4, hl0_1.in5, hl0_1.in6, hl0_1.in7, hl0_1.in8, hl0_1.in9, hl0_1.in10, hl0_1.in11, hl0_1.in12, hl0_1.in13, hl0_1.in14, hl0_1.in15 + input0.out2, input1.out2, input2.out2, input3.out2, input4.out2, input5.out2, input6.out2, input7.out2, input8.out2, input9.out2, input10.out2, input11.out2, input12.out2, input13.out2, input14.out2, input15.out2 -> hl0_2.in0, hl0_2.in1, hl0_2.in2, hl0_2.in3, hl0_2.in4, hl0_2.in5, hl0_2.in6, hl0_2.in7, hl0_2.in8, hl0_2.in9, hl0_2.in10, hl0_2.in11, hl0_2.in12, hl0_2.in13, hl0_2.in14, hl0_2.in15 + input0.out3, input1.out3, input2.out3, input3.out3, input4.out3, input5.out3, input6.out3, input7.out3, input8.out3, input9.out3, input10.out3, input11.out3, input12.out3, input13.out3, input14.out3, input15.out3 -> hl0_3.in0, hl0_3.in1, hl0_3.in2, hl0_3.in3, hl0_3.in4, hl0_3.in5, hl0_3.in6, hl0_3.in7, hl0_3.in8, hl0_3.in9, hl0_3.in10, hl0_3.in11, hl0_3.in12, hl0_3.in13, hl0_3.in14, hl0_3.in15 + input0.out4, input1.out4, input2.out4, input3.out4, input4.out4, input5.out4, input6.out4, input7.out4, input8.out4, input9.out4, input10.out4, input11.out4, input12.out4, input13.out4, input14.out4, input15.out4 -> hl0_4.in0, hl0_4.in1, hl0_4.in2, hl0_4.in3, hl0_4.in4, hl0_4.in5, hl0_4.in6, hl0_4.in7, hl0_4.in8, hl0_4.in9, hl0_4.in10, hl0_4.in11, hl0_4.in12, hl0_4.in13, hl0_4.in14, hl0_4.in15 + input0.out5, input1.out5, input2.out5, input3.out5, input4.out5, input5.out5, input6.out5, input7.out5, input8.out5, input9.out5, input10.out5, input11.out5, input12.out5, input13.out5, input14.out5, input15.out5 -> hl0_5.in0, hl0_5.in1, hl0_5.in2, hl0_5.in3, hl0_5.in4, hl0_5.in5, hl0_5.in6, hl0_5.in7, hl0_5.in8, hl0_5.in9, hl0_5.in10, hl0_5.in11, hl0_5.in12, hl0_5.in13, hl0_5.in14, hl0_5.in15 + input0.out6, input1.out6, input2.out6, input3.out6, input4.out6, input5.out6, input6.out6, input7.out6, input8.out6, input9.out6, input10.out6, input11.out6, input12.out6, input13.out6, input14.out6, input15.out6 -> hl0_6.in0, hl0_6.in1, hl0_6.in2, hl0_6.in3, hl0_6.in4, hl0_6.in5, hl0_6.in6, hl0_6.in7, hl0_6.in8, hl0_6.in9, hl0_6.in10, hl0_6.in11, hl0_6.in12, hl0_6.in13, hl0_6.in14, hl0_6.in15 + hl0_0.out0, hl0_1.out0, hl0_2.out0, hl0_3.out0, hl0_4.out0, hl0_5.out0, hl0_6.out0 -> hl1_0.in0, hl1_0.in1, hl1_0.in2, hl1_0.in3, hl1_0.in4, hl1_0.in5, hl1_0.in6 + hl0_0.out1, hl0_1.out1, hl0_2.out1, hl0_3.out1, hl0_4.out1, hl0_5.out1, hl0_6.out1 -> hl1_1.in0, hl1_1.in1, hl1_1.in2, hl1_1.in3, hl1_1.in4, hl1_1.in5, hl1_1.in6 + hl0_0.out2, hl0_1.out2, hl0_2.out2, hl0_3.out2, hl0_4.out2, hl0_5.out2, hl0_6.out2 -> hl1_2.in0, hl1_2.in1, hl1_2.in2, hl1_2.in3, hl1_2.in4, hl1_2.in5, hl1_2.in6 + hl0_0.out3, hl0_1.out3, hl0_2.out3, hl0_3.out3, hl0_4.out3, hl0_5.out3, hl0_6.out3 -> hl1_3.in0, hl1_3.in1, hl1_3.in2, hl1_3.in3, hl1_3.in4, hl1_3.in5, hl1_3.in6 + hl0_0.out4, hl0_1.out4, hl0_2.out4, hl0_3.out4, hl0_4.out4, hl0_5.out4, hl0_6.out4 -> hl1_4.in0, hl1_4.in1, hl1_4.in2, hl1_4.in3, hl1_4.in4, hl1_4.in5, hl1_4.in6 + hl0_0.out5, hl0_1.out5, hl0_2.out5, hl0_3.out5, hl0_4.out5, hl0_5.out5, hl0_6.out5 -> hl1_5.in0, hl1_5.in1, hl1_5.in2, hl1_5.in3, hl1_5.in4, hl1_5.in5, hl1_5.in6 + hl0_0.out6, hl0_1.out6, hl0_2.out6, hl0_3.out6, hl0_4.out6, hl0_5.out6, hl0_6.out6 -> hl1_6.in0, hl1_6.in1, hl1_6.in2, hl1_6.in3, hl1_6.in4, hl1_6.in5, hl1_6.in6 + hl1_0.out0, hl1_1.out0, hl1_2.out0, hl1_3.out0, hl1_4.out0, hl1_5.out0, hl1_6.out0 -> output0.in0, output0.in1, output0.in2, output0.in3, output0.in4, output0.in5, output0.in6 + hl1_0.out1, hl1_1.out1, hl1_2.out1, hl1_3.out1, hl1_4.out1, hl1_5.out1, hl1_6.out1 -> output1.in0, output1.in1, output1.in2, output1.in3, output1.in4, output1.in5, output1.in6 + hl1_0.out2, hl1_1.out2, hl1_2.out2, hl1_3.out2, hl1_4.out2, hl1_5.out2, hl1_6.out2 -> output2.in0, output2.in1, output2.in2, output2.in3, output2.in4, output2.in5, output2.in6 + hl1_0.out3, hl1_1.out3, hl1_2.out3, hl1_3.out3, hl1_4.out3, hl1_5.out3, hl1_6.out3 -> output3.in0, output3.in1, output3.in2, output3.in3, output3.in4, output3.in5, output3.in6 + output0.result -> processor.in0 + output1.result -> processor.in1 + output2.result -> processor.in2 + output3.result -> processor.in3 + + setup.intended -> processor.intended + + processor.back_out0 -> output0.back_in + processor.back_out1 -> output1.back_in + processor.back_out2 -> output2.back_in + processor.back_out3 -> output3.back_in + hl0_0.back_out0, hl0_0.back_out1, hl0_0.back_out2, hl0_0.back_out3, hl0_0.back_out4, hl0_0.back_out5, hl0_0.back_out6, hl0_0.back_out7, hl0_0.back_out8, hl0_0.back_out9, hl0_0.back_out10, hl0_0.back_out11, hl0_0.back_out12, hl0_0.back_out13, hl0_0.back_out14, hl0_0.back_out15 -> input0.back_in0, input1.back_in0, input2.back_in0, input3.back_in0, input4.back_in0, input5.back_in0, input6.back_in0, input7.back_in0, input8.back_in0, input9.back_in0, input10.back_in0, input11.back_in0, input12.back_in0, input13.back_in0, input14.back_in0, input15.back_in0 + + hl0_1.back_out0, hl0_1.back_out1, hl0_1.back_out2, hl0_1.back_out3, hl0_1.back_out4, hl0_1.back_out5, hl0_1.back_out6, hl0_1.back_out7, hl0_1.back_out8, hl0_1.back_out9, hl0_1.back_out10, hl0_1.back_out11, hl0_1.back_out12, hl0_1.back_out13, hl0_1.back_out14, hl0_1.back_out15 -> input0.back_in1, input1.back_in1, input2.back_in1, input3.back_in1, input4.back_in1, input5.back_in1, input6.back_in1, input7.back_in1, input8.back_in1, input9.back_in1, input10.back_in1, input11.back_in1, input12.back_in1, input13.back_in1, input14.back_in1, input15.back_in1 + + hl0_2.back_out0, hl0_2.back_out1, hl0_2.back_out2, hl0_2.back_out3, hl0_2.back_out4, hl0_2.back_out5, hl0_2.back_out6, hl0_2.back_out7, hl0_2.back_out8, hl0_2.back_out9, hl0_2.back_out10, hl0_2.back_out11, hl0_2.back_out12, hl0_2.back_out13, hl0_2.back_out14, hl0_2.back_out15 -> input0.back_in2, input1.back_in2, input2.back_in2, input3.back_in2, input4.back_in2, input5.back_in2, input6.back_in2, input7.back_in2, input8.back_in2, input9.back_in2, input10.back_in2, input11.back_in2, input12.back_in2, input13.back_in2, input14.back_in2, input15.back_in2 + + hl0_3.back_out0, hl0_3.back_out1, hl0_3.back_out2, hl0_3.back_out3, hl0_3.back_out4, hl0_3.back_out5, hl0_3.back_out6, hl0_3.back_out7, hl0_3.back_out8, hl0_3.back_out9, hl0_3.back_out10, hl0_3.back_out11, hl0_3.back_out12, hl0_3.back_out13, hl0_3.back_out14, hl0_3.back_out15 -> input0.back_in3, input1.back_in3, input2.back_in3, input3.back_in3, input4.back_in3, input5.back_in3, input6.back_in3, input7.back_in3, input8.back_in3, input9.back_in3, input10.back_in3, input11.back_in3, input12.back_in3, input13.back_in3, input14.back_in3, input15.back_in3 + + hl0_4.back_out0, hl0_4.back_out1, hl0_4.back_out2, hl0_4.back_out3, hl0_4.back_out4, hl0_4.back_out5, hl0_4.back_out6, hl0_4.back_out7, hl0_4.back_out8, hl0_4.back_out9, hl0_4.back_out10, hl0_4.back_out11, hl0_4.back_out12, hl0_4.back_out13, hl0_4.back_out14, hl0_4.back_out15 -> input0.back_in4, input1.back_in4, input2.back_in4, input3.back_in4, input4.back_in4, input5.back_in4, input6.back_in4, input7.back_in4, input8.back_in4, input9.back_in4, input10.back_in4, input11.back_in4, input12.back_in4, input13.back_in4, input14.back_in4, input15.back_in4 + + hl0_5.back_out0, hl0_5.back_out1, hl0_5.back_out2, hl0_5.back_out3, hl0_5.back_out4, hl0_5.back_out5, hl0_5.back_out6, hl0_5.back_out7, hl0_5.back_out8, hl0_5.back_out9, hl0_5.back_out10, hl0_5.back_out11, hl0_5.back_out12, hl0_5.back_out13, hl0_5.back_out14, hl0_5.back_out15 -> input0.back_in5, input1.back_in5, input2.back_in5, input3.back_in5, input4.back_in5, input5.back_in5, input6.back_in5, input7.back_in5, input8.back_in5, input9.back_in5, input10.back_in5, input11.back_in5, input12.back_in5, input13.back_in5, input14.back_in5, input15.back_in5 + + hl0_6.back_out0, hl0_6.back_out1, hl0_6.back_out2, hl0_6.back_out3, hl0_6.back_out4, hl0_6.back_out5, hl0_6.back_out6, hl0_6.back_out7, hl0_6.back_out8, hl0_6.back_out9, hl0_6.back_out10, hl0_6.back_out11, hl0_6.back_out12, hl0_6.back_out13, hl0_6.back_out14, hl0_6.back_out15 -> input0.back_in6, input1.back_in6, input2.back_in6, input3.back_in6, input4.back_in6, input5.back_in6, input6.back_in6, input7.back_in6, input8.back_in6, input9.back_in6, input10.back_in6, input11.back_in6, input12.back_in6, input13.back_in6, input14.back_in6, input15.back_in6 + + hl1_0.back_out0, hl1_0.back_out1, hl1_0.back_out2, hl1_0.back_out3, hl1_0.back_out4, hl1_0.back_out5, hl1_0.back_out6 -> hl0_0.back_in0, hl0_1.back_in0, hl0_2.back_in0, hl0_3.back_in0, hl0_4.back_in0, hl0_5.back_in0, hl0_6.back_in0 + + hl1_1.back_out0, hl1_1.back_out1, hl1_1.back_out2, hl1_1.back_out3, hl1_1.back_out4, hl1_1.back_out5, hl1_1.back_out6 -> hl0_0.back_in1, hl0_1.back_in1, hl0_2.back_in1, hl0_3.back_in1, hl0_4.back_in1, hl0_5.back_in1, hl0_6.back_in1 + + hl1_2.back_out0, hl1_2.back_out1, hl1_2.back_out2, hl1_2.back_out3, hl1_2.back_out4, hl1_2.back_out5, hl1_2.back_out6 -> hl0_0.back_in2, hl0_1.back_in2, hl0_2.back_in2, hl0_3.back_in2, hl0_4.back_in2, hl0_5.back_in2, hl0_6.back_in2 + + hl1_3.back_out0, hl1_3.back_out1, hl1_3.back_out2, hl1_3.back_out3, hl1_3.back_out4, hl1_3.back_out5, hl1_3.back_out6 -> hl0_0.back_in3, hl0_1.back_in3, hl0_2.back_in3, hl0_3.back_in3, hl0_4.back_in3, hl0_5.back_in3, hl0_6.back_in3 + + hl1_4.back_out0, hl1_4.back_out1, hl1_4.back_out2, hl1_4.back_out3, hl1_4.back_out4, hl1_4.back_out5, hl1_4.back_out6 -> hl0_0.back_in4, hl0_1.back_in4, hl0_2.back_in4, hl0_3.back_in4, hl0_4.back_in4, hl0_5.back_in4, hl0_6.back_in4 + + hl1_5.back_out0, hl1_5.back_out1, hl1_5.back_out2, hl1_5.back_out3, hl1_5.back_out4, hl1_5.back_out5, hl1_5.back_out6 -> hl0_0.back_in5, hl0_1.back_in5, hl0_2.back_in5, hl0_3.back_in5, hl0_4.back_in5, hl0_5.back_in5, hl0_6.back_in5 + + hl1_6.back_out0, hl1_6.back_out1, hl1_6.back_out2, hl1_6.back_out3, hl1_6.back_out4, hl1_6.back_out5, hl1_6.back_out6 -> hl0_0.back_in6, hl0_1.back_in6, hl0_2.back_in6, hl0_3.back_in6, hl0_4.back_in6, hl0_5.back_in6, hl0_6.back_in6 + + output0.back_out0, output0.back_out1, output0.back_out2, output0.back_out3, output0.back_out4, output0.back_out5, output0.back_out6 -> hl1_0.back_in0, hl1_1.back_in0, hl1_2.back_in0, hl1_3.back_in0, hl1_4.back_in0, hl1_5.back_in0, hl1_6.back_in0 + + output1.back_out0, output1.back_out1, output1.back_out2, output1.back_out3, output1.back_out4, output1.back_out5, output1.back_out6 -> hl1_0.back_in1, hl1_1.back_in1, hl1_2.back_in1, hl1_3.back_in1, hl1_4.back_in1, hl1_5.back_in1, hl1_6.back_in1 + + output2.back_out0, output2.back_out1, output2.back_out2, output2.back_out3, output2.back_out4, output2.back_out5, output2.back_out6 -> hl1_0.back_in2, hl1_1.back_in2, hl1_2.back_in2, hl1_3.back_in2, hl1_4.back_in2, hl1_5.back_in2, hl1_6.back_in2 + + output3.back_out0, output3.back_out1, output3.back_out2, output3.back_out3, output3.back_out4, output3.back_out5, output3.back_out6 -> hl1_0.back_in3, hl1_1.back_in3, hl1_2.back_in3, hl1_3.back_in3, hl1_4.back_in3, hl1_5.back_in3, hl1_6.back_in3 + + } diff --git a/experimental/Python/src/Intersection/Intersection.lf b/experimental/Python/src/Intersection/Intersection.lf index 78f65c74..33ef37b5 100644 --- a/experimental/Python/src/Intersection/Intersection.lf +++ b/experimental/Python/src/Intersection/Intersection.lf @@ -29,109 +29,109 @@ import time import random try: - from math import isclose + from math import isclose except ImportError: - def isclose(a, b, rel_tol=1e-09, abs_tol=0.0): - return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) + def isclose(a, b, rel_tol=1e-09, abs_tol=0.0): + return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) class coordinate: + """ + Represent a GPS coordinate in the form of x (lat), + y (lon), and z (alt). + """ + + def __init__(self, x = 0.0, y = 0.0, z = 0.0): + self.x = x + self.y = y + self.z = z + def distance(self, coordinate2): """ - Represent a GPS coordinate in the form of x (lat), - y (lon), and z (alt). + Calculate the great circle distance between two points + on the earth (specified in decimal degrees) + Taken from: https://stackoverflow.com/a/15737218/783868 """ + # Currently ignores altitude + # Convert decimal degrees to radians + lat1 = radians(self.x) + lon1 = radians(self.y) + lat2 = radians(coordinate2.x) + lon2 = radians(coordinate2.y) - def __init__(self, x = 0.0, y = 0.0, z = 0.0): - self.x = x - self.y = y - self.z = z - def distance(self, coordinate2): - """ - Calculate the great circle distance between two points - on the earth (specified in decimal degrees) - Taken from: https://stackoverflow.com/a/15737218/783868 - """ - # Currently ignores altitude - # Convert decimal degrees to radians - lat1 = radians(self.x) - lon1 = radians(self.y) - lat2 = radians(coordinate2.x) - lon2 = radians(coordinate2.y) - - # Haversine formula - dlon = lon2 - lon1 - dlat = lat2 - lat1 - a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 - c = 2 * atan2(sqrt(a), sqrt(1 - a)) - # Radius of earth in kilometers is 6371 - km = 6371.0 * c - m = km * 1000.0 - return m + # Haversine formula + dlon = lon2 - lon1 + dlat = lat2 - lat1 + a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 + c = 2 * atan2(sqrt(a), sqrt(1 - a)) + # Radius of earth in kilometers is 6371 + km = 6371.0 * c + m = km * 1000.0 + return m class intersection_request: - """ - Represent a request to enter the intersection. - Sent by vehicles attempting to enter to the - Road-Side Unit (RSU) - """ - def __init__(self, speed = 0.0, current_pos = coordinate(0,0,0)): - self.speed = float(speed) - self.current_pos = current_pos # Current GPS position of the vehicle - + """ + Represent a request to enter the intersection. + Sent by vehicles attempting to enter to the + Road-Side Unit (RSU) + """ + def __init__(self, speed = 0.0, current_pos = coordinate(0,0,0)): + self.speed = float(speed) + self.current_pos = current_pos # Current GPS position of the vehicle + class vehicle_grant: - """ - Represent a grant issued by the Road-Side Unit (RSU) - to vehicles allowing them to enter the intersection - at a given 'arrival_time' with a given optional - 'target_speed'. - """ + """ + Represent a grant issued by the Road-Side Unit (RSU) + to vehicles allowing them to enter the intersection + at a given 'arrival_time' with a given optional + 'target_speed'. + """ + + def __init__(self, target_speed = 0.0, arrival_time = None, intersection_pos = None): + self.target_speed = target_speed + self.arrival_time = arrival_time + self.intersection_pos = intersection_pos - def __init__(self, target_speed = 0.0, arrival_time = None, intersection_pos = None): - self.target_speed = target_speed - self.arrival_time = arrival_time - self.intersection_pos = intersection_pos - class vehicle_command: - """ - Command sent by the vehicle controller to the vehicle - interface. - """ + """ + Command sent by the vehicle controller to the vehicle + interface. + """ + + def __init__(self, throttle = 0.0, brake = 0.0): + self.throttle = throttle + self.brake = brake - def __init__(self, throttle = 0.0, brake = 0.0): - self.throttle = throttle - self.brake = brake - class vehicle_status: - """ - Current status of the vehicle received from the - vehicle interface. Currently only contains - 'velocity'. - """ - - def __init__(self, velocity): - self.velocity = velocity # Velocity of the vehicle in m/s - # Fetching acceleration and orientation is also possible, - # but both involve very complex structures. + """ + Current status of the vehicle received from the + vehicle interface. Currently only contains + 'velocity'. + """ + + def __init__(self, velocity): + self.velocity = velocity # Velocity of the vehicle in m/s + # Fetching acceleration and orientation is also possible, + # but both involve very complex structures. class vehicle_position: - """ - Current GPS position of the vehicle, received from - the vehicle interface or a separate GNSS sensor. - """ - - def __init__(self, current_pos): - self.current_pos = current_pos # Current GPS position of the vehicle - + """ + Current GPS position of the vehicle, received from + the vehicle interface or a separate GNSS sensor. + """ + + def __init__(self, current_pos): + self.current_pos = current_pos # Current GPS position of the vehicle + class vehicle_velocity: - """ - Represent the current 3D velocity of the vehicle in a - .x, .y, .z format. - """ - def __init__(self, x=0.0, y=0.0, z=0.0): - self.x = x - self.y = y - self.z = z - + """ + Represent the current 3D velocity of the vehicle in a + .x, .y, .z format. + """ + def __init__(self, x=0.0, y=0.0, z=0.0): + self.x = x + self.y = y + self.z = z + # The speed limit of vehicles in m/s speed_limit = 14.0 # The distance (in meters) at which the controller assumes it has reached its goal @@ -141,279 +141,279 @@ goal_reached_threshold_time = (goal_reached_threshold/speed_limit) =} reactor Vehicle (vehicle_id(0)){ - input vehicle_stat; - input vehicle_pos; + input vehicle_stat; + input vehicle_pos; - input grant; + input grant; + + output request; + + output control; + + output goal_reached; + + logical action delay; + + state current_pos({=coordinate(0.0, 0.0, 0.0)=}); + + state last_pos; + + state granted_time_to_enter(0); + state intersection_pos; + state goal_reached(false); + state velocity(0.0); + + reaction(vehicle_pos) {= + self.current_pos = vehicle_pos.value.current_pos; + =} + reaction(vehicle_stat) -> request, control, goal_reached {= + if self.goal_reached: + # Nothing to do here + return - output request; + # Record the speed + velocity_3d = vehicle_stat.value.velocity + linear_speed = sqrt(velocity_3d.x**2 + velocity_3d.y**2 + velocity_3d.z**2) + self.velocity = linear_speed - output control; + if self.velocity == 0: + # Prevent divisions by zero + self.velocity = 0.001 - output goal_reached; + # Check if we have received an initial pos + if self.current_pos.distance(coordinate(0.0, 0.0, 0.0)) <= 0.00000001: + print("Warning: Have not received initial pos yet.") + return - logical action delay; - - state current_pos({=coordinate(0.0, 0.0, 0.0)=}); - - state last_pos; - - state granted_time_to_enter(0); - state intersection_pos; - state goal_reached(false); - state velocity(0.0); - - reaction(vehicle_pos) {= - self.current_pos = vehicle_pos.value.current_pos; - =} - reaction(vehicle_stat) -> request, control, goal_reached {= - if self.goal_reached: - # Nothing to do here - return - - # Record the speed - velocity_3d = vehicle_stat.value.velocity - linear_speed = sqrt(velocity_3d.x**2 + velocity_3d.y**2 + velocity_3d.z**2) - self.velocity = linear_speed - - if self.velocity == 0: - # Prevent divisions by zero - self.velocity = 0.001 - - # Check if we have received an initial pos - if self.current_pos.distance(coordinate(0.0, 0.0, 0.0)) <= 0.00000001: - print("Warning: Have not received initial pos yet.") - return - - # Send a new request to the RSU if no time to enter - # the intersection is granted - if self.granted_time_to_enter == 0: - message = intersection_request(speed = self.velocity, current_pos = self.current_pos) - request.set(message) - # Stop the vehicle - cmd = vehicle_command(throttle = 0, brake = 1) - control.set(cmd) - else: - # We have a granted time from the RSU - # All we need to do is adjust our velocity - # to enter the intersection at the allocated - # time - - # First, how far are we from the intersection - distance_remaining = self.intersection_pos.distance(self.current_pos) - time_remaining = (self.granted_time_to_enter - lf.time.logical()) / (BILLION * 1.0) - - print("########################################") - print("Vehicle {}: Distance to intersection: {}m.".format(self.vehicle_id + 1, distance_remaining)) - print("Vehicle {}: Time to intersection: {}s.".format(self.vehicle_id + 1, time_remaining)) - print("Vehicle {}: Current speed: {}m/s.".format(self.vehicle_id + 1, self.velocity)) + # Send a new request to the RSU if no time to enter + # the intersection is granted + if self.granted_time_to_enter == 0: + message = intersection_request(speed = self.velocity, current_pos = self.current_pos) + request.set(message) + # Stop the vehicle + cmd = vehicle_command(throttle = 0, brake = 1) + control.set(cmd) + else: + # We have a granted time from the RSU + # All we need to do is adjust our velocity + # to enter the intersection at the allocated + # time + + # First, how far are we from the intersection + distance_remaining = self.intersection_pos.distance(self.current_pos) + time_remaining = (self.granted_time_to_enter - lf.time.logical()) / (BILLION * 1.0) + + print("########################################") + print("Vehicle {}: Distance to intersection: {}m.".format(self.vehicle_id + 1, distance_remaining)) + print("Vehicle {}: Time to intersection: {}s.".format(self.vehicle_id + 1, time_remaining)) + print("Vehicle {}: Current speed: {}m/s.".format(self.vehicle_id + 1, self.velocity)) - target_speed = 0.0 - # target_speed = distance_remaining/time_remaining - - if distance_remaining <= goal_reached_threshold and \ - time_remaining <= goal_reached_threshold_time : - # Goal reached - # At this point, a normal controller should stop the vehicle until - # it receives a new goal. However, for the purposes of this demo, - # it will set the target speed to the speed limit so that vehicles - # can leave the intersection (otherwise, they will just stop at the - # intersection). - target_speed = speed_limit - # Simulation is over - self.goal_reached = True - - print("\n\n*************************************************************\n\n".format(self.vehicle_id + 1)) - print("************* Vehicle {}: Reached intersection! *************".format(self.vehicle_id + 1)) - print("\n\n*************************************************************\n\n".format(self.vehicle_id + 1)) - - goal_reached.set(True) - elif time_remaining < (distance_remaining / speed_limit): - # No time to make it to the intersection even if we - # were going at the speed limit. - # Ask the RSU again - self.granted_time_to_enter = 0 - # Apply the brake since we ran out of time - target_speed = 0 - else: - # Has not reached the goal - # target_speed = ((2 * distance_remaining) / (time_remaining)) - self.velocity - target_speed = distance_remaining / time_remaining - - print("Vehicle {}: Calculated target speed: {}m/s.".format(self.vehicle_id + 1, target_speed)) - - if (target_speed - speed_limit) > 0: - print("Warning: target speed exceeds the speed limit") - target_speed = 0 - self.granted_time_to_enter = 0 - - if target_speed <= 0: - print("Warning: target speed negative or zero") - target_speed = 0.001 - self.granted_time_to_enter = 0 - - brake = 0.0 - throttle = 0.0 + target_speed = 0.0 + # target_speed = distance_remaining/time_remaining - if target_speed >= self.velocity: - # Calculate a proportional throttle (0.0 < throttle < 1.0) - throttle = min((target_speed - self.velocity)/target_speed, 1) - # throttle = 1.0 - brake = 0.0 - # throttle = min(abs(target_speed / self.velocity), 1) - else: - # Need to apply the brake - brake = min((self.velocity - target_speed)/self.velocity, 1) - # brake = 1.0 - throttle = 0.0 - - # Check throttle boundaries - if throttle < 0: - print("Error: negative throttle") - throttle = 0 - - # Prepare and send the target velocity as a vehicle command - cmd = vehicle_command(throttle = throttle, brake = brake) - control.set(cmd) - - print("Vehicle {}: Throttle: {}. Brake: {}".format(self.vehicle_id + 1, throttle, brake)) - =} - - reaction(grant) {= - print("Vehicle {} Granted access".format(self.vehicle_id + 1), - "to enter the intersection at elapsed logical time {:d}.\n".format( - int(grant.value.arrival_time) - lf.time.start() - ), - "Current elapsed logical time: {:d}, Current physical time is {:d}.".format( - lf.time.logical_elapsed(), - lf.time.physical_elapsed()) - ) + if distance_remaining <= goal_reached_threshold and \ + time_remaining <= goal_reached_threshold_time : + # Goal reached + # At this point, a normal controller should stop the vehicle until + # it receives a new goal. However, for the purposes of this demo, + # it will set the target speed to the speed limit so that vehicles + # can leave the intersection (otherwise, they will just stop at the + # intersection). + target_speed = speed_limit + # Simulation is over + self.goal_reached = True - self.granted_time_to_enter = grant.value.arrival_time - self.intersection_pos = grant.value.intersection_pos - self.goal_reached = False - =} deadline (5 sec) {= - # Ignore the grant. Will ask for another one - print("Received the grant late.") + print("\n\n*************************************************************\n\n".format(self.vehicle_id + 1)) + print("************* Vehicle {}: Reached intersection! *************".format(self.vehicle_id + 1)) + print("\n\n*************************************************************\n\n".format(self.vehicle_id + 1)) + + goal_reached.set(True) + elif time_remaining < (distance_remaining / speed_limit): + # No time to make it to the intersection even if we + # were going at the speed limit. + # Ask the RSU again + self.granted_time_to_enter = 0 + # Apply the brake since we ran out of time + target_speed = 0 + else: + # Has not reached the goal + # target_speed = ((2 * distance_remaining) / (time_remaining)) - self.velocity + target_speed = distance_remaining / time_remaining + + print("Vehicle {}: Calculated target speed: {}m/s.".format(self.vehicle_id + 1, target_speed)) + + if (target_speed - speed_limit) > 0: + print("Warning: target speed exceeds the speed limit") + target_speed = 0 + self.granted_time_to_enter = 0 + + if target_speed <= 0: + print("Warning: target speed negative or zero") + target_speed = 0.001 self.granted_time_to_enter = 0 - =} + + brake = 0.0 + throttle = 0.0 + + if target_speed >= self.velocity: + # Calculate a proportional throttle (0.0 < throttle < 1.0) + throttle = min((target_speed - self.velocity)/target_speed, 1) + # throttle = 1.0 + brake = 0.0 + # throttle = min(abs(target_speed / self.velocity), 1) + else: + # Need to apply the brake + brake = min((self.velocity - target_speed)/self.velocity, 1) + # brake = 1.0 + throttle = 0.0 + + # Check throttle boundaries + if throttle < 0: + print("Error: negative throttle") + throttle = 0 + + # Prepare and send the target velocity as a vehicle command + cmd = vehicle_command(throttle = throttle, brake = brake) + control.set(cmd) + + print("Vehicle {}: Throttle: {}. Brake: {}".format(self.vehicle_id + 1, throttle, brake)) + =} + + reaction(grant) {= + print("Vehicle {} Granted access".format(self.vehicle_id + 1), + "to enter the intersection at elapsed logical time {:d}.\n".format( + int(grant.value.arrival_time) - lf.time.start() + ), + "Current elapsed logical time: {:d}, Current physical time is {:d}.".format( + lf.time.logical_elapsed(), + lf.time.physical_elapsed()) + ) + + self.granted_time_to_enter = grant.value.arrival_time + self.intersection_pos = grant.value.intersection_pos + self.goal_reached = False + =} deadline (5 sec) {= + # Ignore the grant. Will ask for another one + print("Received the grant late.") + self.granted_time_to_enter = 0 + =} } reactor RSU ( - num_entries(4), - intersection_width(42.0), // in meters. - // If the vehicle is told to slow down, then its target - // average speed in the intersection should be at least this. - nominal_speed_in_intersection(2.8), // In m/sec 0.6 sec to traverse. - intersection_pos({=coordinate(0.0, 0.0, 0.0)=}) // GPS coordinates for the intersection + num_entries(4), + intersection_width(42.0), // in meters. + // If the vehicle is told to slow down, then its target + // average speed in the intersection should be at least this. + nominal_speed_in_intersection(2.8), // In m/sec 0.6 sec to traverse. + intersection_pos({=coordinate(0.0, 0.0, 0.0)=}) // GPS coordinates for the intersection ) { - input[num_entries] request; - input[num_entries] vehicle_reached_intersection; - output[num_entries] grant; - - state earliest_free(0 msec); - state active_participants({=[0] * 20=}); - - reaction(request) -> grant {= - for i in range(self.num_entries): - if request[i].is_present: - self.active_participants[i] = 1 - if request[i].value.speed == 0: - # Avoid division by zero - request[i].value.speed = 0.001 - # Calculate the time it will take the approaching vehicle to - # arrive at its current speed. Note that this is - # time from the time the vehicle sends the message - # according to the arriving vehicle's clock. - speed_in_m_per_sec = request[i].value.speed - dr = self.intersection_pos.distance(request[i].value.current_pos) - print("*** RSU: Vehicle {}'s distance to intersection is {}.".format(i+1, dr)) - arrival_in = dr / speed_in_m_per_sec - - time_message_sent = lf.time.logical() - - # Convert the time interval to nsec (it is in seconds). - arrival_time_ns = time_message_sent + (arrival_in * BILLION) - - response = vehicle_grant() - if arrival_time_ns >= self.earliest_free: - # Vehicle can maintain speed. - response.target_speed = request[i].value.speed - response.arrival_time = arrival_time_ns - else: - # Could be smarter than this, but just send the nominal speed in intersection. - response.target_speed = self.nominal_speed_in_intersection - # Vehicle has to slow down and maybe stop. - response.arrival_time = self.earliest_free - - response.intersection_pos = self.intersection_pos - grant[i].set(response) - # Update earliest free on the assumption that the vehicle - # maintains its target speed (on average) within the intersection. - time_in_intersection = (BILLION * self.intersection_width) / (response.target_speed) - self.earliest_free = response.arrival_time + time_in_intersection - - print("*** RSU: Granted access to vehicle {} to enter at " - "time {} with average target velocity {} m/s. Next available time is {}".format( - i + 1, - response.arrival_time - lf.time.start(), - response.target_speed, - self.earliest_free - lf.time.start()) - ) - =} - - reaction(vehicle_reached_intersection) {= - sum_of_active_participants = 0 - for i in range(len(vehicle_reached_intersection)): - if vehicle_reached_intersection[i].is_present: - self.active_participants[i] = 0 - sum_of_active_participants += self.active_participants[i] - if sum_of_active_participants == 0: - # End the simulation if all vehicles have reached the intersection - request_stop() - print("\n********* SUCCESS: All vehicles have reached the intersection. *********\n") - =} + input[num_entries] request; + input[num_entries] vehicle_reached_intersection; + output[num_entries] grant; + + state earliest_free(0 msec); + state active_participants({=[0] * 20=}); + + reaction(request) -> grant {= + for i in range(self.num_entries): + if request[i].is_present: + self.active_participants[i] = 1 + if request[i].value.speed == 0: + # Avoid division by zero + request[i].value.speed = 0.001 + # Calculate the time it will take the approaching vehicle to + # arrive at its current speed. Note that this is + # time from the time the vehicle sends the message + # according to the arriving vehicle's clock. + speed_in_m_per_sec = request[i].value.speed + dr = self.intersection_pos.distance(request[i].value.current_pos) + print("*** RSU: Vehicle {}'s distance to intersection is {}.".format(i+1, dr)) + arrival_in = dr / speed_in_m_per_sec + + time_message_sent = lf.time.logical() + + # Convert the time interval to nsec (it is in seconds). + arrival_time_ns = time_message_sent + (arrival_in * BILLION) + + response = vehicle_grant() + if arrival_time_ns >= self.earliest_free: + # Vehicle can maintain speed. + response.target_speed = request[i].value.speed + response.arrival_time = arrival_time_ns + else: + # Could be smarter than this, but just send the nominal speed in intersection. + response.target_speed = self.nominal_speed_in_intersection + # Vehicle has to slow down and maybe stop. + response.arrival_time = self.earliest_free + + response.intersection_pos = self.intersection_pos + grant[i].set(response) + # Update earliest free on the assumption that the vehicle + # maintains its target speed (on average) within the intersection. + time_in_intersection = (BILLION * self.intersection_width) / (response.target_speed) + self.earliest_free = response.arrival_time + time_in_intersection + + print("*** RSU: Granted access to vehicle {} to enter at " + "time {} with average target velocity {} m/s. Next available time is {}".format( + i + 1, + response.arrival_time - lf.time.start(), + response.target_speed, + self.earliest_free - lf.time.start()) + ) + =} + + reaction(vehicle_reached_intersection) {= + sum_of_active_participants = 0 + for i in range(len(vehicle_reached_intersection)): + if vehicle_reached_intersection[i].is_present: + self.active_participants[i] = 0 + sum_of_active_participants += self.active_participants[i] + if sum_of_active_participants == 0: + # End the simulation if all vehicles have reached the intersection + request_stop() + print("\n********* SUCCESS: All vehicles have reached the intersection. *********\n") + =} } main reactor ( - num_vehicles(4), - positions({= [ # Direction (velocity vector) - coordinate(0.000038,-0.000674,2.794825), # /|\ - coordinate(-0.000501,-0.001084,2.794891), # -> - coordinate(-0.000060,-0.001510,2.794854), # \|/ - coordinate(0.000367,-0.001185,2.794846), # <- - ]=}), - initial_speeds({= \ - [ \ - vehicle_velocity(y = -8.0), vehicle_velocity(x = 8.0), \ - vehicle_velocity(y = 8.0), vehicle_velocity(x = -8.0) \ - ] - =}) + num_vehicles(4), + positions({= [ # Direction (velocity vector) + coordinate(0.000038,-0.000674,2.794825), # /|\ + coordinate(-0.000501,-0.001084,2.794891), # -> + coordinate(-0.000060,-0.001510,2.794854), # \|/ + coordinate(0.000367,-0.001185,2.794846), # <- + ]=}), + initial_speeds({= \ + [ \ + vehicle_velocity(y = -8.0), vehicle_velocity(x = 8.0), \ + vehicle_velocity(y = 8.0), vehicle_velocity(x = -8.0) \ + ] + =}) ) { - vehicles = new[num_vehicles] Vehicle(); - - rsu = new RSU( - num_entries = num_vehicles, - intersection_pos = {=coordinate(-0.000007632,-0.001124366,2.792485)=}, - intersection_width = 28, - nominal_speed_in_intersection = 14 - ); - vehicles.request -> rsu.request; - // Simulation will end once all vehicles have reached the intersection - vehicles.goal_reached -> rsu.vehicle_reached_intersection; - rsu.grant -> vehicles.grant; - - // Handle simulation - simulators = new[num_vehicles] Simulator( - initial_speeds = initial_speeds, - initial_speed = {=lambda self: self.initial_speeds[self.bank_index]=}, - positions = positions, - start_pos={=lambda self: self.positions[self.bank_index]=} - ) - - simulators.vehicle_stat -> vehicles.vehicle_stat; - simulators.vehicle_pos -> vehicles.vehicle_pos; - vehicles.control -> simulators.vehicle_command; + vehicles = new[num_vehicles] Vehicle(); + + rsu = new RSU( + num_entries = num_vehicles, + intersection_pos = {=coordinate(-0.000007632,-0.001124366,2.792485)=}, + intersection_width = 28, + nominal_speed_in_intersection = 14 + ); + vehicles.request -> rsu.request; + // Simulation will end once all vehicles have reached the intersection + vehicles.goal_reached -> rsu.vehicle_reached_intersection; + rsu.grant -> vehicles.grant; + + // Handle simulation + simulators = new[num_vehicles] Simulator( + initial_speeds = initial_speeds, + initial_speed = {=lambda self: self.initial_speeds[self.bank_index]=}, + positions = positions, + start_pos={=lambda self: self.positions[self.bank_index]=} + ) + + simulators.vehicle_stat -> vehicles.vehicle_stat; + simulators.vehicle_pos -> vehicles.vehicle_pos; + vehicles.control -> simulators.vehicle_command; } /** @@ -422,122 +422,122 @@ main reactor ( * for a simulator such as Carla. */ reactor Simulator( - interval(1 sec), // Note: For demonstration purposes, - // this interval is set to a very low frequency. - // Set the interval to 16 msecs for a more - // realistic simulation. - initial_speeds({=[]=}), - initial_speed({=vehicle_velocity(x = 12.0)=}), - positions({=[]=}), - start_pos({=coordinate(0.000042,-0.000701,2.794825)=}), - max_acceleration(4), // m/s/s - drag_acceleration({=-0.2=}) + interval(1 sec), // Note: For demonstration purposes, + // this interval is set to a very low frequency. + // Set the interval to 16 msecs for a more + // realistic simulation. + initial_speeds({=[]=}), + initial_speed({=vehicle_velocity(x = 12.0)=}), + positions({=[]=}), + start_pos({=coordinate(0.000042,-0.000701,2.794825)=}), + max_acceleration(4), // m/s/s + drag_acceleration({=-0.2=}) ){ + + input vehicle_command; + output vehicle_stat; + output vehicle_pos; + + state current_pos; + state current_velocity; + state current_throttle(0); + state current_brake(0); + state velocity_vector( + {={"x":0, "y":0}=} + ); + + reaction(startup) -> vehicle_stat, vehicle_pos {= + # Give initial values to the vehicle controller + self.current_pos = self.start_pos(self) + self.current_velocity = self.initial_speed(self) - input vehicle_command; - output vehicle_stat; - output vehicle_pos; + # For this simulation, we would like the vehicles to + # move in a straight line. Therefore, we calculate a + # velocity vector here and will keep it throughout the + # simulation. An improvement would be for the vehicles + # to be able to change directions at the intersection. + if not isclose(self.current_velocity.x, 0): + self.velocity_vector["x"] = self.current_velocity.x / abs(self.current_velocity.x) # Keep the sign - state current_pos; - state current_velocity; - state current_throttle(0); - state current_brake(0); - state velocity_vector( - {={"x":0, "y":0}=} - ); + if not isclose(self.current_velocity.y, 0): + self.velocity_vector["y"] = self.current_velocity.y / abs(self.current_velocity.y) # Keep the sign - reaction(startup) -> vehicle_stat, vehicle_pos {= - # Give initial values to the vehicle controller - self.current_pos = self.start_pos(self) - self.current_velocity = self.initial_speed(self) - - # For this simulation, we would like the vehicles to - # move in a straight line. Therefore, we calculate a - # velocity vector here and will keep it throughout the - # simulation. An improvement would be for the vehicles - # to be able to change directions at the intersection. - if not isclose(self.current_velocity.x, 0): - self.velocity_vector["x"] = self.current_velocity.x / abs(self.current_velocity.x) # Keep the sign - - if not isclose(self.current_velocity.y, 0): - self.velocity_vector["y"] = self.current_velocity.y / abs(self.current_velocity.y) # Keep the sign - - """ - print("Simulator: Vehicle {}:\n".format(self.bank_index + 1), - "Initial position: x={:f}, y={:f}, z={:f}\n".format( - self.current_pos.x, - self.current_pos.y, - self.current_pos.z - ), - "Initial velocity: x={:f}, y={:f}, z={:f}\n".format( - self.current_velocity.x, - self.current_velocity.y, - self.current_velocity.z - ), - "Velocity vector: x={:f}, y={:f}".format( - self.velocity_vector["x"], - self.velocity_vector["y"] - ) - ) - """ - - vehicle_pos.set(vehicle_position(self.current_pos)) - vehicle_stat.set(vehicle_status(self.current_velocity)) - =} - - timer tick(0, interval) - reaction(tick) -> vehicle_stat, vehicle_pos {= - previous_velocity = self.current_velocity - - # Linearly calculate an acceleration based on the value of throttle and brake - current_acceleration = self.current_throttle * self.max_acceleration - current_acceleration -= self.current_brake * self.max_acceleration - - # Apply a constant drag - current_acceleration -= self.drag_acceleration - - print("Simulator: Vehicle {}:\n".format(self.bank_index + 1), - "Current position: x={:f}, y={:f}, z={:f}\n".format( - self.current_pos.x, - self.current_pos.y, - self.current_pos.z - ), - "Current velocity: x={:f}, y={:f}, z={:f}\n".format( - self.current_velocity.x, - self.current_velocity.y, - self.current_velocity.z - ), - "Current acceleration: {:f}\n".format( - current_acceleration - ), - "Velocity vector: x={:f}, y={:f}".format( - self.velocity_vector["x"], - self.velocity_vector["y"] - ) - ) - - # Apply acceleration - self.current_velocity.x += (current_acceleration * (self.interval / BILLION)) \ - * self.velocity_vector["x"] # Keep the direction - self.current_velocity.y += (current_acceleration * (self.interval / BILLION)) \ - * self.velocity_vector["y"] # Keep the direction - - vehicle_stat.set(vehicle_status(self.current_velocity)) - - # Change the GPS position of the vehicle - x_move_meters = previous_velocity.x * self.interval / BILLION - y_move_meters = previous_velocity.y * self.interval / BILLION - - self.current_pos.x = self.current_pos.x + (180/pi)*(x_move_meters/6378137) - self.current_pos.y = self.current_pos.y + (180/pi)*(y_move_meters/6378137) - - vehicle_pos.set(vehicle_position(self.current_pos)) - =} + """ + print("Simulator: Vehicle {}:\n".format(self.bank_index + 1), + "Initial position: x={:f}, y={:f}, z={:f}\n".format( + self.current_pos.x, + self.current_pos.y, + self.current_pos.z + ), + "Initial velocity: x={:f}, y={:f}, z={:f}\n".format( + self.current_velocity.x, + self.current_velocity.y, + self.current_velocity.z + ), + "Velocity vector: x={:f}, y={:f}".format( + self.velocity_vector["x"], + self.velocity_vector["y"] + ) + ) + """ + + vehicle_pos.set(vehicle_position(self.current_pos)) + vehicle_stat.set(vehicle_status(self.current_velocity)) + =} + + timer tick(0, interval) + reaction(tick) -> vehicle_stat, vehicle_pos {= + previous_velocity = self.current_velocity + + # Linearly calculate an acceleration based on the value of throttle and brake + current_acceleration = self.current_throttle * self.max_acceleration + current_acceleration -= self.current_brake * self.max_acceleration + + # Apply a constant drag + current_acceleration -= self.drag_acceleration + + print("Simulator: Vehicle {}:\n".format(self.bank_index + 1), + "Current position: x={:f}, y={:f}, z={:f}\n".format( + self.current_pos.x, + self.current_pos.y, + self.current_pos.z + ), + "Current velocity: x={:f}, y={:f}, z={:f}\n".format( + self.current_velocity.x, + self.current_velocity.y, + self.current_velocity.z + ), + "Current acceleration: {:f}\n".format( + current_acceleration + ), + "Velocity vector: x={:f}, y={:f}".format( + self.velocity_vector["x"], + self.velocity_vector["y"] + ) + ) + + # Apply acceleration + self.current_velocity.x += (current_acceleration * (self.interval / BILLION)) \ + * self.velocity_vector["x"] # Keep the direction + self.current_velocity.y += (current_acceleration * (self.interval / BILLION)) \ + * self.velocity_vector["y"] # Keep the direction + + vehicle_stat.set(vehicle_status(self.current_velocity)) + + # Change the GPS position of the vehicle + x_move_meters = previous_velocity.x * self.interval / BILLION + y_move_meters = previous_velocity.y * self.interval / BILLION + self.current_pos.x = self.current_pos.x + (180/pi)*(x_move_meters/6378137) + self.current_pos.y = self.current_pos.y + (180/pi)*(y_move_meters/6378137) - reaction(vehicle_command) {= - # Update throttle and brake values - self.current_throttle = vehicle_command.value.throttle - self.current_brake = vehicle_command.value.brake - =} + vehicle_pos.set(vehicle_position(self.current_pos)) + =} + + + reaction(vehicle_command) {= + # Update throttle and brake values + self.current_throttle = vehicle_command.value.throttle + self.current_brake = vehicle_command.value.brake + =} } diff --git a/experimental/Python/src/Mining/BusyMine.lf b/experimental/Python/src/Mining/BusyMine.lf index fd1c0860..c64e100f 100644 --- a/experimental/Python/src/Mining/BusyMine.lf +++ b/experimental/Python/src/Mining/BusyMine.lf @@ -12,16 +12,16 @@ * * General info: * 1 - The different blocks represent different stations and - * tasks for the 'autonomous ground vehicle' to complete. - * Burgundy block- mining station - * Blue block- washing station - * Orange/yellow block- filtering station - * Gray block- storing station - * Green block- charging station + * tasks for the 'autonomous ground vehicle' to complete. + * Burgundy block- mining station + * Blue block- washing station + * Orange/yellow block- filtering station + * Gray block- storing station + * Green block- charging station * 2 - The AGV must complete the tasks in the order specified in the reactor's - * list, while simultaneously avoiding people and battery drainage. + * list, while simultaneously avoiding people and battery drainage. * 3 - A version of PhosphateMine.lf where the stations become busy. The agv - * must decide what to do in the meantime. Stations become red when busy. + * must decide what to do in the meantime. Stations become red when busy. * * * TODOs @@ -29,208 +29,208 @@ * 2- Make the demo logic more efficient if possible. * 3- Debug A-star search function instead of using euclidean distance. * 4- Add personalities for each person instead of following pre-determined - * directions. + * directions. * 5- Add modes for people (exploring, chasing, running away). * 6- Enable federated execution if possible. * 7- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the people see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the people see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] + files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbphosphate as mine - import AIPhosphate as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - people_specs = [ - { - "name": "Pinky", - "directions": mine.Pinky_directions, - "width": mine.w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": mine.Blinky_directions, - "width": mine.w, - "height": mine.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": mine.Inky_directions, - "width": mine.i_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": mine.Clyde_directions, - "width": mine.c_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbphosphate as mine + import AIPhosphate as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + people_specs = [ + { + "name": "Pinky", + "directions": mine.Pinky_directions, + "width": mine.w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": mine.Blinky_directions, + "width": mine.w, + "height": mine.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": mine.Inky_directions, + "width": mine.i_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": mine.Clyde_directions, + "width": mine.c_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) + mine.pygame.display.set_icon(agv_icon) + + self._clock = mine.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = mine.pygame.display.set_mode([606, 606]) + # Set the title of the window + mine.pygame.display.set_caption("Phosphate Mine") + # Create a surface we can draw on + background = mine.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(mine.white) + mine.pygame.font.init() + self._font = mine.pygame.font.Font("freesansbold.ttf", 16) + self._screen.fill(mine.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) - mine.pygame.display.set_icon(agv_icon) - - self._clock = mine.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = mine.pygame.display.set_mode([606, 606]) - # Set the title of the window - mine.pygame.display.set_caption("Phosphate Mine") - # Create a surface we can draw on - background = mine.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(mine.white) - mine.pygame.font.init() - self._font = mine.pygame.font.Font("freesansbold.ttf", 16) - self._screen.fill(mine.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(mine.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + mine.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + print("adding static sprites") + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + print(self._static_sprites) + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Processed Material: "+str(score.value), True, mine.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + + self._screen.fill(mine.white) + agv = mine.pygame.sprite.Sprite() + sprite_list = mine.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.ActionPlace): + #print("name is ", sprite.value.name) + #print("color is ", sprite.value.color) + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(mine.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - mine.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - print("adding static sprites") - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - print(self._static_sprites) - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Processed Material: "+str(score.value), True, mine.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - - self._screen.fill(mine.white) - agv = mine.pygame.sprite.Sprite() - sprite_list = mine.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.ActionPlace): - #print("name is ", sprite.value.name) - #print("color is ", sprite.value.color) - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 26]) - self._screen.blit(self._font.render("Cargo to wash: " + str(len(agv.wash_cargo)), True, mine.black), [10, 42]) - self._screen.blit(self._font.render("Cargo to filter: " + str(len(agv.filt_cargo)), True, mine.black), [10, 58]) - self._screen.blit(self._font.render("Cargo to store: " + str(len(agv.store_cargo)), True, mine.black), [10, 74]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - #print("paused") - text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, mine.black) - self._screen.blit(text3, [165, 333]) - mine.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Mining Complete!", True, mine.black) - else: - text1=self._font.render("Collision detected.", True, mine.black) - self._screen.blit(text1, [235, 233]) - #print("game is over") - text2=self._font.render("To play again, press ENTER.", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, mine.black) - self._screen.blit(text3, [165, 333]) - - mine.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 26]) + self._screen.blit(self._font.render("Cargo to wash: " + str(len(agv.wash_cargo)), True, mine.black), [10, 42]) + self._screen.blit(self._font.render("Cargo to filter: " + str(len(agv.filt_cargo)), True, mine.black), [10, 58]) + self._screen.blit(self._font.render("Cargo to store: " + str(len(agv.store_cargo)), True, mine.black), [10, 74]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + #print("paused") + text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, mine.black) + self._screen.blit(text3, [165, 333]) + mine.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Mining Complete!", True, mine.black) + else: + text1=self._font.render("Collision detected.", True, mine.black) + self._screen.blit(text1, [235, 233]) + #print("game is over") + text2=self._font.render("To play again, press ENTER.", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, mine.black) + self._screen.blit(text3, [165, 333]) + + mine.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -238,729 +238,729 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=mine.AGV=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=mine.Player=})) { - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor AGV(width(0), height(0), image("images/Trollman.png"), character_class({=mine.AGV=}), energy_cost(-0.25), charge_at(30), risk(120)) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - input mines - input wash - input filt - input store - input charger - - logical action schedule_filter - logical action schedule_wash - - state _people({=[]=}) - state _layout({=mine.walls=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _mines - state _wash - state _filt - state _store - state _charger - state _stations - state _pause({=False=}) - state _find_moves(0) - state _go_back({=False=}) - #state _event_list({=["mining", "washing", "filtering", "storing"]=}) - state _action({="mining"=}) - state _prev_action({=["mining"]=}) - state _scheduled(True) - - output sprite - output icon_name - output playerpause - output restart - output omines - output owash - output ofilt - output ostore - output ocharger - - method payCost() {= - #print("payCost last move is ", self.character_instance.last_move) - if self.character_instance.last_move is not [0, 0]: - self.character_instance.charge(self.energy_cost) + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + input mines + input wash + input filt + input store + input charger + + logical action schedule_filter + logical action schedule_wash + + state _people({=[]=}) + state _layout({=mine.walls=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _mines + state _wash + state _filt + state _store + state _charger + state _stations + state _pause({=False=}) + state _find_moves(0) + state _go_back({=False=}) + #state _event_list({=["mining", "washing", "filtering", "storing"]=}) + state _action({="mining"=}) + state _prev_action({=["mining"]=}) + state _scheduled(True) + + output sprite + output icon_name + output playerpause + output restart + output omines + output owash + output ofilt + output ostore + output ocharger + + method payCost() {= + #print("payCost last move is ", self.character_instance.last_move) + if self.character_instance.last_move is not [0, 0]: + self.character_instance.charge(self.energy_cost) + =} + + method setNextAction() {= + # temp = self._event_list[0] + # self._event_list = self._event_list[1:] + # self._event_list.append(temp) + #print("mine: ", self._mines.busy) + #print(len(self.character_instance.wash_cargo)) + + # if len(self.character_instance.store_cargo) > 6: + # self._action = "storing" + # elif self.character_instance.num_tot_cargo <= 3 or (self._wash.busy and self._filt.busy and len(self.character_instance.store_cargo) < 5): + # #print("mine selected") + # self._action = "mining" + # elif (len(self.character_instance.filt_cargo) >= 10 or len(self.character_instance.filt_cargo) > len(self.character_instance.wash_cargo)) and not self._filt.busy and len(self.character_instance.filt_cargo) > 0: + # self._action = "filtering" + # elif not self._wash.busy and len(self.character_instance.wash_cargo) > 0: + # self._action = "washing" + # else: + # self._action = "mining" + + busyness = self._wash.busy and self._filt.busy + agv_x = self.character_instance.rect.left + agv_y = self.character_instance.rect.top + to_wash = len(self.character_instance.wash_cargo) + to_filter = len(self.character_instance.filt_cargo) + to_store = len(self.character_instance.store_cargo) + if self.character_instance.num_tot_cargo <= 6 and busyness: + print("set to mining") + self._action = "mining" + elif not self._wash.busy and ai.euclid_dist(agv_x, agv_y, mine.washspot[0], mine.washspot[1]) <= 85 and (len(self._wash.cargo) > 0 or to_wash > 0): + self._action = "washing" + elif not self._filt.busy and ai.euclid_dist(agv_x, agv_y, mine.filterspot[0], mine.filterspot[1]) <= 85 and (len(self._filt.cargo) > 0 or to_filter > 0): + print("set to filtering") + self._action = "filtering" + elif (ai.euclid_dist(agv_x, agv_y, mine.storespot[0], mine.storespot[1]) <= 85 and to_store > 0) or to_store >= 20: + self._action = "storing" + elif ai.euclid_dist(agv_x, agv_y, mine.chargerspot[0], mine.chargerspot[1]) <= 50 and self.character_instance.battery is not 100 and self._prev_action[-1] is not "charging": + print("set to charging") + self._action = "charging" + elif not self._filt.busy and (to_filter > 0 or len(self._filt.cargo) > 0): + self._action = "filtering" + elif not self._wash.busy and (to_wash > 4 or len(self._wash.cargo) > 0): + self._action = "washing" + else: + self._action = "mining" + + =} + + initial mode export { + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._stations = mine.setupMineStations(_all_sprites_list) + + for station in self._stations: + if station.name == "mining": + self._mines = station + elif station.name == "washing": + self._wash = station + elif station.name == "filtering": + self._filt = station + elif station.name == "storing": + self._store = station + elif station.name == "charging": + self._charger = station + + self._mines.restart() + self._wash.restart() + self._filt.restart() + self._store.restart() + self._charger.restart() =} - - method setNextAction() {= - # temp = self._event_list[0] - # self._event_list = self._event_list[1:] - # self._event_list.append(temp) - #print("mine: ", self._mines.busy) - #print(len(self.character_instance.wash_cargo)) - - # if len(self.character_instance.store_cargo) > 6: - # self._action = "storing" - # elif self.character_instance.num_tot_cargo <= 3 or (self._wash.busy and self._filt.busy and len(self.character_instance.store_cargo) < 5): - # #print("mine selected") - # self._action = "mining" - # elif (len(self.character_instance.filt_cargo) >= 10 or len(self.character_instance.filt_cargo) > len(self.character_instance.wash_cargo)) and not self._filt.busy and len(self.character_instance.filt_cargo) > 0: - # self._action = "filtering" - # elif not self._wash.busy and len(self.character_instance.wash_cargo) > 0: - # self._action = "washing" - # else: - # self._action = "mining" - - busyness = self._wash.busy and self._filt.busy - agv_x = self.character_instance.rect.left - agv_y = self.character_instance.rect.top - to_wash = len(self.character_instance.wash_cargo) - to_filter = len(self.character_instance.filt_cargo) - to_store = len(self.character_instance.store_cargo) - if self.character_instance.num_tot_cargo <= 6 and busyness: - print("set to mining") - self._action = "mining" - elif not self._wash.busy and ai.euclid_dist(agv_x, agv_y, mine.washspot[0], mine.washspot[1]) <= 85 and (len(self._wash.cargo) > 0 or to_wash > 0): - self._action = "washing" - elif not self._filt.busy and ai.euclid_dist(agv_x, agv_y, mine.filterspot[0], mine.filterspot[1]) <= 85 and (len(self._filt.cargo) > 0 or to_filter > 0): - print("set to filtering") - self._action = "filtering" - elif (ai.euclid_dist(agv_x, agv_y, mine.storespot[0], mine.storespot[1]) <= 85 and to_store > 0) or to_store >= 20: - self._action = "storing" - elif ai.euclid_dist(agv_x, agv_y, mine.chargerspot[0], mine.chargerspot[1]) <= 50 and self.character_instance.battery is not 100 and self._prev_action[-1] is not "charging": - print("set to charging") - self._action = "charging" - elif not self._filt.busy and (to_filter > 0 or len(self._filt.cargo) > 0): - self._action = "filtering" - elif not self._wash.busy and (to_wash > 4 or len(self._wash.cargo) > 0): - self._action = "washing" - else: - self._action = "mining" - + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value =} - - initial mode export { - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._stations = mine.setupMineStations(_all_sprites_list) - - for station in self._stations: - if station.name == "mining": - self._mines = station - elif station.name == "washing": - self._wash = station - elif station.name == "filtering": - self._filt = station - elif station.name == "storing": - self._store = station - elif station.name == "charging": - self._charger = station + reaction(pygame_event) -> sprite, playerpause, restart, reset(Close), omines, owash, ofilt, ostore, ocharger {= + #print("action is ", self._action) + #print("prev action is ", self._prev_action[-1]) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self._action = "mining" + self._prev_action = ["mining"] + self.character_instance.zero_cargo() + self.character_instance.charge("full") self._mines.restart() self._wash.restart() self._filt.restart() self._store.restart() self._charger.restart() - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart, reset(Close), omines, owash, ofilt, ostore, ocharger {= - #print("action is ", self._action) - #print("prev action is ", self._prev_action[-1]) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self._action = "mining" - self._prev_action = ["mining"] - self.character_instance.zero_cargo() - self.character_instance.charge("full") - self._mines.restart() - self._wash.restart() - self._filt.restart() - self._store.restart() - self._charger.restart() - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - playerpause.set(self._pause) - omines.set(self._mines) - owash.set(self._wash) - ofilt.set(self._filt) - ostore.set(self._store) - ocharger.set(self._charger) - if not self._pause: - print("num tot cargo: ", self.character_instance.num_tot_cargo) - print("wash_cargo: ", len(self.character_instance.wash_cargo)) - print("filt_cargo: ", len(self.character_instance.filt_cargo)) - print("store_cargo: ", len(self.character_instance.store_cargo)) - print("in wash: ", len(self._wash.cargo)) - print("in filter: ", len(self._filt.cargo)) - print("in store: ", len(self._store.cargo)) - if self._action is not "charging": - self.setNextAction() - if self._action is not "mining": - self._mines.not_busy() - if self._action is not "storing": - self._store.not_busy() - if not self._pause and self.character_instance.battery > 0: #and self._active - if self._ai_control: - Close.set() - - =} - - reaction(people_sprites) {= - self._ghosts = [] - for person in people_sprites: - if person.is_present: - self._people.append(person.value) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} - - } mode Charge_check { - reaction(reset) -> reset(Location_check) {= - if self.character_instance.battery <= self.charge_at: - self._action = "charging" - Location_check.set() - =} - } mode Location_check { - reaction(reset) -> reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing), reset(findspot) {= - if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: - mining.set() - elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: - washing.set() - elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: - filtering.set() - elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: - storing.set() - elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargerspot: - charging.set() - else: - findspot.set() - - =} - } mode findspot { - reaction(reset) -> history(export) {= - for station in self._stations: - if station is not None and self._action == station.name: - #print("findspot: ", [station.rect.left, station.rect.top]) - self.character_instance.approach(self._layout, self._people, [station.rect.left, station.rect.top], self._find_moves) - # if self._action == "mining": - # self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) - # elif self._action == "charging": - # self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) - # elif self._action == "washing": - # self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) - # elif self._action == "filtering": - # self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) - # elif self._action == "storing": - # self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) - - self.payCost() - self._find_moves = self.character_instance.num_moves - export.set() - =} - } mode mining { - reaction(reset) -> history(export) {= - for i in range(1): - if randint(1, 10) > 5: - self.character_instance.wash_cargo.append(mine.Cargo("washing")) - self.character_instance.num_tot_cargo += 1 - #print("player mine color: ", self._mines.color) - self._scheduled = True - self._prev_action.append("mining") - self._mines.set_busy() - #self.setNextAction() - #print("next set action is ", self._action) - export.set() - =} - - } mode washing { - - reaction(reset) -> schedule_wash, history(export) {= - #print("in washing") - if len(self._wash.cargo) > 0: - print("unloading washed items") - for item in self._wash.cargo: - self.character_instance.filt_cargo.append(item) - self.character_instance.num_tot_cargo += 1 - self._wash.cargo = [] - - for item in self.character_instance.wash_cargo: - print("loading items to wash") - self._wash.cargo.append(mine.Cargo("filtering")) - self.character_instance.num_tot_cargo -= 1 - - self.character_instance.wash_cargo = [] - self._wash.set_busy() - self._scheduled = True - schedule_wash.schedule(SEC(randint(5, 7))) - self._prev_action.append("washing") - #self.setNextAction() - export.set() - =} - - } mode charging { - reaction(reset) -> history(export) {= - #print("in charging") - if self.character_instance.battery < 100: - self.character_instance.charge(1) - self._charger.set_busy() + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True else: - self._charger.not_busy() - # temp = self._action - # self._action = self._prev_action[-1] - self._prev_action.append("charging") - self.setNextAction() - #self._action, self._prev_action = self._prev_action, self._action - export.set() - =} - } mode filtering { - reaction(reset) -> schedule_filter, history(export) {= - #print("in filtering") - if len(self._filt.cargo) > 0: - for item in self._filt.cargo: - self.character_instance.store_cargo.append(item) - self.character_instance.num_tot_cargo += 1 - self._filt.cargo = [] - - for item in self.character_instance.filt_cargo: - print("loading items to filter") - self._filt.cargo.append(mine.Cargo("storing")) - self.character_instance.num_tot_cargo -= 1 - - self.character_instance.filt_cargo = [] - self._filt.set_busy() - schedule_filter.schedule(SEC(randint(5, 7))) - self._scheduled = True - self._prev_action.append("filtering") - #self.setNextAction() - export.set() - =} - } mode storing { - reaction(reset) -> history(export) {= - #print("in storing") - self._scheduled = True - self._store.set_busy() - self._prev_action.append("storing") - #self.setNextAction() - self.character_instance.store_processed() - self.character_instance.store_cargo = [] - - export.set() - =} - } mode Close { - - reaction(reset) -> reset(Charge_check), reset(Avoid), history(export) {= - #print("in mode close") - if len(self._people) > 0: - #if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: - if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: - Charge_check.set() - else: - Avoid.set() - else: - export.set() - =} - - } mode Avoid { - - reaction(reset) -> history(export) {= - #print("avoid time") - self.character_instance.ai_avoid(self._layout, self._people, 7) - self.payCost() - #self._avoid_moves = self.character_instance.get_num_moves() - export.set() - =} - - } - - reaction(schedule_filter) {= - self._filt.not_busy() + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + playerpause.set(self._pause) + omines.set(self._mines) + owash.set(self._wash) + ofilt.set(self._filt) + ostore.set(self._store) + ocharger.set(self._charger) + if not self._pause: + print("num tot cargo: ", self.character_instance.num_tot_cargo) + print("wash_cargo: ", len(self.character_instance.wash_cargo)) + print("filt_cargo: ", len(self.character_instance.filt_cargo)) + print("store_cargo: ", len(self.character_instance.store_cargo)) + print("in wash: ", len(self._wash.cargo)) + print("in filter: ", len(self._filt.cargo)) + print("in store: ", len(self._store.cargo)) + if self._action is not "charging": + self.setNextAction() + if self._action is not "mining": + self._mines.not_busy() + if self._action is not "storing": + self._store.not_busy() + if not self._pause and self.character_instance.battery > 0: #and self._active + if self._ai_control: + Close.set() + =} - reaction(schedule_wash) {= - self._wash.not_busy() + reaction(people_sprites) {= + self._ghosts = [] + for person in people_sprites: + if person.is_present: + self._people.append(person.value) =} -} -## Ghosts -# FIXME: Different Ghosts should have different personalities -reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} - reaction(game_over) {= - self._active = False + self._active = False + self._pause = True + self.character_instance.speedzero() =} - reaction(restart) {= - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) + } mode Charge_check { + reaction(reset) -> reset(Location_check) {= + if self.character_instance.battery <= self.charge_at: + self._action = "charging" + Location_check.set() =} -} - -#### Controller -reactor GameController(number_of_people(4)) { - output wall_list # List of walls on the map - output gate - output mines - output charger - output wash - output filt - output store - output score # The game score - output game_over - - input[number_of_people] people_sprites - input agv_sprite - input tick # The game tick - input restart - input in_mines - input in_charger - input in_wash - input in_filt - input in_store - - state _wall_list - state _stations_list - state _gate - state _mine - state _charger - state _wash - state _filter - state _store - state _score_to_win(100) - state _score(0) - state _agv_sprite - state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - - reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._wall_list = mine.setupMineWalls(_all_sprites_list) - self._gate = mine.setupGate(_all_sprites_list) - #self._stations_list = mine.setupMineStations(_all_sprites_list) - # self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) - # self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) - # self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) - # self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) - # self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) - wall_list.set(self._wall_list) - gate.set(self._gate) - - # for station in self._stations_list: - # if station.name == "mining": - # self._mine = station - # mines.set(station) - # elif station.name == "washing": - # self._wash = station - # wash.set(station) - # elif station.name == "filtering": - # self._filter = station - # filt.set(station) - # elif station.name == "storing": - # self._store = station - # store.set(station) - # elif station.name == "charging": - # self._charger = station - # charger.set(station) - # mines.set(self._mine) - # charger.set(self._charger) - # wash.set(self._wash) - # filt.set(self._filter) - # store.set(self._store) + } mode Location_check { + reaction(reset) -> reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing), reset(findspot) {= + if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: + mining.set() + elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: + washing.set() + elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: + filtering.set() + elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: + storing.set() + elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargerspot: + charging.set() + else: + findspot.set() + =} - - reaction(agv_sprite) {= - self._agv_collide.empty() - self._agv_collide.add(agv_sprite.value) - self._agv_sprite = agv_sprite.value + } mode findspot { + reaction(reset) -> history(export) {= + for station in self._stations: + if station is not None and self._action == station.name: + #print("findspot: ", [station.rect.left, station.rect.top]) + self.character_instance.approach(self._layout, self._people, [station.rect.left, station.rect.top], self._find_moves) + # if self._action == "mining": + # self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) + # elif self._action == "charging": + # self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) + # elif self._action == "washing": + # self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) + # elif self._action == "filtering": + # self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) + # elif self._action == "storing": + # self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) + + self.payCost() + self._find_moves = self.character_instance.num_moves + export.set() =} - - reaction(agv_sprite) -> score, game_over {= - if self._score == self._score_to_win: - game_over.set(True) - - score.set(agv_sprite.value.total_stored) + } mode mining { + reaction(reset) -> history(export) {= + for i in range(1): + if randint(1, 10) > 5: + self.character_instance.wash_cargo.append(mine.Cargo("washing")) + self.character_instance.num_tot_cargo += 1 + #print("player mine color: ", self._mines.color) + self._scheduled = True + self._prev_action.append("mining") + self._mines.set_busy() + #self.setNextAction() + #print("next set action is ", self._action) + export.set() =} - reaction(people_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = mine.pygame.sprite.RenderPlain() - for person in people_sprites: - if person.is_present: - monsta_list.add(person.value) - - monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set(False) - + } mode washing { + + reaction(reset) -> schedule_wash, history(export) {= + #print("in washing") + if len(self._wash.cargo) > 0: + print("unloading washed items") + for item in self._wash.cargo: + self.character_instance.filt_cargo.append(item) + self.character_instance.num_tot_cargo += 1 + self._wash.cargo = [] + + for item in self.character_instance.wash_cargo: + print("loading items to wash") + self._wash.cargo.append(mine.Cargo("filtering")) + self.character_instance.num_tot_cargo -= 1 + + self.character_instance.wash_cargo = [] + self._wash.set_busy() + self._scheduled = True + schedule_wash.schedule(SEC(randint(5, 7))) + self._prev_action.append("washing") + #self.setNextAction() + export.set() =} - reaction(in_mines, in_wash, in_filt, in_store, in_charger) -> mines, charger, wash, filt, store {= - if in_mines.is_present: - #print("mine updated") - self._mine = in_mines.value - if in_wash.is_present: - self._wash = in_wash.value - if in_filt.is_present: - self._filter = in_filt.value - if in_charger.is_present: - self._charger = in_charger.value - if in_store.is_present: - self._store = in_store.value - self._stations_list = [self._mine, self._wash, self._filter, self._store, self._charger] - - for station in self._stations_list: - if station.name == "mining": - self._mine = station - mines.set(station) - elif station.name == "washing": - self._wash = station - wash.set(station) - elif station.name == "filtering": - self._filter = station - filt.set(station) - elif station.name == "storing": - self._store = station - store.set(station) - elif station.name == "charging": - self._charger = station - charger.set(station) + } mode charging { + reaction(reset) -> history(export) {= + #print("in charging") + if self.character_instance.battery < 100: + self.character_instance.charge(1) + self._charger.set_busy() + else: + self._charger.not_busy() + # temp = self._action + # self._action = self._prev_action[-1] + self._prev_action.append("charging") + self.setNextAction() + #self._action, self._prev_action = self._prev_action, self._action + export.set() =} - - reaction(restart) {= - if restart.value: - - self._score_to_win = 100 - self._score = 0 + } mode filtering { + reaction(reset) -> schedule_filter, history(export) {= + #print("in filtering") + if len(self._filt.cargo) > 0: + for item in self._filt.cargo: + self.character_instance.store_cargo.append(item) + self.character_instance.num_tot_cargo += 1 + self._filt.cargo = [] + + for item in self.character_instance.filt_cargo: + print("loading items to filter") + self._filt.cargo.append(mine.Cargo("storing")) + self.character_instance.num_tot_cargo -= 1 + + self.character_instance.filt_cargo = [] + self._filt.set_busy() + schedule_filter.schedule(SEC(randint(5, 7))) + self._scheduled = True + self._prev_action.append("filtering") + #self.setNextAction() + export.set() =} - - reaction(shutdown) {= - mine.pygame.quit() + } mode storing { + reaction(reset) -> history(export) {= + #print("in storing") + self._scheduled = True + self._store.set_busy() + self._prev_action.append("storing") + #self.setNextAction() + self.character_instance.store_processed() + self.character_instance.store_cargo = [] + + export.set() + =} + } mode Close { + + reaction(reset) -> reset(Charge_check), reset(Avoid), history(export) {= + #print("in mode close") + if len(self._people) > 0: + #if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: + if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: + Charge_check.set() + else: + Avoid.set() + else: + export.set() =} -} + } mode Avoid { -main reactor { - ### Controller - controller = new GameController() + reaction(reset) -> history(export) {= + #print("avoid time") + self.character_instance.ai_avoid(self._layout, self._people, 7) + self.payCost() + #self._avoid_moves = self.character_instance.get_num_moves() + export.set() + =} - ### Model(s) - agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") - #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" - - # Ghosts - peoples = new[4] People( - width = {= people_specs[bank_index]["width"] =}, - height = {= people_specs[bank_index]["height"] =}, - directions = {= people_specs[bank_index]["directions"] =}, - name = {= people_specs[bank_index]["name"] =}, - character_class = ({=mine.People=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 10, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> peoples.wall_list - - (controller.wall_list, controller.gate)+ -> - agv.wall_list, agv.gate_list - + } - agv.omines, agv.owash, agv.ofilt, agv.ostore, agv.ocharger -> - controller.in_mines, controller.in_wash, controller.in_filt, - controller.in_store, controller.in_charger - // controller.mines, controller.wash, controller.filt, controller.store, - // controller.charger -> agv.mines, agv.wash, agv.filt, agv.store, agv.charger - - # Send the sprites to the display to be drawn - #controller.block_list - agv.sprite, - peoples.sprite, controller.mines, - controller.charger, controller.wash, controller.filt, controller.store -> - display.moving_sprites - - controller.wall_list, controller.gate -> - display.static_sprites - + reaction(schedule_filter) {= + self._filt.not_busy() + =} - (display.tick)+ -> - controller.tick, - peoples.tick + reaction(schedule_wash) {= + self._wash.not_busy() + =} +} + +## Ghosts +# FIXME: Different Ghosts should have different personalities +reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} +} - #Send pause player to game controller and ghosts - (agv.playerpause)+ -> peoples.playerpause, display.playerpause +#### Controller +reactor GameController(number_of_people(4)) { + output wall_list # List of walls on the map + output gate + output mines + output charger + output wash + output filt + output store + output score # The game score + output game_over + + input[number_of_people] people_sprites + input agv_sprite + input tick # The game tick + input restart + input in_mines + input in_charger + input in_wash + input in_filt + input in_store - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause + state _wall_list + state _stations_list + state _gate + state _mine + state _charger + state _wash + state _filter + state _store + state _score_to_win(100) + state _score(0) + state _agv_sprite + state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + + reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._wall_list = mine.setupMineWalls(_all_sprites_list) + self._gate = mine.setupGate(_all_sprites_list) + #self._stations_list = mine.setupMineStations(_all_sprites_list) + # self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) + # self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) + # self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) + # self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) + # self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) + wall_list.set(self._wall_list) + gate.set(self._gate) + + # for station in self._stations_list: + # if station.name == "mining": + # self._mine = station + # mines.set(station) + # elif station.name == "washing": + # self._wash = station + # wash.set(station) + # elif station.name == "filtering": + # self._filter = station + # filt.set(station) + # elif station.name == "storing": + # self._store = station + # store.set(station) + # elif station.name == "charging": + # self._charger = station + # charger.set(station) + # mines.set(self._mine) + # charger.set(self._charger) + # wash.set(self._wash) + # filt.set(self._filter) + # store.set(self._store) + =} + + reaction(agv_sprite) {= + self._agv_collide.empty() + self._agv_collide.add(agv_sprite.value) + self._agv_sprite = agv_sprite.value + =} + + reaction(agv_sprite) -> score, game_over {= + if self._score == self._score_to_win: + game_over.set(True) + + score.set(agv_sprite.value.total_stored) + =} + + reaction(people_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = mine.pygame.sprite.RenderPlain() + for person in people_sprites: + if person.is_present: + monsta_list.add(person.value) - agv.sprite -> controller.agv_sprite - - (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites + monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) + + if monsta_hit_list: + game_over.set(False) + + =} + + reaction(in_mines, in_wash, in_filt, in_store, in_charger) -> mines, charger, wash, filt, store {= + if in_mines.is_present: + #print("mine updated") + self._mine = in_mines.value + if in_wash.is_present: + self._wash = in_wash.value + if in_filt.is_present: + self._filter = in_filt.value + if in_charger.is_present: + self._charger = in_charger.value + if in_store.is_present: + self._store = in_store.value + self._stations_list = [self._mine, self._wash, self._filter, self._store, self._charger] + + for station in self._stations_list: + if station.name == "mining": + self._mine = station + mines.set(station) + elif station.name == "washing": + self._wash = station + wash.set(station) + elif station.name == "filtering": + self._filter = station + filt.set(station) + elif station.name == "storing": + self._store = station + store.set(station) + elif station.name == "charging": + self._charger = station + charger.set(station) + =} - controller.score -> display.score + reaction(restart) {= + if restart.value: + + self._score_to_win = 100 + self._score = 0 + =} + + reaction(shutdown) {= + mine.pygame.quit() + =} - peoples.icon_name, agv.icon_name -> display.icon_name +} - display.icon -> peoples.icon, agv.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over - - (agv.restart)+ -> controller.restart, display.restart, peoples.restart - - - #controller.block_list -> agv.block_list - +main reactor { + ### Controller + controller = new GameController() + + ### Model(s) + agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") + #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" + + # Ghosts + peoples = new[4] People( + width = {= people_specs[bank_index]["width"] =}, + height = {= people_specs[bank_index]["height"] =}, + directions = {= people_specs[bank_index]["directions"] =}, + name = {= people_specs[bank_index]["name"] =}, + character_class = ({=mine.People=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 10, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> peoples.wall_list + + (controller.wall_list, controller.gate)+ -> + agv.wall_list, agv.gate_list + + + agv.omines, agv.owash, agv.ofilt, agv.ostore, agv.ocharger -> + controller.in_mines, controller.in_wash, controller.in_filt, + controller.in_store, controller.in_charger + // controller.mines, controller.wash, controller.filt, controller.store, + // controller.charger -> agv.mines, agv.wash, agv.filt, agv.store, agv.charger + + # Send the sprites to the display to be drawn + #controller.block_list + agv.sprite, + peoples.sprite, controller.mines, + controller.charger, controller.wash, controller.filt, controller.store -> + display.moving_sprites + + controller.wall_list, controller.gate -> + display.static_sprites + + + (display.tick)+ -> + controller.tick, + peoples.tick + + #Send pause player to game controller and ghosts + (agv.playerpause)+ -> peoples.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + agv.sprite -> controller.agv_sprite + + (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites + + controller.score -> display.score + + peoples.icon_name, agv.icon_name -> display.icon_name + + display.icon -> peoples.icon, agv.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over + + (agv.restart)+ -> controller.restart, display.restart, peoples.restart + + + #controller.block_list -> agv.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Mining/MineTest.lf b/experimental/Python/src/Mining/MineTest.lf index 83416bb2..3e053f98 100644 --- a/experimental/Python/src/Mining/MineTest.lf +++ b/experimental/Python/src/Mining/MineTest.lf @@ -13,205 +13,205 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] + files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbphosphate as mine - import AIPhosphate as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - people_specs = [ - { - "name": "Pinky", - "directions": mine.Pinky_directions, - "width": mine.w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": mine.Blinky_directions, - "width": mine.w, - "height": mine.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": mine.Inky_directions, - "width": mine.i_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": mine.Clyde_directions, - "width": mine.c_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbphosphate as mine + import AIPhosphate as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + people_specs = [ + { + "name": "Pinky", + "directions": mine.Pinky_directions, + "width": mine.w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": mine.Blinky_directions, + "width": mine.w, + "height": mine.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": mine.Inky_directions, + "width": mine.i_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": mine.Clyde_directions, + "width": mine.c_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) + mine.pygame.display.set_icon(agv_icon) + + self._clock = mine.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = mine.pygame.display.set_mode([606, 606]) + # Set the title of the window + mine.pygame.display.set_caption("Phosphate Mine") + # Create a surface we can draw on + background = mine.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(mine.white) + mine.pygame.font.init() + self._font = mine.pygame.font.Font("freesansbold.ttf", 18) + self._screen.fill(mine.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) - mine.pygame.display.set_icon(agv_icon) - - self._clock = mine.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = mine.pygame.display.set_mode([606, 606]) - # Set the title of the window - mine.pygame.display.set_caption("Phosphate Mine") - # Create a surface we can draw on - background = mine.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(mine.white) - mine.pygame.font.init() - self._font = mine.pygame.font.Font("freesansbold.ttf", 18) - self._screen.fill(mine.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(mine.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + mine.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + print(self._static_sprites) + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, mine.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(mine.white) + agv = mine.pygame.sprite.Sprite() + sprite_list = mine.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + #print("agv battery is ", agv.battery) + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(mine.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - mine.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - print(self._static_sprites) - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, mine.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(mine.white) - agv = mine.pygame.sprite.Sprite() - sprite_list = mine.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - #print("agv battery is ", agv.battery) - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, mine.black) - self._screen.blit(text3, [165, 333]) - mine.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Area Cleared!", True, mine.black) - else: - text1=self._font.render("Collision detected.", True, mine.black) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER.", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, mine.black) - self._screen.blit(text3, [165, 333]) - - mine.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, mine.black) + self._screen.blit(text3, [165, 333]) + mine.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Area Cleared!", True, mine.black) + else: + text1=self._font.render("Collision detected.", True, mine.black) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER.", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, mine.black) + self._screen.blit(text3, [165, 333]) + + mine.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -219,443 +219,443 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=mine.AGV=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=mine.Player=})) { - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor AGV(width(0), height(0), image("images/Trollman.png"), character_class({=mine.AGV=}), energy_cost(-0.5)) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - - state _people({=[]=}) - state _layout({=mine.walls=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _eat_moves(0) - state _action({="mining"=}) - - output sprite - output icon_name - output playerpause - output restart - - initial mode export { - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= - #print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - #print("setting sprite") - #print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0: #and self._active - if self._ai_control: - Close.set() - - =} - - reaction(people_sprites) {= - self._ghosts = [] - for person in people_sprites: - if person.is_present: - self._people.append(person.value) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} - - } mode Charge_check { - reaction(reset) -> reset(Close) {= - if self.character_instance.battery <= self.charge_at: - self._action = "charging" - Close.set() - =} - } mode Location_check { - reaction(reset) -> reset(findspot) {= - if self._action == "mining" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.minespot: - mining.set() - elif self._action == "washing" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.washspot: - washing.set() - elif self._action == "filtering" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.filterspot: - filtering.set() - elif self._action == "storing" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.storespot: - storing.set() - elif self._action == "storing" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.storespot: - charging.set() - else: - findspot.set() - =} - } mode findspot { - reaction(startup) -> reset(Location_check) {= - # implement to go to intended location - cond = False - if cond: - Location_check.set() - =} - } mode Close { - - reaction(reset) -> reset(Avoid), history(export) {= - #print("in mode close") - if len(self._people) > 0: - #if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: - if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > 180: - export.set() - else: - #print("ghost close") - Avoid.set() - else: - export.set() - =} - - } mode Avoid { - - reaction(reset) -> history(export) {= - #print("avoid time") - self.character_instance.ai_avoid(self._layout, self._people, 7) - if self.character_instance.last_move is not [0, 0]: - self.character_instance.charge(self.energy_cost) - #self._avoid_moves = self.character_instance.get_num_moves() - export.set() - =} - - } -} + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + + state _people({=[]=}) + state _layout({=mine.walls=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _eat_moves(0) + state _action({="mining"=}) + + output sprite + output icon_name + output playerpause + output restart + + initial mode export { + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} -## Ghosts -# FIXME: Different Ghosts should have different personalities -reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value + reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= + #print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + #print("setting sprite") + #print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0: #and self._active + if self._ai_control: + Close.set() + =} - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) + reaction(people_sprites) {= + self._ghosts = [] + for person in people_sprites: + if person.is_present: + self._people.append(person.value) =} - + reaction(game_over) {= - self._active = False + self._active = False + self._pause = True + self.character_instance.speedzero() =} - reaction(restart) {= - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) + } mode Charge_check { + reaction(reset) -> reset(Close) {= + if self.character_instance.battery <= self.charge_at: + self._action = "charging" + Close.set() =} -} - -#### Controller -reactor GameController(number_of_people(4)) { - output wall_list # List of walls on the map - output gate - output mines - output charger - output wash - output filt - output store - output score # The game score - output game_over - - input[number_of_people] people_sprites - input agv_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _mine - state _charger - state _wash - state _filter - state _store - state _score_to_win(0) - state _score(0) - state _agv_sprite - state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - - reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._wall_list = mine.setupMineWalls(_all_sprites_list) - self._gate = mine.setupGate(_all_sprites_list) - self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) - self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) - self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) - self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) - self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) - wall_list.set(self._wall_list) - gate.set(self._gate) - mines.set(self._mine) - charger.set(self._charger) - wash.set(self._wash) - filt.set(self._filter) - store.set(self._store) + } mode Location_check { + reaction(reset) -> reset(findspot) {= + if self._action == "mining" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.minespot: + mining.set() + elif self._action == "washing" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.washspot: + washing.set() + elif self._action == "filtering" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.filterspot: + filtering.set() + elif self._action == "storing" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.storespot: + storing.set() + elif self._action == "storing" and [self.character_instance.rect.left, self.character_instance.rect.top] == mine.storespot: + charging.set() + else: + findspot.set() =} - - reaction(agv_sprite) {= - self._agv_collide.empty() - self._agv_collide.add(agv_sprite.value) - self._agv_sprite = agv_sprite.value + } mode findspot { + reaction(startup) -> reset(Location_check) {= + # implement to go to intended location + cond = False + if cond: + Location_check.set() =} - - reaction(agv_sprite) -> score, game_over {= - # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - - # Check the list of collisions. - #if len(blocks_hit_list) > 0: - # self._score +=len(blocks_hit_list) - - #if self._score == self._score_to_win: - # game_over.set(True) - - score.set(self._score) + } mode Close { + + reaction(reset) -> reset(Avoid), history(export) {= + #print("in mode close") + if len(self._people) > 0: + #if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: + if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > 180: + export.set() + else: + #print("ghost close") + Avoid.set() + else: + export.set() =} - reaction(people_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = mine.pygame.sprite.RenderPlain() - for person in people_sprites: - if person.is_present: - monsta_list.add(person.value) - - monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set(False) - - =} + } mode Avoid { - - reaction(restart) {= - if restart.value: - - self._score_to_win = 5 - #print(self._energizer_list) - self._score = 0 - =} - - reaction(shutdown) {= - mine.pygame.quit() + reaction(reset) -> history(export) {= + #print("avoid time") + self.character_instance.ai_avoid(self._layout, self._people, 7) + if self.character_instance.last_move is not [0, 0]: + self.character_instance.charge(self.energy_cost) + #self._avoid_moves = self.character_instance.get_num_moves() + export.set() =} + } } -main reactor { - ### Controller - controller = new GameController() +## Ghosts +# FIXME: Different Ghosts should have different personalities +reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} +} - ### Model(s) - agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") - #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" - - # Ghosts - peoples = new[4] People( - width = {= people_specs[bank_index]["width"] =}, - height = {= people_specs[bank_index]["height"] =}, - directions = {= people_specs[bank_index]["directions"] =}, - name = {= people_specs[bank_index]["name"] =}, - character_class = ({=mine.People=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> peoples.wall_list - - # Send the sprites to the display to be drawn - #controller.block_list - agv.sprite, - peoples.sprite -> - display.moving_sprites +#### Controller +reactor GameController(number_of_people(4)) { + output wall_list # List of walls on the map + output gate + output mines + output charger + output wash + output filt + output store + output score # The game score + output game_over + + input[number_of_people] people_sprites + input agv_sprite + input tick # The game tick + input restart - (controller.wall_list, controller.gate)+ -> - agv.wall_list, agv.gate_list + state _wall_list + state _gate + state _mine + state _charger + state _wash + state _filter + state _store + state _score_to_win(0) + state _score(0) + state _agv_sprite + state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + + reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._wall_list = mine.setupMineWalls(_all_sprites_list) + self._gate = mine.setupGate(_all_sprites_list) + self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) + self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) + self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) + self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) + self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) + wall_list.set(self._wall_list) + gate.set(self._gate) + mines.set(self._mine) + charger.set(self._charger) + wash.set(self._wash) + filt.set(self._filter) + store.set(self._store) + =} + + reaction(agv_sprite) {= + self._agv_collide.empty() + self._agv_collide.add(agv_sprite.value) + self._agv_sprite = agv_sprite.value + =} + + reaction(agv_sprite) -> score, game_over {= + # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) + + # Check the list of collisions. + #if len(blocks_hit_list) > 0: + # self._score +=len(blocks_hit_list) + + #if self._score == self._score_to_win: + # game_over.set(True) + + score.set(self._score) + =} + + reaction(people_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = mine.pygame.sprite.RenderPlain() + for person in people_sprites: + if person.is_present: + monsta_list.add(person.value) - controller.wall_list, controller.gate, controller.mines, - controller.charger, controller.wash, controller.filt, controller.store -> - display.static_sprites + monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - (display.tick)+ -> - controller.tick, - peoples.tick + if monsta_hit_list: + game_over.set(False) + + =} - #Send pause player to game controller and ghosts - (agv.playerpause)+ -> peoples.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - agv.sprite -> controller.agv_sprite - - (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites - controller.score -> display.score + reaction(restart) {= + if restart.value: + + self._score_to_win = 5 + #print(self._energizer_list) + self._score = 0 + =} + + reaction(shutdown) {= + mine.pygame.quit() + =} - peoples.icon_name, agv.icon_name -> display.icon_name +} - display.icon -> peoples.icon, agv.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over - - (agv.restart)+ -> controller.restart, display.restart, peoples.restart - - #controller.block_list -> agv.block_list +main reactor { + ### Controller + controller = new GameController() + + ### Model(s) + agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") + #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" + + # Ghosts + peoples = new[4] People( + width = {= people_specs[bank_index]["width"] =}, + height = {= people_specs[bank_index]["height"] =}, + directions = {= people_specs[bank_index]["directions"] =}, + name = {= people_specs[bank_index]["name"] =}, + character_class = ({=mine.People=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> peoples.wall_list + + # Send the sprites to the display to be drawn + #controller.block_list + agv.sprite, + peoples.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + agv.wall_list, agv.gate_list + + controller.wall_list, controller.gate, controller.mines, + controller.charger, controller.wash, controller.filt, controller.store -> + display.static_sprites + + (display.tick)+ -> + controller.tick, + peoples.tick + + #Send pause player to game controller and ghosts + (agv.playerpause)+ -> peoples.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + agv.sprite -> controller.agv_sprite + + (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites + + controller.score -> display.score + + peoples.icon_name, agv.icon_name -> display.icon_name + + display.icon -> peoples.icon, agv.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over + + (agv.restart)+ -> controller.restart, display.restart, peoples.restart + #controller.block_list -> agv.block_list + } diff --git a/experimental/Python/src/Mining/MineWObserver.lf b/experimental/Python/src/Mining/MineWObserver.lf index 0455640b..567a9a12 100644 --- a/experimental/Python/src/Mining/MineWObserver.lf +++ b/experimental/Python/src/Mining/MineWObserver.lf @@ -13,208 +13,208 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] + files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbphosphate as mine - import AIPhosphate as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - people_specs = [ - { - "name": "Pinky", - "directions": mine.Pinky_directions, - "width": mine.w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": mine.Blinky_directions, - "width": mine.w, - "height": mine.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": mine.Inky_directions, - "width": mine.i_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": mine.Clyde_directions, - "width": mine.c_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbphosphate as mine + import AIPhosphate as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + people_specs = [ + { + "name": "Pinky", + "directions": mine.Pinky_directions, + "width": mine.w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": mine.Blinky_directions, + "width": mine.w, + "height": mine.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": mine.Inky_directions, + "width": mine.i_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": mine.Clyde_directions, + "width": mine.c_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) + mine.pygame.display.set_icon(agv_icon) + + self._clock = mine.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = mine.pygame.display.set_mode([606, 606]) + # Set the title of the window + mine.pygame.display.set_caption("Phosphate Mine") + # Create a surface we can draw on + background = mine.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(mine.white) + mine.pygame.font.init() + self._font = mine.pygame.font.Font("freesansbold.ttf", 18) + self._screen.fill(mine.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) - mine.pygame.display.set_icon(agv_icon) - - self._clock = mine.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = mine.pygame.display.set_mode([606, 606]) - # Set the title of the window - mine.pygame.display.set_caption("Phosphate Mine") - # Create a surface we can draw on - background = mine.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(mine.white) - mine.pygame.font.init() - self._font = mine.pygame.font.Font("freesansbold.ttf", 18) - self._screen.fill(mine.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(mine.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + mine.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + print("adding static sprites") + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + print(self._static_sprites) + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Mined Material: "+str(score.value), True, mine.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + + #print("adding moving sprites") + self._screen.fill(mine.white) + agv = mine.pygame.sprite.Sprite() + sprite_list = mine.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + #print("agv battery is ", agv.battery) + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(mine.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - mine.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - print("adding static sprites") - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - print(self._static_sprites) - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Mined Material: "+str(score.value), True, mine.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - - #print("adding moving sprites") - self._screen.fill(mine.white) - agv = mine.pygame.sprite.Sprite() - sprite_list = mine.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - #print("agv battery is ", agv.battery) - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - print("paused") - text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, mine.black) - self._screen.blit(text3, [165, 333]) - mine.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Mining Complete!", True, mine.black) - else: - text1=self._font.render("Collision detected.", True, mine.black) - self._screen.blit(text1, [235, 233]) - print("game is over") - text2=self._font.render("To play again, press ENTER.", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, mine.black) - self._screen.blit(text3, [165, 333]) - - mine.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + print("paused") + text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, mine.black) + self._screen.blit(text3, [165, 333]) + mine.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Mining Complete!", True, mine.black) + else: + text1=self._font.render("Collision detected.", True, mine.black) + self._screen.blit(text1, [235, 233]) + print("game is over") + text2=self._font.render("To play again, press ENTER.", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, mine.black) + self._screen.blit(text3, [165, 333]) + + mine.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -222,582 +222,582 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=mine.AGV=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } reactor Observer(risk(160)) { - input[5] moving_sprites - state _layout({=mine.walls=}) - output warning - - reaction(moving_sprites) -> warning {= - agv = None - people = [] - - for sprite in moving_sprites: - if isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - else: - people.append(sprite.value) - - if ai.euclid_close_people(people, agv.rect.left, agv.rect.top)[1] <= self.risk: - path = ai.peopleavoid(self._layout, people, agv.rect.left, agv.rect.top) - warning.set([path[0][0], path[0][1]]) - else: - warning.set([]) - =} + input[5] moving_sprites + state _layout({=mine.walls=}) + output warning + + reaction(moving_sprites) -> warning {= + agv = None + people = [] + + for sprite in moving_sprites: + if isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + else: + people.append(sprite.value) + + if ai.euclid_close_people(people, agv.rect.left, agv.rect.top)[1] <= self.risk: + path = ai.peopleavoid(self._layout, people, agv.rect.left, agv.rect.top) + warning.set([path[0][0], path[0][1]]) + else: + warning.set([]) + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=mine.Player=})) { - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor AGV(width(0), height(0), image("images/Trollman.png"), character_class({=mine.AGV=}), energy_cost(-0.25), charge_at(30), risk(120)) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - input warning - - logical action scheduler - - state _people({=[]=}) - state _layout({=mine.walls=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _find_moves(0) - state _event_list({=["mining", "washing", "filtering", "storing"]=}) - state _action({=self._event_list[0]=}) - state _prev_action({=self._event_list[0]=}) - state _scheduled(True) - state _warned({=[]=}) - - - output sprite - output icon_name - output playerpause - output restart - - method payCost() {= - print("payCost last move is ", self.character_instance.last_move) - if self.character_instance.last_move is not [0, 0]: - self.character_instance.charge(self.energy_cost) + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + input warning + + logical action scheduler + + state _people({=[]=}) + state _layout({=mine.walls=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _find_moves(0) + state _event_list({=["mining", "washing", "filtering", "storing"]=}) + state _action({=self._event_list[0]=}) + state _prev_action({=self._event_list[0]=}) + state _scheduled(True) + state _warned({=[]=}) + + + output sprite + output icon_name + output playerpause + output restart + + method payCost() {= + print("payCost last move is ", self.character_instance.last_move) + if self.character_instance.last_move is not [0, 0]: + self.character_instance.charge(self.energy_cost) + =} + + method proceedList() {= + temp = self._event_list[0] + self._event_list = self._event_list[1:] + self._event_list.append(temp) + =} + + initial mode export { + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) =} - - method proceedList() {= - temp = self._event_list[0] - self._event_list = self._event_list[1:] - self._event_list.append(temp) + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) =} - - initial mode export { - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart, reset(Charge_check), reset(Avoid) {= - print("action is ", self._action) - print("prev action is ", self._prev_action) - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self._action = "mining" - self._prev_action = "mining" - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - #print("setting sprite") - #print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0: #and self._active - if self._ai_control: - if len(self._warned) > 0: - print("warned is ", self._warned) - Avoid.set() - else: - Charge_check.set() - - =} - - reaction(warning) {= - if len(warning.value) > 0: - self._warned.append(warning.value) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} - - } mode Charge_check { - reaction(reset) -> reset(Location_check) {= - #print("checking charge") - if self.character_instance.battery <= self.charge_at: - self._action = "charging" - Location_check.set() - =} - } mode Location_check { - reaction(reset) -> reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing), reset(findspot) {= - #print("checking location") - if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: - mining.set() - elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: - washing.set() - elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: - filtering.set() - elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: - storing.set() - elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargerspot: - charging.set() - else: - #print("setting findspot") - findspot.set() - - =} - } mode findspot { - reaction(reset) -> history(export) {= - #print("in findspot") - if self._action == "mining": - self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) - elif self._action == "charging": - self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) - elif self._action == "washing": - self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) - elif self._action == "filtering": - self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) - elif self._action == "storing": - self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) - - self.payCost() - #print("found the spot move") - self._find_moves = self.character_instance.num_moves - export.set() - =} - } mode mining { - #implement - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(4)) - # self._scheduled = False - # print("mining rn") - # export.set() - print("in mining") - self._scheduled = True + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> sprite, playerpause, restart, reset(Charge_check), reset(Avoid) {= + print("action is ", self._action) + print("prev action is ", self._prev_action) + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self._action = "mining" self._prev_action = "mining" - self.proceedList() - self._action = self._event_list[0] - #print("ending mining, beginning wash") - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - - } mode washing { - - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(3)) - # self._scheduled = False - # export.set() - print("in washing") - self._scheduled = True - self._prev_action = "washing" - self.proceedList() - self._action = self._event_list[0] - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - - } mode charging { - reaction(reset) -> history(export) {= - print("in charging") - if self.character_instance.battery < 100: - self.character_instance.charge(1) + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True else: - self._action, self._prev_action = self._prev_action, self._action - export.set() - =} - } mode filtering { - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(4)) - # self._scheduled = False - # export.set() - print("in filtering") - self._scheduled = True - self._prev_action = "filtering" - self.proceedList() - self._action = self._event_list[0] - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - } mode storing { - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(4)) - # self._scheduled = False - # export.set() - print("in storing") - self._scheduled = True - self._prev_action = "storing" - self.proceedList() - self._action = self._event_list[0] - self.character_instance.store(randint(7, 11)) - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - } mode Avoid { - - reaction(reset) -> history(export) {= - print("avoid time") - move = self._warned.pop() - self.character_instance.rect.left += move[0] - self.character_instance.rect.top += move[1] - self.payCost() - #self._avoid_moves = self.character_instance.get_num_moves() - export.set() - =} - - } -} - -## Ghosts -# FIXME: Different Ghosts should have different personalities -reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + #print("setting sprite") + #print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0: #and self._active + if self._ai_control: + if len(self._warned) > 0: + print("warned is ", self._warned) + Avoid.set() + else: + Charge_check.set() + =} - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) + reaction(warning) {= + if len(warning.value) > 0: + self._warned.append(warning.value) =} - + reaction(game_over) {= - self._active = False + self._active = False + self._pause = True + self.character_instance.speedzero() =} - reaction(restart) {= - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) + } mode Charge_check { + reaction(reset) -> reset(Location_check) {= + #print("checking charge") + if self.character_instance.battery <= self.charge_at: + self._action = "charging" + Location_check.set() =} -} - -#### Controller -reactor GameController(number_of_people(4)) { - output wall_list # List of walls on the map - output gate - output mines - output charger - output wash - output filt - output store - output score # The game score - output game_over - - input[number_of_people] people_sprites - input agv_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _mine - state _charger - state _wash - state _filter - state _store - state _score_to_win(100) - state _score(0) - state _agv_sprite - state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - - reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._wall_list = mine.setupMineWalls(_all_sprites_list) - self._gate = mine.setupGate(_all_sprites_list) - self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) - self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) - self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) - self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) - self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) - wall_list.set(self._wall_list) - gate.set(self._gate) - mines.set(self._mine) - charger.set(self._charger) - wash.set(self._wash) - filt.set(self._filter) - store.set(self._store) + } mode Location_check { + reaction(reset) -> reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing), reset(findspot) {= + #print("checking location") + if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: + mining.set() + elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: + washing.set() + elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: + filtering.set() + elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: + storing.set() + elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargerspot: + charging.set() + else: + #print("setting findspot") + findspot.set() + =} - - reaction(agv_sprite) {= - self._agv_collide.empty() - self._agv_collide.add(agv_sprite.value) - self._agv_sprite = agv_sprite.value + } mode findspot { + reaction(reset) -> history(export) {= + #print("in findspot") + if self._action == "mining": + self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) + elif self._action == "charging": + self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) + elif self._action == "washing": + self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) + elif self._action == "filtering": + self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) + elif self._action == "storing": + self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) + + self.payCost() + #print("found the spot move") + self._find_moves = self.character_instance.num_moves + export.set() =} - - reaction(agv_sprite) -> score, game_over {= - # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - - # Check the list of collisions. - #if len(blocks_hit_list) > 0: - # self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set(True) - - score.set(agv_sprite.value.total_stored) + } mode mining { + #implement + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(4)) + # self._scheduled = False + # print("mining rn") + # export.set() + print("in mining") + self._scheduled = True + self._prev_action = "mining" + self.proceedList() + self._action = self._event_list[0] + #print("ending mining, beginning wash") + export.set() =} - reaction(people_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = mine.pygame.sprite.RenderPlain() - for person in people_sprites: - if person.is_present: - monsta_list.add(person.value) - - monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set(False) + #reaction(scheduler) -> history(export) {= + + #=} + + } mode washing { + + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(3)) + # self._scheduled = False + # export.set() + print("in washing") + self._scheduled = True + self._prev_action = "washing" + self.proceedList() + self._action = self._event_list[0] + export.set() + =} + #reaction(scheduler) -> history(export) {= + + #=} + + } mode charging { + reaction(reset) -> history(export) {= + print("in charging") + if self.character_instance.battery < 100: + self.character_instance.charge(1) + else: + self._action, self._prev_action = self._prev_action, self._action + export.set() + =} + } mode filtering { + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(4)) + # self._scheduled = False + # export.set() + print("in filtering") + self._scheduled = True + self._prev_action = "filtering" + self.proceedList() + self._action = self._event_list[0] + export.set() =} - - reaction(restart) {= - if restart.value: - - self._score_to_win = 100 - #print(self._energizer_list) - self._score = 0 + #reaction(scheduler) -> history(export) {= + + #=} + } mode storing { + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(4)) + # self._scheduled = False + # export.set() + print("in storing") + self._scheduled = True + self._prev_action = "storing" + self.proceedList() + self._action = self._event_list[0] + self.character_instance.store(randint(7, 11)) + export.set() =} - - reaction(shutdown) {= - mine.pygame.quit() + + #reaction(scheduler) -> history(export) {= + + #=} + } mode Avoid { + + reaction(reset) -> history(export) {= + print("avoid time") + move = self._warned.pop() + self.character_instance.rect.left += move[0] + self.character_instance.rect.top += move[1] + self.payCost() + #self._avoid_moves = self.character_instance.get_num_moves() + export.set() =} + } } -main reactor { - ### Controller - controller = new GameController() +## Ghosts +# FIXME: Different Ghosts should have different personalities +reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} +} - ### Model(s) - agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") - #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" - - # Ghosts - peoples = new[4] People( - width = {= people_specs[bank_index]["width"] =}, - height = {= people_specs[bank_index]["height"] =}, - directions = {= people_specs[bank_index]["directions"] =}, - name = {= people_specs[bank_index]["name"] =}, - character_class = ({=mine.People=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) - - observer = new Observer() - - observer.warning -> agv.warning - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> peoples.wall_list - - # Send the sprites to the display to be drawn - #controller.block_list - (agv.sprite, - peoples.sprite)+ -> - display.moving_sprites, observer.moving_sprites +#### Controller +reactor GameController(number_of_people(4)) { + output wall_list # List of walls on the map + output gate + output mines + output charger + output wash + output filt + output store + output score # The game score + output game_over + + input[number_of_people] people_sprites + input agv_sprite + input tick # The game tick + input restart - (controller.wall_list, controller.gate)+ -> - agv.wall_list, agv.gate_list + state _wall_list + state _gate + state _mine + state _charger + state _wash + state _filter + state _store + state _score_to_win(100) + state _score(0) + state _agv_sprite + state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + + reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._wall_list = mine.setupMineWalls(_all_sprites_list) + self._gate = mine.setupGate(_all_sprites_list) + self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) + self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) + self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) + self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) + self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) + wall_list.set(self._wall_list) + gate.set(self._gate) + mines.set(self._mine) + charger.set(self._charger) + wash.set(self._wash) + filt.set(self._filter) + store.set(self._store) + =} + + reaction(agv_sprite) {= + self._agv_collide.empty() + self._agv_collide.add(agv_sprite.value) + self._agv_sprite = agv_sprite.value + =} + + reaction(agv_sprite) -> score, game_over {= + # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) + + # Check the list of collisions. + #if len(blocks_hit_list) > 0: + # self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set(True) + + score.set(agv_sprite.value.total_stored) + =} + + reaction(people_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = mine.pygame.sprite.RenderPlain() + for person in people_sprites: + if person.is_present: + monsta_list.add(person.value) - controller.wall_list, controller.gate, controller.mines, - controller.charger, controller.wash, controller.filt, controller.store -> - display.static_sprites + monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - (display.tick)+ -> - controller.tick, - peoples.tick + if monsta_hit_list: + game_over.set(False) + + =} - #Send pause player to game controller and ghosts - (agv.playerpause)+ -> peoples.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - agv.sprite -> controller.agv_sprite - - (peoples.sprite)+ -> controller.people_sprites - controller.score -> display.score + reaction(restart) {= + if restart.value: + + self._score_to_win = 100 + #print(self._energizer_list) + self._score = 0 + =} + + reaction(shutdown) {= + mine.pygame.quit() + =} - peoples.icon_name, agv.icon_name -> display.icon_name +} - display.icon -> peoples.icon, agv.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over - - (agv.restart)+ -> controller.restart, display.restart, peoples.restart - - #controller.block_list -> agv.block_list +main reactor { + ### Controller + controller = new GameController() + + ### Model(s) + agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") + #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" + + # Ghosts + peoples = new[4] People( + width = {= people_specs[bank_index]["width"] =}, + height = {= people_specs[bank_index]["height"] =}, + directions = {= people_specs[bank_index]["directions"] =}, + name = {= people_specs[bank_index]["name"] =}, + character_class = ({=mine.People=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) + + observer = new Observer() + + observer.warning -> agv.warning + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> peoples.wall_list + + # Send the sprites to the display to be drawn + #controller.block_list + (agv.sprite, + peoples.sprite)+ -> + display.moving_sprites, observer.moving_sprites + + (controller.wall_list, controller.gate)+ -> + agv.wall_list, agv.gate_list + + controller.wall_list, controller.gate, controller.mines, + controller.charger, controller.wash, controller.filt, controller.store -> + display.static_sprites + + (display.tick)+ -> + controller.tick, + peoples.tick + + #Send pause player to game controller and ghosts + (agv.playerpause)+ -> peoples.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + agv.sprite -> controller.agv_sprite + + (peoples.sprite)+ -> controller.people_sprites + + controller.score -> display.score + + peoples.icon_name, agv.icon_name -> display.icon_name + + display.icon -> peoples.icon, agv.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over + + (agv.restart)+ -> controller.restart, display.restart, peoples.restart + #controller.block_list -> agv.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Mining/ModalMine.lf b/experimental/Python/src/Mining/ModalMine.lf index d1d3209d..337bedd1 100644 --- a/experimental/Python/src/Mining/ModalMine.lf +++ b/experimental/Python/src/Mining/ModalMine.lf @@ -13,208 +13,208 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] + files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbphosphate as mine - import AIPhosphate as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - people_specs = [ - { - "name": "Pinky", - "directions": mine.Pinky_directions, - "width": mine.w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": mine.Blinky_directions, - "width": mine.w, - "height": mine.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": mine.Inky_directions, - "width": mine.i_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": mine.Clyde_directions, - "width": mine.c_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbphosphate as mine + import AIPhosphate as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + people_specs = [ + { + "name": "Pinky", + "directions": mine.Pinky_directions, + "width": mine.w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": mine.Blinky_directions, + "width": mine.w, + "height": mine.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": mine.Inky_directions, + "width": mine.i_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": mine.Clyde_directions, + "width": mine.c_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) + mine.pygame.display.set_icon(agv_icon) + + self._clock = mine.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = mine.pygame.display.set_mode([606, 606]) + # Set the title of the window + mine.pygame.display.set_caption("Phosphate Mine") + # Create a surface we can draw on + background = mine.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(mine.white) + mine.pygame.font.init() + self._font = mine.pygame.font.Font("freesansbold.ttf", 18) + self._screen.fill(mine.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) - mine.pygame.display.set_icon(agv_icon) - - self._clock = mine.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = mine.pygame.display.set_mode([606, 606]) - # Set the title of the window - mine.pygame.display.set_caption("Phosphate Mine") - # Create a surface we can draw on - background = mine.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(mine.white) - mine.pygame.font.init() - self._font = mine.pygame.font.Font("freesansbold.ttf", 18) - self._screen.fill(mine.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(mine.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + mine.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + print("adding static sprites") + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + print(self._static_sprites) + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, mine.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + + print("adding moving sprites") + self._screen.fill(mine.white) + agv = mine.pygame.sprite.Sprite() + sprite_list = mine.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + #print("agv battery is ", agv.battery) + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(mine.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - mine.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - print("adding static sprites") - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - print(self._static_sprites) - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, mine.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - - print("adding moving sprites") - self._screen.fill(mine.white) - agv = mine.pygame.sprite.Sprite() - sprite_list = mine.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - #print("agv battery is ", agv.battery) - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - print("paused") - text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, mine.black) - self._screen.blit(text3, [165, 333]) - mine.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Area Cleared!", True, mine.black) - else: - text1=self._font.render("Collision detected.", True, mine.black) - self._screen.blit(text1, [235, 233]) - print("game is over") - text2=self._font.render("To play again, press ENTER.", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, mine.black) - self._screen.blit(text3, [165, 333]) - - mine.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + print("paused") + text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, mine.black) + self._screen.blit(text3, [165, 333]) + mine.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Area Cleared!", True, mine.black) + else: + text1=self._font.render("Collision detected.", True, mine.black) + self._screen.blit(text1, [235, 233]) + print("game is over") + text2=self._font.render("To play again, press ENTER.", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, mine.black) + self._screen.blit(text3, [165, 333]) + + mine.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -222,856 +222,856 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=mine.AGV=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=mine.Player=})) { - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor AGV(width(0), height(0), image("images/Trollman.png"), character_class({=mine.AGV=}), event_list({=["mining", "washing", "filtering", "storing"]=}), energy_cost(-0.5), charge_at(20), risk(180)) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - - logical action scheduler - - state _people({=[]=}) - state _layout({=mine.walls=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _find_moves(0) - state _prev_action({=["mining"]=}) - - output sprite - output icon_name - output playerpause - output restart - - method ensureCharge(current_mode) {= - if self.character_instance.battery <= self.charge_at: - self._prev_action = [current_mode] - return True - return False - =} - - method atLocation() {= - if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: - return True - elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: - return True - elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: - return True - elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: - return True - elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargespot: - return True - return False - =} - - method isClose() {= - if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: - return False - return True - =} + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + + logical action scheduler + + state _people({=[]=}) + state _layout({=mine.walls=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _find_moves(0) + state _prev_action({=["mining"]=}) + + output sprite + output icon_name + output playerpause + output restart + + method ensureCharge(current_mode) {= + if self.character_instance.battery <= self.charge_at: + self._prev_action = [current_mode] + return True + return False + =} + + method atLocation() {= + if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: + return True + elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: + return True + elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: + return True + elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: + return True + elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargespot: + return True + return False + =} + + method isClose() {= + if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: + return False + return True + =} + + method Avoid() {= + self.character_instance.ai_avoid(self._layout, self._people, 7) + self.payCost() + =} + + // method performNext() {= + // if self.event_list[0] == "mining": + // self.event_list = self.event_list[1:] + // self.event_list.append("mining") + // mining.set() + // elif self.event_list[0] == "filtering": + // self.event_list = self.event_list[1:] + // self.event_list.append("filtering") + // filtering.set() + // elif self.event_list[0] == "washing": + // self.event_list = self.event_list[1:] + // self.event_list.append("washing") + // washing.set() + // elif self.event_list[0] == "storing": + // self.event_list = self.event_list[1:] + // self.event_list.append("storing") + // storing.set() + // =} + + // method performLast() {= + // if self._prev_action[0] == "mining": + // mining.set() + // elif self._prev_action[0] == "filtering": + // filtering.set() + // elif self._prev_action[0] == "washing": + // washing.set() + // elif self._prev_action[0] == "storing": + // storing.set() + // elif self._prev_action[0] == "charging": + // charging.set() + // =} + + method payCost() {= + if self.character_instance.last_move is not [0, 0]: + self.character_instance.charge(self.energy_cost) + =} + + method Approach() {= + if self._action == "mining": + self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) + elif self._action == "charging": + self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) + elif self._action == "washing": + self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) + elif self._action == "filtering": + self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) + elif self._action == "storing": + self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) + + self.payCost() + print("finding the spot") + self._find_moves = self.character_instance.num_moves + =} + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + initial mode mining { + + reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= + print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active + if self.isClose(): + self.Avoid() + elif self.ensureCharge(): + #implement correctly for new design + charging.set() + elif self.atLocation(): + if self.event_list[0] == "mining": + self.event_list = self.event_list[1:] + self.event_list.append("mining") + mining.set() + elif self.event_list[0] == "filtering": + self.event_list = self.event_list[1:] + self.event_list.append("filtering") + filtering.set() + elif self.event_list[0] == "washing": + self.event_list = self.event_list[1:] + self.event_list.append("washing") + washing.set() + elif self.event_list[0] == "storing": + self.event_list = self.event_list[1:] + self.event_list.append("storing") + storing.set() + else: + self.Approach() - method Avoid() {= - self.character_instance.ai_avoid(self._layout, self._people, 7) - self.payCost() + =} - // method performNext() {= - // if self.event_list[0] == "mining": - // self.event_list = self.event_list[1:] - // self.event_list.append("mining") - // mining.set() - // elif self.event_list[0] == "filtering": - // self.event_list = self.event_list[1:] - // self.event_list.append("filtering") - // filtering.set() - // elif self.event_list[0] == "washing": - // self.event_list = self.event_list[1:] - // self.event_list.append("washing") - // washing.set() - // elif self.event_list[0] == "storing": - // self.event_list = self.event_list[1:] - // self.event_list.append("storing") - // storing.set() - // =} - - // method performLast() {= - // if self._prev_action[0] == "mining": - // mining.set() - // elif self._prev_action[0] == "filtering": - // filtering.set() - // elif self._prev_action[0] == "washing": - // washing.set() - // elif self._prev_action[0] == "storing": - // storing.set() - // elif self._prev_action[0] == "charging": - // charging.set() - // =} - - method payCost() {= - if self.character_instance.last_move is not [0, 0]: - self.character_instance.charge(self.energy_cost) - =} + } mode washing { + + reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= + print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active + if self.isClose(): + self.Avoid() + elif self.ensureCharge(): + #implement correctly for new design + charging.set() + elif self.atLocation(): + if self.event_list[0] == "mining": + self.event_list = self.event_list[1:] + self.event_list.append("mining") + mining.set() + elif self.event_list[0] == "filtering": + self.event_list = self.event_list[1:] + self.event_list.append("filtering") + filtering.set() + elif self.event_list[0] == "washing": + self.event_list = self.event_list[1:] + self.event_list.append("washing") + washing.set() + elif self.event_list[0] == "storing": + self.event_list = self.event_list[1:] + self.event_list.append("storing") + storing.set() + else: + self.Approach() - method Approach() {= - if self._action == "mining": - self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) - elif self._action == "charging": - self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) - elif self._action == "washing": - self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) - elif self._action == "filtering": - self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) - elif self._action == "storing": - self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) - - self.payCost() - print("finding the spot") - self._find_moves = self.character_instance.num_moves + =} - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + #reaction(scheduler) -> history(export) {= + + #=} + + } mode charging { + reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= + print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active + if self.isClose(): + self.Avoid() + elif self.ensureCharge(): + #implement correctly for new design + charging.set() + elif self.atLocation(): + if self.event_list[0] == "mining": + self.event_list = self.event_list[1:] + self.event_list.append("mining") + mining.set() + elif self.event_list[0] == "filtering": + self.event_list = self.event_list[1:] + self.event_list.append("filtering") + filtering.set() + elif self.event_list[0] == "washing": + self.event_list = self.event_list[1:] + self.event_list.append("washing") + washing.set() + elif self.event_list[0] == "storing": + self.event_list = self.event_list[1:] + self.event_list.append("storing") + storing.set() + else: + self.Approach() - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) + =} + } mode filtering { + reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= + print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active + if self.isClose(): + self.Avoid() + elif self.ensureCharge(): + #implement correctly for new design + charging.set() + elif self.atLocation(): + if self.event_list[0] == "mining": + self.event_list = self.event_list[1:] + self.event_list.append("mining") + mining.set() + elif self.event_list[0] == "filtering": + self.event_list = self.event_list[1:] + self.event_list.append("filtering") + filtering.set() + elif self.event_list[0] == "washing": + self.event_list = self.event_list[1:] + self.event_list.append("washing") + washing.set() + elif self.event_list[0] == "storing": + self.event_list = self.event_list[1:] + self.event_list.append("storing") + storing.set() + else: + self.Approach() - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - initial mode mining { - - reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= - print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active - if self.isClose(): - self.Avoid() - elif self.ensureCharge(): - #implement correctly for new design - charging.set() - elif self.atLocation(): - if self.event_list[0] == "mining": - self.event_list = self.event_list[1:] - self.event_list.append("mining") - mining.set() - elif self.event_list[0] == "filtering": - self.event_list = self.event_list[1:] - self.event_list.append("filtering") - filtering.set() - elif self.event_list[0] == "washing": - self.event_list = self.event_list[1:] - self.event_list.append("washing") - washing.set() - elif self.event_list[0] == "storing": - self.event_list = self.event_list[1:] - self.event_list.append("storing") - storing.set() - else: - self.Approach() - - - =} - - } mode washing { - - reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= - print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active - if self.isClose(): - self.Avoid() - elif self.ensureCharge(): - #implement correctly for new design - charging.set() - elif self.atLocation(): - if self.event_list[0] == "mining": - self.event_list = self.event_list[1:] - self.event_list.append("mining") - mining.set() - elif self.event_list[0] == "filtering": - self.event_list = self.event_list[1:] - self.event_list.append("filtering") - filtering.set() - elif self.event_list[0] == "washing": - self.event_list = self.event_list[1:] - self.event_list.append("washing") - washing.set() - elif self.event_list[0] == "storing": - self.event_list = self.event_list[1:] - self.event_list.append("storing") - storing.set() - else: - self.Approach() - - - =} - - #reaction(scheduler) -> history(export) {= - - #=} - - } mode charging { - reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= - print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active - if self.isClose(): - self.Avoid() - elif self.ensureCharge(): - #implement correctly for new design - charging.set() - elif self.atLocation(): - if self.event_list[0] == "mining": - self.event_list = self.event_list[1:] - self.event_list.append("mining") - mining.set() - elif self.event_list[0] == "filtering": - self.event_list = self.event_list[1:] - self.event_list.append("filtering") - filtering.set() - elif self.event_list[0] == "washing": - self.event_list = self.event_list[1:] - self.event_list.append("washing") - washing.set() - elif self.event_list[0] == "storing": - self.event_list = self.event_list[1:] - self.event_list.append("storing") - storing.set() - else: - self.Approach() - - - =} - } mode filtering { - reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= - print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active - if self.isClose(): - self.Avoid() - elif self.ensureCharge(): - #implement correctly for new design - charging.set() - elif self.atLocation(): - if self.event_list[0] == "mining": - self.event_list = self.event_list[1:] - self.event_list.append("mining") - mining.set() - elif self.event_list[0] == "filtering": - self.event_list = self.event_list[1:] - self.event_list.append("filtering") - filtering.set() - elif self.event_list[0] == "washing": - self.event_list = self.event_list[1:] - self.event_list.append("washing") - washing.set() - elif self.event_list[0] == "storing": - self.event_list = self.event_list[1:] - self.event_list.append("storing") - storing.set() - else: - self.Approach() - - - =} - } mode storing { - reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= - print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active - if self.isClose(): - self.Avoid() - elif self.ensureCharge(): - #implement correctly for new design - charging.set() - elif self.atLocation(): - if self.event_list[0] == "mining": - self.event_list = self.event_list[1:] - self.event_list.append("mining") - mining.set() - elif self.event_list[0] == "filtering": - self.event_list = self.event_list[1:] - self.event_list.append("filtering") - filtering.set() - elif self.event_list[0] == "washing": - self.event_list = self.event_list[1:] - self.event_list.append("washing") - washing.set() - elif self.event_list[0] == "storing": - self.event_list = self.event_list[1:] - self.event_list.append("storing") - storing.set() - else: - self.Approach() - - - =} - } - - reaction(people_sprites) {= - self._ghosts = [] - for person in people_sprites: - if person.is_present: - self._people.append(person.value) + =} + } mode storing { + reaction(pygame_event) -> sprite, playerpause, restart, reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing) {= + print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active + if self.isClose(): + self.Avoid() + elif self.ensureCharge(): + #implement correctly for new design + charging.set() + elif self.atLocation(): + if self.event_list[0] == "mining": + self.event_list = self.event_list[1:] + self.event_list.append("mining") + mining.set() + elif self.event_list[0] == "filtering": + self.event_list = self.event_list[1:] + self.event_list.append("filtering") + filtering.set() + elif self.event_list[0] == "washing": + self.event_list = self.event_list[1:] + self.event_list.append("washing") + washing.set() + elif self.event_list[0] == "storing": + self.event_list = self.event_list[1:] + self.event_list.append("storing") + storing.set() + else: + self.Approach() - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() + =} + } + + reaction(people_sprites) {= + self._ghosts = [] + for person in people_sprites: + if person.is_present: + self._people.append(person.value) + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} } #### Controller reactor GameController(number_of_people(4)) { - output wall_list # List of walls on the map - output gate - output mines - output charger - output wash - output filt - output store - output score # The game score - output game_over - - input[number_of_people] people_sprites - input agv_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _mine - state _charger - state _wash - state _filter - state _store - state _score_to_win(0) - state _score(0) - state _agv_sprite - state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - - reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._wall_list = mine.setupMineWalls(_all_sprites_list) - self._gate = mine.setupGate(_all_sprites_list) - self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) - self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) - self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) - self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) - self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) - wall_list.set(self._wall_list) - gate.set(self._gate) - mines.set(self._mine) - charger.set(self._charger) - wash.set(self._wash) - filt.set(self._filter) - store.set(self._store) - =} - - reaction(agv_sprite) {= - self._agv_collide.empty() - self._agv_collide.add(agv_sprite.value) - self._agv_sprite = agv_sprite.value - =} - - reaction(agv_sprite) -> score, game_over {= - # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - - # Check the list of collisions. - #if len(blocks_hit_list) > 0: - # self._score +=len(blocks_hit_list) - - #if self._score == self._score_to_win: - # game_over.set(True) + output wall_list # List of walls on the map + output gate + output mines + output charger + output wash + output filt + output store + output score # The game score + output game_over + + input[number_of_people] people_sprites + input agv_sprite + input tick # The game tick + input restart - score.set(self._score) - =} - - reaction(people_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = mine.pygame.sprite.RenderPlain() - for person in people_sprites: - if person.is_present: - monsta_list.add(person.value) + state _wall_list + state _gate + state _mine + state _charger + state _wash + state _filter + state _store + state _score_to_win(0) + state _score(0) + state _agv_sprite + state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + + reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._wall_list = mine.setupMineWalls(_all_sprites_list) + self._gate = mine.setupGate(_all_sprites_list) + self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) + self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) + self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) + self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) + self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) + wall_list.set(self._wall_list) + gate.set(self._gate) + mines.set(self._mine) + charger.set(self._charger) + wash.set(self._wash) + filt.set(self._filter) + store.set(self._store) + =} + + reaction(agv_sprite) {= + self._agv_collide.empty() + self._agv_collide.add(agv_sprite.value) + self._agv_sprite = agv_sprite.value + =} + + reaction(agv_sprite) -> score, game_over {= + # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) + # Check the list of collisions. + #if len(blocks_hit_list) > 0: + # self._score +=len(blocks_hit_list) + + #if self._score == self._score_to_win: + # game_over.set(True) + + score.set(self._score) + =} + + reaction(people_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = mine.pygame.sprite.RenderPlain() + for person in people_sprites: + if person.is_present: + monsta_list.add(person.value) + + monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - if monsta_hit_list: - game_over.set(False) + if monsta_hit_list: + game_over.set(False) - =} + =} - - reaction(restart) {= - if restart.value: - - self._score_to_win = 5 - #print(self._energizer_list) - self._score = 0 - =} - reaction(shutdown) {= - mine.pygame.quit() - =} + reaction(restart) {= + if restart.value: + + self._score_to_win = 5 + #print(self._energizer_list) + self._score = 0 + =} + + reaction(shutdown) {= + mine.pygame.quit() + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") - #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" - - # Ghosts - peoples = new[4] People( - width = {= people_specs[bank_index]["width"] =}, - height = {= people_specs[bank_index]["height"] =}, - directions = {= people_specs[bank_index]["directions"] =}, - name = {= people_specs[bank_index]["name"] =}, - character_class = ({=mine.People=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> peoples.wall_list - - # Send the sprites to the display to be drawn - #controller.block_list - agv.sprite, - peoples.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - agv.wall_list, agv.gate_list - - controller.wall_list, controller.gate, controller.mines, - controller.charger, controller.wash, controller.filt, controller.store -> - display.static_sprites - - (display.tick)+ -> - controller.tick, - peoples.tick - - #Send pause player to game controller and ghosts - (agv.playerpause)+ -> peoples.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - agv.sprite -> controller.agv_sprite + ### Controller + controller = new GameController() + + ### Model(s) + agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") + #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" + + # Ghosts + peoples = new[4] People( + width = {= people_specs[bank_index]["width"] =}, + height = {= people_specs[bank_index]["height"] =}, + directions = {= people_specs[bank_index]["directions"] =}, + name = {= people_specs[bank_index]["name"] =}, + character_class = ({=mine.People=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> peoples.wall_list + + # Send the sprites to the display to be drawn + #controller.block_list + agv.sprite, + peoples.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + agv.wall_list, agv.gate_list + + controller.wall_list, controller.gate, controller.mines, + controller.charger, controller.wash, controller.filt, controller.store -> + display.static_sprites + + (display.tick)+ -> + controller.tick, + peoples.tick + + #Send pause player to game controller and ghosts + (agv.playerpause)+ -> peoples.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + agv.sprite -> controller.agv_sprite - (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites - - controller.score -> display.score - - peoples.icon_name, agv.icon_name -> display.icon_name - - display.icon -> peoples.icon, agv.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over - - (agv.restart)+ -> controller.restart, display.restart, peoples.restart - - #controller.block_list -> agv.block_list + (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites + + controller.score -> display.score + + peoples.icon_name, agv.icon_name -> display.icon_name + + display.icon -> peoples.icon, agv.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over + + (agv.restart)+ -> controller.restart, display.restart, peoples.restart + #controller.block_list -> agv.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Mining/PhosphateMine.lf b/experimental/Python/src/Mining/PhosphateMine.lf index 07abce50..0982165e 100644 --- a/experimental/Python/src/Mining/PhosphateMine.lf +++ b/experimental/Python/src/Mining/PhosphateMine.lf @@ -13,208 +13,208 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] + files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbphosphate as mine - import AIPhosphate as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - people_specs = [ - { - "name": "Pinky", - "directions": mine.Pinky_directions, - "width": mine.w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": mine.Blinky_directions, - "width": mine.w, - "height": mine.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": mine.Inky_directions, - "width": mine.i_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": mine.Clyde_directions, - "width": mine.c_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbphosphate as mine + import AIPhosphate as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + people_specs = [ + { + "name": "Pinky", + "directions": mine.Pinky_directions, + "width": mine.w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": mine.Blinky_directions, + "width": mine.w, + "height": mine.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": mine.Inky_directions, + "width": mine.i_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": mine.Clyde_directions, + "width": mine.c_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) + mine.pygame.display.set_icon(agv_icon) + + self._clock = mine.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = mine.pygame.display.set_mode([606, 606]) + # Set the title of the window + mine.pygame.display.set_caption("Phosphate Mine") + # Create a surface we can draw on + background = mine.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(mine.white) + mine.pygame.font.init() + self._font = mine.pygame.font.Font("freesansbold.ttf", 18) + self._screen.fill(mine.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) - mine.pygame.display.set_icon(agv_icon) - - self._clock = mine.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = mine.pygame.display.set_mode([606, 606]) - # Set the title of the window - mine.pygame.display.set_caption("Phosphate Mine") - # Create a surface we can draw on - background = mine.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(mine.white) - mine.pygame.font.init() - self._font = mine.pygame.font.Font("freesansbold.ttf", 18) - self._screen.fill(mine.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(mine.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + mine.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + print("adding static sprites") + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + print(self._static_sprites) + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Mined Material: "+str(score.value), True, mine.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + + #print("adding moving sprites") + self._screen.fill(mine.white) + agv = mine.pygame.sprite.Sprite() + sprite_list = mine.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + #print("agv battery is ", agv.battery) + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(mine.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - mine.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - print("adding static sprites") - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - print(self._static_sprites) - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Mined Material: "+str(score.value), True, mine.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - - #print("adding moving sprites") - self._screen.fill(mine.white) - agv = mine.pygame.sprite.Sprite() - sprite_list = mine.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - #print("agv battery is ", agv.battery) - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - print("paused") - text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, mine.black) - self._screen.blit(text3, [165, 333]) - mine.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Mining Complete!", True, mine.black) - else: - text1=self._font.render("Collision detected.", True, mine.black) - self._screen.blit(text1, [235, 233]) - print("game is over") - text2=self._font.render("To play again, press ENTER.", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, mine.black) - self._screen.blit(text3, [165, 333]) - - mine.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + print("paused") + text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, mine.black) + self._screen.blit(text3, [165, 333]) + mine.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Mining Complete!", True, mine.black) + else: + text1=self._font.render("Collision detected.", True, mine.black) + self._screen.blit(text1, [235, 233]) + print("game is over") + text2=self._font.render("To play again, press ENTER.", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, mine.black) + self._screen.blit(text3, [165, 333]) + + mine.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -222,563 +222,563 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=mine.AGV=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=mine.Player=})) { - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor AGV(width(0), height(0), image("images/Trollman.png"), character_class({=mine.AGV=}), energy_cost(-0.25), charge_at(30), risk(120)) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - - logical action scheduler - - state _people({=[]=}) - state _layout({=mine.walls=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _find_moves(0) - state _event_list({=["washing", "mining", "washing", "filtering", "storing"]=}) - state _action({=self._event_list[0]=}) - state _prev_action({=self._event_list[0]=}) - state _scheduled(True) - - output sprite - output icon_name - output playerpause - output restart - - method payCost() {= - print("payCost last move is ", self.character_instance.last_move) - if self.character_instance.last_move is not [0, 0]: - self.character_instance.charge(self.energy_cost) + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + + logical action scheduler + + state _people({=[]=}) + state _layout({=mine.walls=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _find_moves(0) + state _event_list({=["washing", "mining", "washing", "filtering", "storing"]=}) + state _action({=self._event_list[0]=}) + state _prev_action({=self._event_list[0]=}) + state _scheduled(True) + + output sprite + output icon_name + output playerpause + output restart + + method payCost() {= + print("payCost last move is ", self.character_instance.last_move) + if self.character_instance.last_move is not [0, 0]: + self.character_instance.charge(self.energy_cost) + =} + + method proceedList() {= + temp = self._event_list[0] + self._event_list = self._event_list[1:] + self._event_list.append(temp) + =} + + initial mode export { + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) =} - - method proceedList() {= - temp = self._event_list[0] - self._event_list = self._event_list[1:] - self._event_list.append(temp) + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) =} - - initial mode export { - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= - print("action is ", self._action) - print("prev action is ", self._prev_action) - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self._action = "mining" - self._prev_action = "mining" - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - #print("setting sprite") - #print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0: #and self._active - if self._ai_control: - Close.set() - - =} - - reaction(people_sprites) {= - self._ghosts = [] - for person in people_sprites: - if person.is_present: - self._people.append(person.value) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} - - } mode Charge_check { - reaction(reset) -> reset(Location_check) {= - #print("checking charge") - if self.character_instance.battery <= self.charge_at: - self._action = "charging" - Location_check.set() - =} - } mode Location_check { - reaction(reset) -> reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing), reset(findspot) {= - #print("checking location") - if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: - mining.set() - elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: - washing.set() - elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: - filtering.set() - elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: - storing.set() - elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargerspot: - charging.set() - else: - #print("setting findspot") - findspot.set() - - =} - } mode findspot { - reaction(reset) -> history(export) {= - #print("in findspot") - if self._action == "mining": - self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) - elif self._action == "charging": - self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) - elif self._action == "washing": - self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) - elif self._action == "filtering": - self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) - elif self._action == "storing": - self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) - - self.payCost() - #print("found the spot move") - self._find_moves = self.character_instance.num_moves - export.set() - =} - } mode mining { - #implement - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(4)) - # self._scheduled = False - # print("mining rn") - # export.set() - print("in mining") - self._scheduled = True + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= + print("action is ", self._action) + print("prev action is ", self._prev_action) + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() + + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self._action = "mining" self._prev_action = "mining" - self.proceedList() - self._action = self._event_list[0] - #print("ending mining, beginning wash") - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - - } mode washing { - - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(3)) - # self._scheduled = False - # export.set() - print("in washing") - self._scheduled = True - self._prev_action = "washing" - self.proceedList() - self._action = self._event_list[0] - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - - } mode charging { - reaction(reset) -> history(export) {= - print("in charging") - if self.character_instance.battery < 100: - self.character_instance.charge(1) + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True else: - self._action, self._prev_action = self._prev_action, self._action - export.set() - =} - } mode filtering { - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(4)) - # self._scheduled = False - # export.set() - print("in filtering") - self._scheduled = True - self._prev_action = "filtering" - self.proceedList() - self._action = self._event_list[0] - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - } mode storing { - reaction(reset) -> history(export) {= - # if self._scheduled: - # scheduler.schedule(SEC(4)) - # self._scheduled = False - # export.set() - print("in storing") - self._scheduled = True - self._prev_action = "storing" - self.proceedList() - self._action = self._event_list[0] - self.character_instance.store(randint(7, 11)) - export.set() - =} - - #reaction(scheduler) -> history(export) {= - - #=} - } mode Close { - - reaction(reset) -> reset(Charge_check), reset(Avoid), history(export) {= - print("in mode close") - if len(self._people) > 0: - #if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: - if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: - Charge_check.set() - else: - #print("ghost close") - Avoid.set() - else: - export.set() - =} - - } mode Avoid { - - reaction(reset) -> history(export) {= - print("avoid time") - self.character_instance.ai_avoid(self._layout, self._people, 7) - self.payCost() - #self._avoid_moves = self.character_instance.get_num_moves() - export.set() - =} - - } -} - -## Ghosts -# FIXME: Different Ghosts should have different personalities -reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + #print("setting sprite") + #print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0: #and self._active + if self._ai_control: + Close.set() + =} - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) + reaction(people_sprites) {= + self._ghosts = [] + for person in people_sprites: + if person.is_present: + self._people.append(person.value) =} - + reaction(game_over) {= - self._active = False + self._active = False + self._pause = True + self.character_instance.speedzero() =} - reaction(restart) {= - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) + } mode Charge_check { + reaction(reset) -> reset(Location_check) {= + #print("checking charge") + if self.character_instance.battery <= self.charge_at: + self._action = "charging" + Location_check.set() =} -} - -#### Controller -reactor GameController(number_of_people(4)) { - output wall_list # List of walls on the map - output gate - output mines - output charger - output wash - output filt - output store - output score # The game score - output game_over - - input[number_of_people] people_sprites - input agv_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _mine - state _charger - state _wash - state _filter - state _store - state _score_to_win(100) - state _score(0) - state _agv_sprite - state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - - reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._wall_list = mine.setupMineWalls(_all_sprites_list) - self._gate = mine.setupGate(_all_sprites_list) - self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) - self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) - self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) - self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) - self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) - wall_list.set(self._wall_list) - gate.set(self._gate) - mines.set(self._mine) - charger.set(self._charger) - wash.set(self._wash) - filt.set(self._filter) - store.set(self._store) + } mode Location_check { + reaction(reset) -> reset(charging), reset(mining), reset(washing), reset(filtering), reset(storing), reset(findspot) {= + #print("checking location") + if self._action == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: + mining.set() + elif self._action == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: + washing.set() + elif self._action == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: + filtering.set() + elif self._action == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: + storing.set() + elif self._action == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargerspot: + charging.set() + else: + #print("setting findspot") + findspot.set() + =} - - reaction(agv_sprite) {= - self._agv_collide.empty() - self._agv_collide.add(agv_sprite.value) - self._agv_sprite = agv_sprite.value + } mode findspot { + reaction(reset) -> history(export) {= + #print("in findspot") + if self._action == "mining": + self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) + elif self._action == "charging": + self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) + elif self._action == "washing": + self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) + elif self._action == "filtering": + self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) + elif self._action == "storing": + self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) + + self.payCost() + #print("found the spot move") + self._find_moves = self.character_instance.num_moves + export.set() =} - - reaction(agv_sprite) -> score, game_over {= - # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - - # Check the list of collisions. - #if len(blocks_hit_list) > 0: - # self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set(True) - - score.set(agv_sprite.value.total_stored) + } mode mining { + #implement + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(4)) + # self._scheduled = False + # print("mining rn") + # export.set() + print("in mining") + self._scheduled = True + self._prev_action = "mining" + self.proceedList() + self._action = self._event_list[0] + #print("ending mining, beginning wash") + export.set() =} - reaction(people_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = mine.pygame.sprite.RenderPlain() - for person in people_sprites: - if person.is_present: - monsta_list.add(person.value) - - monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) + #reaction(scheduler) -> history(export) {= + + #=} + + } mode washing { + + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(3)) + # self._scheduled = False + # export.set() + print("in washing") + self._scheduled = True + self._prev_action = "washing" + self.proceedList() + self._action = self._event_list[0] + export.set() + =} - if monsta_hit_list: - game_over.set(False) + #reaction(scheduler) -> history(export) {= + + #=} + + } mode charging { + reaction(reset) -> history(export) {= + print("in charging") + if self.character_instance.battery < 100: + self.character_instance.charge(1) + else: + self._action, self._prev_action = self._prev_action, self._action + export.set() + =} + } mode filtering { + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(4)) + # self._scheduled = False + # export.set() + print("in filtering") + self._scheduled = True + self._prev_action = "filtering" + self.proceedList() + self._action = self._event_list[0] + export.set() + =} + #reaction(scheduler) -> history(export) {= + + #=} + } mode storing { + reaction(reset) -> history(export) {= + # if self._scheduled: + # scheduler.schedule(SEC(4)) + # self._scheduled = False + # export.set() + print("in storing") + self._scheduled = True + self._prev_action = "storing" + self.proceedList() + self._action = self._event_list[0] + self.character_instance.store(randint(7, 11)) + export.set() =} - - reaction(restart) {= - if restart.value: - - self._score_to_win = 100 - #print(self._energizer_list) - self._score = 0 + #reaction(scheduler) -> history(export) {= + + #=} + } mode Close { + + reaction(reset) -> reset(Charge_check), reset(Avoid), history(export) {= + print("in mode close") + if len(self._people) > 0: + #if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: + if ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: + Charge_check.set() + else: + #print("ghost close") + Avoid.set() + else: + export.set() =} - - reaction(shutdown) {= - mine.pygame.quit() + + } mode Avoid { + + reaction(reset) -> history(export) {= + print("avoid time") + self.character_instance.ai_avoid(self._layout, self._people, 7) + self.payCost() + #self._avoid_moves = self.character_instance.get_num_moves() + export.set() =} + } } -main reactor { - ### Controller - controller = new GameController() +## Ghosts +# FIXME: Different Ghosts should have different personalities +reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} +} - ### Model(s) - agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") - #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" - - # Ghosts - peoples = new[4] People( - width = {= people_specs[bank_index]["width"] =}, - height = {= people_specs[bank_index]["height"] =}, - directions = {= people_specs[bank_index]["directions"] =}, - name = {= people_specs[bank_index]["name"] =}, - character_class = ({=mine.People=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> peoples.wall_list - - # Send the sprites to the display to be drawn - #controller.block_list - agv.sprite, - peoples.sprite -> - display.moving_sprites +#### Controller +reactor GameController(number_of_people(4)) { + output wall_list # List of walls on the map + output gate + output mines + output charger + output wash + output filt + output store + output score # The game score + output game_over + + input[number_of_people] people_sprites + input agv_sprite + input tick # The game tick + input restart - (controller.wall_list, controller.gate)+ -> - agv.wall_list, agv.gate_list + state _wall_list + state _gate + state _mine + state _charger + state _wash + state _filter + state _store + state _score_to_win(100) + state _score(0) + state _agv_sprite + state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + + reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._wall_list = mine.setupMineWalls(_all_sprites_list) + self._gate = mine.setupGate(_all_sprites_list) + self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) + self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) + self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) + self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) + self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) + wall_list.set(self._wall_list) + gate.set(self._gate) + mines.set(self._mine) + charger.set(self._charger) + wash.set(self._wash) + filt.set(self._filter) + store.set(self._store) + =} + + reaction(agv_sprite) {= + self._agv_collide.empty() + self._agv_collide.add(agv_sprite.value) + self._agv_sprite = agv_sprite.value + =} + + reaction(agv_sprite) -> score, game_over {= + # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) + + # Check the list of collisions. + #if len(blocks_hit_list) > 0: + # self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set(True) + + score.set(agv_sprite.value.total_stored) + =} + + reaction(people_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = mine.pygame.sprite.RenderPlain() + for person in people_sprites: + if person.is_present: + monsta_list.add(person.value) - controller.wall_list, controller.gate, controller.mines, - controller.charger, controller.wash, controller.filt, controller.store -> - display.static_sprites + monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - (display.tick)+ -> - controller.tick, - peoples.tick + if monsta_hit_list: + game_over.set(False) + + =} - #Send pause player to game controller and ghosts - (agv.playerpause)+ -> peoples.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - agv.sprite -> controller.agv_sprite - - (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites - controller.score -> display.score + reaction(restart) {= + if restart.value: + + self._score_to_win = 100 + #print(self._energizer_list) + self._score = 0 + =} + + reaction(shutdown) {= + mine.pygame.quit() + =} - peoples.icon_name, agv.icon_name -> display.icon_name +} - display.icon -> peoples.icon, agv.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over - - (agv.restart)+ -> controller.restart, display.restart, peoples.restart - - #controller.block_list -> agv.block_list +main reactor { + ### Controller + controller = new GameController() + + ### Model(s) + agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") + #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" + + # Ghosts + peoples = new[4] People( + width = {= people_specs[bank_index]["width"] =}, + height = {= people_specs[bank_index]["height"] =}, + directions = {= people_specs[bank_index]["directions"] =}, + name = {= people_specs[bank_index]["name"] =}, + character_class = ({=mine.People=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> peoples.wall_list + + # Send the sprites to the display to be drawn + #controller.block_list + agv.sprite, + peoples.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + agv.wall_list, agv.gate_list + + controller.wall_list, controller.gate, controller.mines, + controller.charger, controller.wash, controller.filt, controller.store -> + display.static_sprites + + (display.tick)+ -> + controller.tick, + peoples.tick + + #Send pause player to game controller and ghosts + (agv.playerpause)+ -> peoples.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + agv.sprite -> controller.agv_sprite + + (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites + + controller.score -> display.score + + peoples.icon_name, agv.icon_name -> display.icon_name + + display.icon -> peoples.icon, agv.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over + + (agv.restart)+ -> controller.restart, display.restart, peoples.restart + #controller.block_list -> agv.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Mining/SingleModeMine.lf b/experimental/Python/src/Mining/SingleModeMine.lf index 09ee0f78..71c797e9 100644 --- a/experimental/Python/src/Mining/SingleModeMine.lf +++ b/experimental/Python/src/Mining/SingleModeMine.lf @@ -13,208 +13,208 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] + files: ["include/hbphosphate.py", "include/images", "include/AIPhosphate.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbphosphate as mine - import AIPhosphate as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - people_specs = [ - { - "name": "Pinky", - "directions": mine.Pinky_directions, - "width": mine.w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": mine.Blinky_directions, - "width": mine.w, - "height": mine.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": mine.Inky_directions, - "width": mine.i_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": mine.Clyde_directions, - "width": mine.c_w, - "height": mine.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbphosphate as mine + import AIPhosphate as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + people_specs = [ + { + "name": "Pinky", + "directions": mine.Pinky_directions, + "width": mine.w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": mine.Blinky_directions, + "width": mine.w, + "height": mine.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": mine.Inky_directions, + "width": mine.i_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": mine.Clyde_directions, + "width": mine.c_w, + "height": mine.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) + mine.pygame.display.set_icon(agv_icon) + + self._clock = mine.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = mine.pygame.display.set_mode([606, 606]) + # Set the title of the window + mine.pygame.display.set_caption("Phosphate Mine") + # Create a surface we can draw on + background = mine.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(mine.white) + mine.pygame.font.init() + self._font = mine.pygame.font.Font("freesansbold.ttf", 18) + self._screen.fill(mine.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=mine.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - agv_icon = mine.pygame.image.load(os.path.join(dirname, self.nav_icon)) - mine.pygame.display.set_icon(agv_icon) - - self._clock = mine.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = mine.pygame.display.set_mode([606, 606]) - # Set the title of the window - mine.pygame.display.set_caption("Phosphate Mine") - # Create a surface we can draw on - background = mine.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(mine.white) - mine.pygame.font.init() - self._font = mine.pygame.font.Font("freesansbold.ttf", 18) - self._screen.fill(mine.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(mine.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + mine.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + print("adding static sprites") + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + print(self._static_sprites) + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, mine.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + + print("adding moving sprites") + self._screen.fill(mine.white) + agv = mine.pygame.sprite.Sprite() + sprite_list = mine.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): + agv = sprite.value + #print("agv battery is ", agv.battery) + sprite_list.add(sprite.value) + elif isinstance(sprite.value, mine.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(mine.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - mine.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - print("adding static sprites") - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - print(self._static_sprites) - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, mine.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - - print("adding moving sprites") - self._screen.fill(mine.white) - agv = mine.pygame.sprite.Sprite() - sprite_list = mine.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, mine.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, mine.AGV) and not isinstance(sprite.value, mine.People): - agv = sprite.value - #print("agv battery is ", agv.battery) - sprite_list.add(sprite.value) - elif isinstance(sprite.value, mine.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - print("paused") - text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, mine.black) - self._screen.blit(text3, [165, 333]) - mine.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = mine.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Area Cleared!", True, mine.black) - else: - text1=self._font.render("Collision detected.", True, mine.black) - self._screen.blit(text1, [235, 233]) - print("game is over") - text2=self._font.render("To play again, press ENTER.", True, mine.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, mine.black) - self._screen.blit(text3, [165, 333]) - - mine.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + self._screen.blit(self._font.render("Battery: " + str(agv.battery), True, mine.black), [10, 28]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + print("paused") + text2=self._font.render("Paused. Press SPACE to continue,", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, mine.black) + self._screen.blit(text3, [165, 333]) + mine.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = mine.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Area Cleared!", True, mine.black) + else: + text1=self._font.render("Collision detected.", True, mine.black) + self._screen.blit(text1, [235, 233]) + print("game is over") + text2=self._font.render("To play again, press ENTER.", True, mine.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, mine.black) + self._screen.blit(text3, [165, 333]) + + mine.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -222,468 +222,468 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=mine.AGV=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=mine.Player=})) { - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor AGV(width(0), height(0), image("images/Trollman.png"), character_class({=mine.AGV=}), event_list({=["mining", "washing", "filtering", "storing"]=}), energy_cost(-0.5), charge_at(20), risk(180)) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input[4] people_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - - logical action scheduler - - state _people({=[]=}) - state _layout({=mine.walls=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _find_moves(0) - state _prev_action({=["mining"]=}) - state _action({=["mining"]=}) - state _charging(False) - state _event_list({=self.event_list=}) - - output sprite - output icon_name - output playerpause - output restart - - method ensureCharge() {= - if self.character_instance.battery <= self.charge_at or self._charging: - #self._prev_action = [current_mode] - self._charging = True - return True - return False - =} - - method Charge() {= - self.character_instance.charge(1) - if self.character_instance.battery == "full" or self.character_instance.battery == 100: - self._charging = False - =} - - method atLocation() {= - if self._event_list[0] == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: - return True - elif self._event_list[0] == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: - return True - elif self._event_list[0] == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: - return True - elif self._event_list[0] == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: - return True - elif self._event_list[0] == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargespot: - return True - return False - =} - - method isClose() {= - if len(self._people) <= 0 or ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: - return False - return True - =} - - method Avoid() {= - self.character_instance.ai_avoid(self._layout, self._people, 7) - self.payCost() - =} - - method payCost() {= - if self.character_instance.last_move is not [0, 0]: - self.character_instance.charge(self.energy_cost) - =} - - method Approach() {= - if self._event_list[0] == "mining": - self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) - elif self._event_list[0] == "charging": - self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) - elif self._event_list[0] == "washing": - self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) - elif self._event_list[0] == "filtering": - self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) - elif self._event_list[0] == "storing": - self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) - - self.payCost() - print("finding the spot") - self._find_moves = self.character_instance.num_moves - =} - - method proceedNext() {= - temp = self._event_list[0] - self._event_list = self._event_list[1:] - self._event_list.append(temp) - =} - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart {= - print("in export mode") - #print("pause is ", self._pause) - #print("ai control is ", self._ai_control) - keyboard_events = mine.pygame.event.get() - for event in keyboard_events: - if event.type == mine.pygame.QUIT: - request_stop() - - if event.type == mine.pygame.KEYDOWN: - #print("detecting key down") - if event.key == mine.pygame.K_ESCAPE: - request_stop() - if event.key == mine.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - self.character_instance.charge("full") - #print(self.character_instance.rect.left) - if event.key == mine.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == mine.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, 30) + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input[4] people_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + + logical action scheduler + + state _people({=[]=}) + state _layout({=mine.walls=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _find_moves(0) + state _prev_action({=["mining"]=}) + state _action({=["mining"]=}) + state _charging(False) + state _event_list({=self.event_list=}) + + output sprite + output icon_name + output playerpause + output restart + + method ensureCharge() {= + if self.character_instance.battery <= self.charge_at or self._charging: + #self._prev_action = [current_mode] + self._charging = True + return True + return False + =} + + method Charge() {= + self.character_instance.charge(1) + if self.character_instance.battery == "full" or self.character_instance.battery == 100: + self._charging = False + =} + + method atLocation() {= + if self._event_list[0] == "mining" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.minespot: + return True + elif self._event_list[0] == "washing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.washspot: + return True + elif self._event_list[0] == "filtering" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.filterspot: + return True + elif self._event_list[0] == "storing" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.storespot: + return True + elif self._event_list[0] == "charging" and [self.character_instance.rect.left + 16, self.character_instance.rect.top + 16] == mine.chargespot: + return True + return False + =} + + method isClose() {= + if len(self._people) <= 0 or ai.euclid_close_people(self._people, self.character_instance.rect.left, self.character_instance.rect.top)[1] > self.risk: + return False + return True + =} + + method Avoid() {= + self.character_instance.ai_avoid(self._layout, self._people, 7) + self.payCost() + =} + + method payCost() {= + if self.character_instance.last_move is not [0, 0]: + self.character_instance.charge(self.energy_cost) + =} + + method Approach() {= + if self._event_list[0] == "mining": + self.character_instance.approach(self._layout, self._people, mine.minespot, self._find_moves) + elif self._event_list[0] == "charging": + self.character_instance.approach(self._layout, self._people, mine.chargerspot, self._find_moves) + elif self._event_list[0] == "washing": + self.character_instance.approach(self._layout, self._people, mine.washspot, self._find_moves) + elif self._event_list[0] == "filtering": + self.character_instance.approach(self._layout, self._people, mine.filterspot, self._find_moves) + elif self._event_list[0] == "storing": + self.character_instance.approach(self._layout, self._people, mine.storespot, self._find_moves) + + self.payCost() + print("finding the spot") + self._find_moves = self.character_instance.num_moves + =} + + method proceedNext() {= + temp = self._event_list[0] + self._event_list = self._event_list[1:] + self._event_list.append(temp) + =} + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> sprite, playerpause, restart {= + print("in export mode") + #print("pause is ", self._pause) + #print("ai control is ", self._ai_control) + keyboard_events = mine.pygame.event.get() + for event in keyboard_events: + if event.type == mine.pygame.QUIT: + request_stop() - if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: - self.character_instance.changespeed(0, -30) - - if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: - self.character_instance.charge(self.energy_cost) - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - print("battery is ", self.character_instance.battery) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active - if self.isClose(): - self.Avoid() - elif self.ensureCharge(): - #implement correctly for new design - self.Charge() - elif self.atLocation(): - self.proceedNext() - else: - self.Approach() - - - =} - - reaction(people_sprites) {= - self._ghosts = [] - for person in people_sprites: - if person.is_present: - self._people.append(person.value) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} + if event.type == mine.pygame.KEYDOWN: + #print("detecting key down") + if event.key == mine.pygame.K_ESCAPE: + request_stop() + if event.key == mine.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + self.character_instance.charge("full") + #print(self.character_instance.rect.left) + if event.key == mine.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == mine.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, 30) + if event.type == mine.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == mine.pygame.K_LEFT or event.key == mine.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == mine.pygame.K_RIGHT or event.key == mine.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == mine.pygame.K_UP or event.key == mine.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == mine.pygame.K_DOWN or event.key == mine.pygame.K_s: + self.character_instance.changespeed(0, -30) + + if not self._ai_control and not self._pause and self._active and self.character_instance.battery > 0: + self.character_instance.charge(self.energy_cost) + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + print("battery is ", self.character_instance.battery) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self.character_instance.battery > 0 and self._ai_control: #and self._active + if self.isClose(): + self.Avoid() + elif self.ensureCharge(): + #implement correctly for new design + self.Charge() + elif self.atLocation(): + self.proceedNext() + else: + self.Approach() + + + =} + + reaction(people_sprites) {= + self._ghosts = [] + for person in people_sprites: + if person.is_present: + self._people.append(person.value) + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() + =} + } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor People (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} } #### Controller reactor GameController(number_of_people(4)) { - output wall_list # List of walls on the map - output gate - output mines - output charger - output wash - output filt - output store - output score # The game score - output game_over - - input[number_of_people] people_sprites - input agv_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _mine - state _charger - state _wash - state _filter - state _store - state _score_to_win(0) - state _score(0) - state _agv_sprite - state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - - reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= - _all_sprites_list = mine.pygame.sprite.RenderPlain() - self._wall_list = mine.setupMineWalls(_all_sprites_list) - self._gate = mine.setupGate(_all_sprites_list) - self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) - self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) - self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) - self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) - self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) - wall_list.set(self._wall_list) - gate.set(self._gate) - mines.set(self._mine) - charger.set(self._charger) - wash.set(self._wash) - filt.set(self._filter) - store.set(self._store) - =} - - reaction(agv_sprite) {= - self._agv_collide.empty() - self._agv_collide.add(agv_sprite.value) - self._agv_sprite = agv_sprite.value - =} - - reaction(agv_sprite) -> score, game_over {= - # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - - # Check the list of collisions. - #if len(blocks_hit_list) > 0: - # self._score +=len(blocks_hit_list) - - #if self._score == self._score_to_win: - # game_over.set(True) + output wall_list # List of walls on the map + output gate + output mines + output charger + output wash + output filt + output store + output score # The game score + output game_over + + input[number_of_people] people_sprites + input agv_sprite + input tick # The game tick + input restart - score.set(self._score) - =} - - reaction(people_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = mine.pygame.sprite.RenderPlain() - for person in people_sprites: - if person.is_present: - monsta_list.add(person.value) + state _wall_list + state _gate + state _mine + state _charger + state _wash + state _filter + state _store + state _score_to_win(0) + state _score(0) + state _agv_sprite + state _agv_collide({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_list({=mine.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + + reaction(startup) -> wall_list, gate, mines, charger, wash, filt, store {= + _all_sprites_list = mine.pygame.sprite.RenderPlain() + self._wall_list = mine.setupMineWalls(_all_sprites_list) + self._gate = mine.setupGate(_all_sprites_list) + self._mine = mine.ActionPlace("mine", mine.burgundy, 51, 81, 30, 30) + self._charger = mine.ActionPlace("charger", mine.green, 51, 501, 30, 30) + self._wash = mine.ActionPlace("wash", mine.wash_blue, 261, 201, 30, 30) + self._filter = mine.ActionPlace("filter", mine.filter_orange, 411, 531, 30, 30) + self._store = mine.ActionPlace("store", mine.gray, 531, 111, 30, 30) + wall_list.set(self._wall_list) + gate.set(self._gate) + mines.set(self._mine) + charger.set(self._charger) + wash.set(self._wash) + filt.set(self._filter) + store.set(self._store) + =} + + reaction(agv_sprite) {= + self._agv_collide.empty() + self._agv_collide.add(agv_sprite.value) + self._agv_sprite = agv_sprite.value + =} + + reaction(agv_sprite) -> score, game_over {= + # blocks_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, self._block_list, True) - monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) + # Check the list of collisions. + #if len(blocks_hit_list) > 0: + # self._score +=len(blocks_hit_list) + + #if self._score == self._score_to_win: + # game_over.set(True) + + score.set(self._score) + =} + + reaction(people_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = mine.pygame.sprite.RenderPlain() + for person in people_sprites: + if person.is_present: + monsta_list.add(person.value) + + monsta_hit_list = mine.pygame.sprite.spritecollide(self._agv_sprite, monsta_list, False) - if monsta_hit_list: - game_over.set(False) + if monsta_hit_list: + game_over.set(False) - =} + =} - - reaction(restart) {= - if restart.value: - - self._score_to_win = 5 - #print(self._energizer_list) - self._score = 0 - =} - reaction(shutdown) {= - mine.pygame.quit() - =} + reaction(restart) {= + if restart.value: + + self._score_to_win = 5 + #print(self._energizer_list) + self._score = 0 + =} + + reaction(shutdown) {= + mine.pygame.quit() + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") - #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" - - # Ghosts - peoples = new[4] People( - width = {= people_specs[bank_index]["width"] =}, - height = {= people_specs[bank_index]["height"] =}, - directions = {= people_specs[bank_index]["directions"] =}, - name = {= people_specs[bank_index]["name"] =}, - character_class = ({=mine.People=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> peoples.wall_list - - # Send the sprites to the display to be drawn - #controller.block_list - agv.sprite, - peoples.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - agv.wall_list, agv.gate_list - - controller.wall_list, controller.gate, controller.mines, - controller.charger, controller.wash, controller.filt, controller.store -> - display.static_sprites - - (display.tick)+ -> - controller.tick, - peoples.tick - - #Send pause player to game controller and ghosts - (agv.playerpause)+ -> peoples.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - agv.sprite -> controller.agv_sprite + ### Controller + controller = new GameController() + + ### Model(s) + agv = new AGV(width = {=mine.w=}, height = {=mine.p_h=}, image = "images/roomb1.png") + #width = {=mine.w=}, height = {=mine.p_h=}, image = "images/mine.png" + + # Ghosts + peoples = new[4] People( + width = {= people_specs[bank_index]["width"] =}, + height = {= people_specs[bank_index]["height"] =}, + directions = {= people_specs[bank_index]["directions"] =}, + name = {= people_specs[bank_index]["name"] =}, + character_class = ({=mine.People=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 5, num_static_sprites = 7 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> peoples.wall_list + + # Send the sprites to the display to be drawn + #controller.block_list + agv.sprite, + peoples.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + agv.wall_list, agv.gate_list + + controller.wall_list, controller.gate, controller.mines, + controller.charger, controller.wash, controller.filt, controller.store -> + display.static_sprites + + (display.tick)+ -> + controller.tick, + peoples.tick + + #Send pause player to game controller and ghosts + (agv.playerpause)+ -> peoples.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + agv.sprite -> controller.agv_sprite - (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites - - controller.score -> display.score - - peoples.icon_name, agv.icon_name -> display.icon_name - - display.icon -> peoples.icon, agv.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over - - (agv.restart)+ -> controller.restart, display.restart, peoples.restart - - #controller.block_list -> agv.block_list + (peoples.sprite)+ -> controller.people_sprites, agv.people_sprites + + controller.score -> display.score + + peoples.icon_name, agv.icon_name -> display.icon_name + + display.icon -> peoples.icon, agv.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, agv.game_over, peoples.game_over + + (agv.restart)+ -> controller.restart, display.restart, peoples.restart + #controller.block_list -> agv.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/BankedObserver.lf b/experimental/Python/src/Pac-Man/BankedObserver.lf index 66977715..ba6b1057 100644 --- a/experimental/Python/src/Pac-Man/BankedObserver.lf +++ b/experimental/Python/src/Pac-Man/BankedObserver.lf @@ -13,520 +13,520 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images"] + files: ["include/hbpacman.py", "include/images"] }; preamble {= - import os - import pyautogui - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - #input controllerpause - - output tick - output[5] icon - - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + #input controllerpause + + output tick + output[5] icon + + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - if self._active == True: - pyautogui.keyDown(' ') - self._active = False - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER or SPACE.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + if self._active == True: + pyautogui.keyDown(' ') + self._active = False + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER or SPACE.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} } #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - input pause - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pause) {= - self._pause = pause - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + input pause + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pause) {= + self._pause = pause + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } ## Player # Should be replacable with an AI reactor Player extends BaseCharacter { - timer pygame_event(0, 100 msec) - state _active(True) - - state _speed(30) - state _boosted(False) - - reaction(pygame_event) -> sprite {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_b and self._boosted == False: - self._speed += 15 - self._boosted = True - elif event.key == pacman.pygame.K_b and self._boosted == True: - self._speed -= 15 - self._boosted = False - elif self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - print("left") - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - print("right") - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed) - - if event.type == pacman.pygame.KEYUP and self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_b: - self._speed -= 10 - - if self._pause is False: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - =} + timer pygame_event(0, 100 msec) + state _active(True) + + state _speed(30) + state _boosted(False) + + reaction(pygame_event) -> sprite {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_b and self._boosted == False: + self._speed += 15 + self._boosted = True + elif event.key == pacman.pygame.K_b and self._boosted == True: + self._speed -= 15 + self._boosted = False + elif self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + print("left") + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + print("right") + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed) + + if event.type == pacman.pygame.KEYUP and self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_b: + self._speed -= 10 + + if self._pause is False: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=})) extends BaseCharacter { - input tick - #input pause # pause from player - - state turn(0) - state steps(0) - - reaction(tick) -> sprite {= - if self._pause is False: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} - + input tick + #input pause # pause from player + + state turn(0) + state steps(0) + + reaction(tick) -> sprite {= + if self._pause is False: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(2) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(2) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) # state _controllerpause({=False=}) #not necessary - - reaction(startup, restart) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) + + reaction(startup, restart) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} + + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} + + reaction(startup, restart) -> block_list {= + # Draw the grid + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup, restart) -> block_list {= - # Draw the grid - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - - block = pacman.Block(pacman.purple, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") - - self._score_to_win = len(self._block_list) - =} + block = pacman.Block(pacman.purple, 4, 4) - reaction(pacman_sprite) -> score, game_over {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set("Lost!") - #pyautogui.keyDown(' ') - #print("Lost") - #self._controllerpause = True - #Pause the game instead of stopping it - #controllerpause.set(True) - #request_stop() #remove once above finished - - =} - - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - - reaction(shutdown) {= - pacman.pygame.quit() - =} + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + =} + + reaction(pacman_sprite) -> score, game_over {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list: + game_over.set("Lost!") + #pyautogui.keyDown(' ') + #print("Lost") + #self._controllerpause = True + #Pause the game instead of stopping it + #controllerpause.set(True) + #request_stop() #remove once above finished + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + + reaction(shutdown) {= + pacman.pygame.quit() + =} } reactor Watcher { - - state _pause(False) - state _active(True) - - input game_over - logical action delay - - output restart - output pause - - timer watch_tick(0, 100msec) - reaction(game_over) -> restart {= - if game_over.is_present: - print("game over") - self._active = False - pause.set(True) - for event in pacman.pygame.event.get(): - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - print("yikes") - request_stop() - elif event.type == pacman.pygame.K_SPACE or event.type == pacman.pygame.K_r: - print("restart") - restart.set(True) - =} + + state _pause(False) + state _active(True) + + input game_over + logical action delay + + output restart + output pause + + timer watch_tick(0, 100msec) + reaction(game_over) -> restart {= + if game_over.is_present: + print("game over") + self._active = False + pause.set(True) + for event in pacman.pygame.event.get(): + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + print("yikes") + request_stop() + elif event.type == pacman.pygame.K_SPACE or event.type == pacman.pygame.K_r: + print("restart") + restart.set(True) + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - image = {= ghost_specs[bank_index]["image"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - character_class = ({=pacman.Ghost=}) - ) - - observer = new Watcher() - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + image = {= ghost_specs[bank_index]["image"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + character_class = ({=pacman.Ghost=}) + ) + + observer = new Watcher() + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + player.sprite -> controller.pacman_sprite - ghosts.sprite -> controller.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, observer.game_over - - observer.restart -> controller.restart - - (observer.pause)+ -> player.pause, ghosts.pause - + ghosts.sprite -> controller.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, observer.game_over + + observer.restart -> controller.restart + + (observer.pause)+ -> player.pause, ghosts.pause + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/ContainedPlayer.lf b/experimental/Python/src/Pac-Man/ContainedPlayer.lf index 7d2b50db..eb3f6fec 100644 --- a/experimental/Python/src/Pac-Man/ContainedPlayer.lf +++ b/experimental/Python/src/Pac-Man/ContainedPlayer.lf @@ -3,319 +3,319 @@ */ target Python { - files: ["include/AIPacSupport.py", "include/hbpacman.py"] + files: ["include/AIPacSupport.py", "include/hbpacman.py"] }; preamble {= - # must import some file for setup functions - import AIPacSupport as ai - import hbpacman as pacman + # must import some file for setup functions + import AIPacSupport as ai + import hbpacman as pacman =} ### Ticker Reactor reactor Ticker(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer ticker(0, 100msec) - - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input[4] ghost_sprites - input icon - input eat_result - input avoid_result - input chase_result - input game_over - input character - - state character_instance - state _wall_list - state _gate_list - state _ghosts({=[]=}) - state _pause(False) - state _active(True) - state _ai_control(True) - state _layout({=pacman.walls=}) + timer ticker(0, 100msec) + + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input[4] ghost_sprites + input icon + input eat_result + input avoid_result + input chase_result + input game_over + input character + + state character_instance + state _wall_list + state _gate_list + state _ghosts({=[]=}) + state _pause(False) + state _active(True) + state _ai_control(True) + state _layout({=pacman.walls=}) - output[7] result - output sprite - output icon_name - output playerpause - output restart - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + output[7] result + output sprite + output icon_name + output playerpause + output restart + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(ticker) -> playerpause, restart, sprite, result {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - print(self.character_instance.rect.left) - - if event.key == pacman.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == pacman.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(ticker) -> playerpause, restart, sprite, result {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + print(self.character_instance.rect.left) + + if event.key == pacman.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == pacman.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == pacman.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, -30) if not self._ai_control and not self._pause and self._active: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - playerpause.set(self._pause) - sprite.set(self.character_instance) - if not self._pause and self._active and self._ai_control: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == pacman.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, -30) + if not self._ai_control and not self._pause and self._active: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + playerpause.set(self._pause) + sprite.set(self.character_instance) + if not self._pause and self._active and self._ai_control: - for i, port in enumerate(result): - if i == 0: - port.set(True) - elif i == 1: - port.set(self.character_instance) - elif i == 2: - port.set(self._layout) - elif (i - 3) < len(self._ghosts): - port.set(self._ghosts[i - 3]) + for i, port in enumerate(result): + if i == 0: + port.set(True) + elif i == 1: + port.set(self.character_instance) + elif i == 2: + port.set(self._layout) + elif (i - 3) < len(self._ghosts): + port.set(self._ghosts[i - 3]) - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for ghost in ghost_sprites: - if ghost.is_present: - self._ghosts.append(ghost.value) - =} + =} + + reaction(ghost_sprites) {= + self._ghosts = [] + for ghost in ghost_sprites: + if ghost.is_present: + self._ghosts.append(ghost.value) + =} - reaction(eat_result, chase_result, avoid_result) {= - if self._pause is False: - if avoid_result.is_present: - self.character_instance = avoid_result.value - elif chase_result.is_present: - self.character_instance = chase_result.value - elif eat_result.is_present: - self.character_instance = eat_result.value - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} + reaction(eat_result, chase_result, avoid_result) {= + if self._pause is False: + if avoid_result.is_present: + self.character_instance = avoid_result.value + elif chase_result.is_present: + self.character_instance = chase_result.value + elif eat_result.is_present: + self.character_instance = eat_result.value + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() + =} } ### Ghost Close? Condition Reactor reactor Close { - input[7] tick - output[7] result - - reaction(tick) -> result {= - all_present = True - #result_bool = True - ghost_list = [] - for i, port in enumerate(tick): - if port.is_present: - if i >= 3: - ghost_list.append(tick[i].value) - #print(tick[i].value.rect.left) - else: - all_present = False - break - if all_present: - #print(len(ai.closeghostdist(tick[2].value, ghost_list, tick[1].value.rect.left, tick[1].value.rect.top, 7)), " is dist") - if len(ai.closeghostdist(tick[2].value, ghost_list, tick[1].value.rect.left, tick[1].value.rect.top, 6)) > 5: - for i, port in enumerate(result): - if i == 0: - port.set(False) - else: - port.set(tick[i].value) - else: - print("ghosts are close") - for i, port in enumerate(result): - port.set(tick[i].value) - #result.set(tick) - =} + input[7] tick + output[7] result + + reaction(tick) -> result {= + all_present = True + #result_bool = True + ghost_list = [] + for i, port in enumerate(tick): + if port.is_present: + if i >= 3: + ghost_list.append(tick[i].value) + #print(tick[i].value.rect.left) + else: + all_present = False + break + if all_present: + #print(len(ai.closeghostdist(tick[2].value, ghost_list, tick[1].value.rect.left, tick[1].value.rect.top, 7)), " is dist") + if len(ai.closeghostdist(tick[2].value, ghost_list, tick[1].value.rect.left, tick[1].value.rect.top, 6)) > 5: + for i, port in enumerate(result): + if i == 0: + port.set(False) + else: + port.set(tick[i].value) + else: + print("ghosts are close") + for i, port in enumerate(result): + port.set(tick[i].value) + #result.set(tick) + =} } ### Ghost Scared? Condition Reactor reactor Scared { - input[7] tick - input frenzy - state _frenzy(False) - output[7] result - - reaction(tick) -> result {= - all_present = True - for i, port in enumerate(tick): - if port.is_present: - continue - else: - all_present = False - break - if all_present and tick[0].value: - if self._frenzy: - print("ghosts are scared") - for i, port in enumerate(result): - port.set(tick[i].value) - #result.set(tick) - else: - print("ghosts not scared") - for i, port in enumerate(result): - if i == 0: - port.set(False) - else: - port.set(tick[i].value) - =} + input[7] tick + input frenzy + state _frenzy(False) + output[7] result + + reaction(tick) -> result {= + all_present = True + for i, port in enumerate(tick): + if port.is_present: + continue + else: + all_present = False + break + if all_present and tick[0].value: + if self._frenzy: + print("ghosts are scared") + for i, port in enumerate(result): + port.set(tick[i].value) + #result.set(tick) + else: + print("ghosts not scared") + for i, port in enumerate(result): + if i == 0: + port.set(False) + else: + port.set(tick[i].value) + =} - reaction(frenzy) {= - self._frenzy = frenzy.value - =} + reaction(frenzy) {= + self._frenzy = frenzy.value + =} } ### Chase Ghost Action Reactor reactor Chase { - input[7] tick - state character_instance - output result - - reaction(tick) -> result {= - if tick[0].is_present and tick[0].value: - all_present = True - ghost_list = [] - for i, port in enumerate(tick): - if port.is_present: - if i >= 3: - ghost_list.append(tick[i].value) - else: - all_present = False - break - if all_present: - self.character_instance = tick[1].value - self.character_instance.ai_chase(tick[2].value, ghost_list, 7) - result.set(self.character_instance) - =} + input[7] tick + state character_instance + output result + + reaction(tick) -> result {= + if tick[0].is_present and tick[0].value: + all_present = True + ghost_list = [] + for i, port in enumerate(tick): + if port.is_present: + if i >= 3: + ghost_list.append(tick[i].value) + else: + all_present = False + break + if all_present: + self.character_instance = tick[1].value + self.character_instance.ai_chase(tick[2].value, ghost_list, 7) + result.set(self.character_instance) + =} } ### Avoid Ghost Action Reactor reactor Avoid { - input[7] tick - state character_instance - output result - - reaction(tick) -> result {= - print("avoid got tick") - if tick[0].is_present and not tick[0].value: - all_present = True - ghost_list = [] - for i, port in enumerate(tick): - if port.is_present: - if i >= 3: - ghost_list.append(tick[i].value) - else: - all_present = False - break - if all_present: - print("avoiding") - self.character_instance = tick[1].value - self.character_instance.ai_avoid(tick[2].value, ghost_list, 7) - result.set(self.character_instance) - =} + input[7] tick + state character_instance + output result + + reaction(tick) -> result {= + print("avoid got tick") + if tick[0].is_present and not tick[0].value: + all_present = True + ghost_list = [] + for i, port in enumerate(tick): + if port.is_present: + if i >= 3: + ghost_list.append(tick[i].value) + else: + all_present = False + break + if all_present: + print("avoiding") + self.character_instance = tick[1].value + self.character_instance.ai_avoid(tick[2].value, ghost_list, 7) + result.set(self.character_instance) + =} } ### Eat Pills Action Reactor reactor Eat { - input[7] tick - input block_list - input wall_list - input sprite - state _wall_list - state _block_list - state _eat_moves(0) - state character_instance - output result + input[7] tick + input block_list + input wall_list + input sprite + state _wall_list + state _block_list + state _eat_moves(0) + state character_instance + output result - reaction(wall_list) {= - self._wall_list = wall_list.value - =} - - reaction(tick) block_list -> result {= - if tick[0].is_present and not tick[0].value: - all_present = True - ghost_list = [] - for i, port in enumerate(tick): - if port.is_present: - if i >= 3: - ghost_list.append(tick[i].value) - else: - all_present = False - break - if all_present and block_list.is_present: - path = ai.closestpillpath(pacman.walls, ghost_list, self.character_instance.rect.left, self.character_instance.rect.top, block_list.value) - self.character_instance.ai_eat(pacman.walls, ghost_list, self._block_list, self._eat_moves) - #self.character_instance.speedzero() - #print(self.character_instance.change_x, " change") - #print("move is ", path[0]) - #self.character_instance.changespeed(path[0][0], path[0][1]) - #self.character_instance.update(self._wall_list) - self._eat_moves = self.character_instance.get_num_moves() - result.set(self.character_instance) - =} + reaction(wall_list) {= + self._wall_list = wall_list.value + =} + + reaction(tick) block_list -> result {= + if tick[0].is_present and not tick[0].value: + all_present = True + ghost_list = [] + for i, port in enumerate(tick): + if port.is_present: + if i >= 3: + ghost_list.append(tick[i].value) + else: + all_present = False + break + if all_present and block_list.is_present: + path = ai.closestpillpath(pacman.walls, ghost_list, self.character_instance.rect.left, self.character_instance.rect.top, block_list.value) + self.character_instance.ai_eat(pacman.walls, ghost_list, self._block_list, self._eat_moves) + #self.character_instance.speedzero() + #print(self.character_instance.change_x, " change") + #print("move is ", path[0]) + #self.character_instance.changespeed(path[0][0], path[0][1]) + #self.character_instance.update(self._wall_list) + self._eat_moves = self.character_instance.get_num_moves() + result.set(self.character_instance) + =} - reaction(sprite) {= - #print("received sprite") - self.character_instance = sprite.value - =} + reaction(sprite) {= + #print("received sprite") + self.character_instance = sprite.value + =} - reaction(block_list) {= - self._block_list = block_list.value - =} - + reaction(block_list) {= + self._block_list = block_list.value + =} + } diff --git a/experimental/Python/src/Pac-Man/Foomba.lf b/experimental/Python/src/Pac-Man/Foomba.lf index ebd29a3a..9347afda 100644 --- a/experimental/Python/src/Pac-Man/Foomba.lf +++ b/experimental/Python/src/Pac-Man/Foomba.lf @@ -13,199 +13,199 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] + files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] }; preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - import AIPacSupport as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/wheelchair.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/wheelchair.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/wheelchair.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + import AIPacSupport as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/wheelchair.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/wheelchair.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/wheelchair.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Foomba") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.white) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.white) - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Foomba") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.white) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.white) + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.black) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.white) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.black) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.white) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("Paused. Press SPACE to continue,", True, pacman.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("Press M to toggle AI.", True, pacman.black) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - if game_over.value: - text1=self._font.render("Area Cleared!", True, pacman.black) - else: - text1=self._font.render("Collision detected.", True, pacman.black) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER.", True, pacman.black) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.black) - self._screen.blit(text3, [165, 333]) - - pacman.pygame.display.flip() - - =} - - reaction(restart) {= - self._game_over = False - =} + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("Paused. Press SPACE to continue,", True, pacman.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("Press M to toggle AI.", True, pacman.black) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + if game_over.value: + text1=self._font.render("Area Cleared!", True, pacman.black) + else: + text1=self._font.render("Collision detected.", True, pacman.black) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER.", True, pacman.black) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.black) + self._screen.blit(text3, [165, 333]) + + pacman.pygame.display.flip() + + =} + + reaction(restart) {= + self._game_over = False + =} } @@ -213,491 +213,491 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/user.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon - - state _frenzy(False) - state _ghosts({=[]=}) - state _layout({=pacman.walls=}) - state _block_list({=[]=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _resetting(False) - state _eat_moves(0) - state _avoid_moves(0) - - output sprite - output icon_name - output playerpause - output restart - - initial mode export { - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= - print("in export mode") - print("pause is ", self._pause) - print("ai control is ", self._ai_control) - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - print("detecting key down") - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - #print(self.character_instance.rect.left) - if event.key == pacman.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == pacman.pygame.K_m: - self._pause = False - self._ai_control = not self._ai_control - if not self._ai_control and not self._pause and self._active: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == pacman.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_b: - self._speed -= 10 - - if not self._ai_control and not self._pause and self._active: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - print("setting sprite") - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause: #and self._active - if self._ai_control: - print("changing to close") - Close.set() - - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for ghost in ghost_sprites: - if ghost.is_present: - self._ghosts.append(ghost.value) - =} - - reaction(block_list) {= - self._block_list = block_list.value - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} - - } mode Close { - - reaction(reset) -> reset(Eat), reset(Avoid), history(export) {= - print("in mode close") - if len(self._block_list) > 0 and len(self._ghosts) > 0: - if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: - Eat.set() - else: - print("ghost close") - Avoid.set() - else: - export.set() - =} - - } mode Eat { + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon + + state _frenzy(False) + state _ghosts({=[]=}) + state _layout({=pacman.walls=}) + state _block_list({=[]=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _resetting(False) + state _eat_moves(0) + state _avoid_moves(0) + + output sprite + output icon_name + output playerpause + output restart + + initial mode export { + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} - reaction(reset) -> history(export) {= - print("in mode eat") - self.character_instance.ai_eat(pacman.walls, self._ghosts, self._block_list, self._eat_moves) - self._eat_moves = self.character_instance.get_num_moves() - export.set() - =} + reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= + print("in export mode") + print("pause is ", self._pause) + print("ai control is ", self._ai_control) + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + print("detecting key down") + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + #print(self.character_instance.rect.left) + if event.key == pacman.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == pacman.pygame.K_m: + self._pause = False + self._ai_control = not self._ai_control + if not self._ai_control and not self._pause and self._active: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == pacman.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_b: + self._speed -= 10 + + if not self._ai_control and not self._pause and self._active: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + print("setting sprite") + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause: #and self._active + if self._ai_control: + print("changing to close") + Close.set() - } mode Avoid { - - reaction(reset) -> history(export) {= - print("avoid time") - self.character_instance.ai_avoid(self._layout, self._ghosts, 7) - #self._avoid_moves = self.character_instance.get_num_moves() - export.set() - =} + =} - } -} + reaction(ghost_sprites) {= + self._ghosts = [] + for ghost in ghost_sprites: + if ghost.is_present: + self._ghosts.append(ghost.value) + =} -## Ghosts -# FIXME: Different Ghosts should have different personalities -reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - input frenzy - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - state _frenzy(False) - - reaction(playerpause) {= - if playerpause.is_present: - self._pause = playerpause.value + reaction(block_list) {= + self._block_list = block_list.value =} - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) + reaction(frenzy) {= + self._frenzy = frenzy.value =} - + reaction(game_over) {= - self._active = False + self._active = False + self._pause = True + self.character_instance.speedzero() =} - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) + } mode Close { + + reaction(reset) -> reset(Eat), reset(Avoid), history(export) {= + print("in mode close") + if len(self._block_list) > 0 and len(self._ghosts) > 0: + if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 7)) > 6: + Eat.set() + else: + print("ghost close") + Avoid.set() + else: + export.set() =} - - reaction(frenzy) {= - self._frenzy = frenzy.value + + } mode Eat { + + reaction(reset) -> history(export) {= + print("in mode eat") + self.character_instance.ai_eat(pacman.walls, self._ghosts, self._block_list, self._eat_moves) + self._eat_moves = self.character_instance.get_num_moves() + export.set() =} + } mode Avoid { + + reaction(reset) -> history(export) {= + print("avoid time") + self.character_instance.ai_avoid(self._layout, self._ghosts, 7) + #self._avoid_moves = self.character_instance.get_num_moves() + export.set() + =} + + } +} + +## Ghosts +# FIXME: Different Ghosts should have different personalities +reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { + input tick + input playerpause # pause from player + input game_over + input restart + input frenzy + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + state _frenzy(False) + + reaction(playerpause) {= + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - logical action end_frenzy - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - state _frenzy(False) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + logical action end_frenzy + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + state _frenzy(False) - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomFoomba(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomFoomba(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} + wall_list.set(self._wall_list) + gate.set(self._gate) - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} + =} + + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} + + reaction(startup) -> block_list {= + # Draw the grid - reaction(startup) -> block_list {= - # Draw the grid + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + block = pacman.Block(pacman.brown, 6, 6) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - block = pacman.Block(pacman.brown, 6, 6) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn + if b_collide: + continue + if p_collide: + continue - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite) -> score, game_over {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set(True) + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn - score.set(self._score) - =} + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + reaction(pacman_sprite) -> score, game_over {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set(True) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - if monsta_hit_list and not self._frenzy: - game_over.set(False) - elif monsta_hit_list and self._frenzy: - for monsta in monsta_hit_list: - monsta.moveoffgrid() + if monsta_hit_list and not self._frenzy: + game_over.set(False) + elif monsta_hit_list and self._frenzy: + for monsta in monsta_hit_list: + monsta.moveoffgrid() - =} + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - block = pacman.Block(pacman.brown, 6, 6) + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._frenzy = False - self._score = 0 - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + block = pacman.Block(pacman.brown, 6, 6) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._frenzy = False + self._score = 0 + =} + + reaction(shutdown) {= + pacman.pygame.quit() + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/roomb1.png") - #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/roomb1.png") + #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - - controller.block_list -> player.block_list + (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + controller.block_list -> player.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/KielPacManTree.lf b/experimental/Python/src/Pac-Man/KielPacManTree.lf index 0893673e..3bc96ee9 100644 --- a/experimental/Python/src/Pac-Man/KielPacManTree.lf +++ b/experimental/Python/src/Pac-Man/KielPacManTree.lf @@ -1,279 +1,279 @@ /** PacMan Dresden Reactor **/ target Python{ - files: ["include/hbpacman.py", "include/AIPacSupport.py"] + files: ["include/hbpacman.py", "include/AIPacSupport.py"] }; preamble {= - import hbpacman as pacman - import AIPacSupport as ai + import hbpacman as pacman + import AIPacSupport as ai =} reactor BehaviorNode { - input start - output success - output failure + input start + output success + output failure } reactor MergeOr { - input right - input left - output merged - - reaction(left, right) -> merged {= - if left.is_present: - merged.set(left.value) - else: - merged.set(right.value) - =} + input right + input left + output merged + + reaction(left, right) -> merged {= + if left.is_present: + merged.set(left.value) + else: + merged.set(right.value) + =} } reactor EatPills extends BehaviorNode { - input[4] ghost_sprites - input pacman_sprite - input block_list - input init_sprite - - #output sprite - - state character_instance - state _ghosts({=[]=}) - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _eat_moves(0) - - reaction(start) -> success, failure {= - print("eat ghosts leng is ", len(self._ghosts)) - print("character is ", self.character_instance) - if len(self._ghosts) > 0 and self.character_instance is not None: - self.character_instance.ai_eat(pacman.walls, self._ghosts, self._block_list, self._eat_moves) - self._eat_moves = self.character_instance.get_num_moves() - #sprite.set(self.character_instance) - print("eat success, instance is ", self.character_instance) - success.set(self.character_instance) - else: - print("eat has failed") - failure.set(True) - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for sprite in ghost_sprites: - if sprite.is_present: - self._ghosts.append(sprite.value) - =} - - reaction(block_list) {= - self._block_list = block_list.value - =} - - reaction(pacman_sprite, init_sprite) {= - print("eat has received character") - if pacman_sprite.is_present: - self.character_instance = pacman_sprite.value - else: - self.character_instance = init_sprite.value - =} + input[4] ghost_sprites + input pacman_sprite + input block_list + input init_sprite + + #output sprite + + state character_instance + state _ghosts({=[]=}) + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _eat_moves(0) + + reaction(start) -> success, failure {= + print("eat ghosts leng is ", len(self._ghosts)) + print("character is ", self.character_instance) + if len(self._ghosts) > 0 and self.character_instance is not None: + self.character_instance.ai_eat(pacman.walls, self._ghosts, self._block_list, self._eat_moves) + self._eat_moves = self.character_instance.get_num_moves() + #sprite.set(self.character_instance) + print("eat success, instance is ", self.character_instance) + success.set(self.character_instance) + else: + print("eat has failed") + failure.set(True) + =} + + reaction(ghost_sprites) {= + self._ghosts = [] + for sprite in ghost_sprites: + if sprite.is_present: + self._ghosts.append(sprite.value) + =} + + reaction(block_list) {= + self._block_list = block_list.value + =} + + reaction(pacman_sprite, init_sprite) {= + print("eat has received character") + if pacman_sprite.is_present: + self.character_instance = pacman_sprite.value + else: + self.character_instance = init_sprite.value + =} } reactor AvoidGhost extends BehaviorNode { - input pacman_sprite - input[4] ghost_sprites - - #output sprite - - state character_instance - state _ghosts({=[]=}) - - reaction(start) -> success, failure {= - if len(self._ghosts) > 0: - self.character_instance.ai_avoid(pacman.walls, self._ghosts, 7) - #sprite.set(self.character_instance) - success.set(self.character_instance) - else: - failure.set(True) - =} + input pacman_sprite + input[4] ghost_sprites + + #output sprite + + state character_instance + state _ghosts({=[]=}) + + reaction(start) -> success, failure {= + if len(self._ghosts) > 0: + self.character_instance.ai_avoid(pacman.walls, self._ghosts, 7) + #sprite.set(self.character_instance) + success.set(self.character_instance) + else: + failure.set(True) + =} - reaction(pacman_sprite) {= - self.character_instance = pacman_sprite.value - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for sprite in ghost_sprites: - if sprite.is_present: - self._ghosts.append(sprite.value) - =} + reaction(pacman_sprite) {= + self.character_instance = pacman_sprite.value + =} + + reaction(ghost_sprites) {= + self._ghosts = [] + for sprite in ghost_sprites: + if sprite.is_present: + self._ghosts.append(sprite.value) + =} } reactor ChaseGhost extends BehaviorNode { - input pacman_sprite - input[4] ghost_sprites - - #output sprite - - state character_instance - state _ghosts({=[]=}) - - reaction(start) -> success, failure {= - if len(self._ghosts) > 0: - self.character_instance.ai_chase(pacman.walls, self._ghosts, 7) - #sprite.set(self.character_instance) - success.set(self.character_instance) - else: - failure.set(True) - =} - - reaction(pacman_sprite) {= - self.character_instance = pacman_sprite.value - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for sprite in ghost_sprites: - if sprite.is_present: - self._ghosts.append(sprite.value) - =} + input pacman_sprite + input[4] ghost_sprites + + #output sprite + + state character_instance + state _ghosts({=[]=}) + + reaction(start) -> success, failure {= + if len(self._ghosts) > 0: + self.character_instance.ai_chase(pacman.walls, self._ghosts, 7) + #sprite.set(self.character_instance) + success.set(self.character_instance) + else: + failure.set(True) + =} + + reaction(pacman_sprite) {= + self.character_instance = pacman_sprite.value + =} + + reaction(ghost_sprites) {= + self._ghosts = [] + for sprite in ghost_sprites: + if sprite.is_present: + self._ghosts.append(sprite.value) + =} } reactor GhostClose extends BehaviorNode { - input pacman_sprite - input[4] ghost_sprites - - #output sprite - - state character_instance - state _ghosts({=[]=}) - - reaction(start) -> success, failure {= - if len(self._ghosts) > 0 and len(self._ghosts) > 0 and self.character_instance is not None: - if len(ai.closeghostdist(pacman.walls, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 6)) > 5: - failure.set(True) - else: - success.set(True) - else: - failure.set(True) - =} - - reaction(pacman_sprite) {= - self.character_instance = pacman_sprite.value - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for sprite in ghost_sprites: - if sprite.is_present: - self._ghosts.append(sprite.value) - =} + input pacman_sprite + input[4] ghost_sprites + + #output sprite + + state character_instance + state _ghosts({=[]=}) + + reaction(start) -> success, failure {= + if len(self._ghosts) > 0 and len(self._ghosts) > 0 and self.character_instance is not None: + if len(ai.closeghostdist(pacman.walls, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 6)) > 5: + failure.set(True) + else: + success.set(True) + else: + failure.set(True) + =} + + reaction(pacman_sprite) {= + self.character_instance = pacman_sprite.value + =} + + reaction(ghost_sprites) {= + self._ghosts = [] + for sprite in ghost_sprites: + if sprite.is_present: + self._ghosts.append(sprite.value) + =} } reactor GhostScared extends BehaviorNode { - input frenzy - state _frenzy(False) + input frenzy + state _frenzy(False) - reaction(start) -> success, failure {= - if self._frenzy: - success.set(True) - else: - failure.set(True) - =} + reaction(start) -> success, failure {= + if self._frenzy: + success.set(True) + else: + failure.set(True) + =} - reaction(frenzy) {= - self._frenzy = frenzy.value - =} + reaction(frenzy) {= + self._frenzy = frenzy.value + =} } #fallback reactor PacMan0(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) extends BehaviorNode { - left = new PacMan1() - right = new EatPills() - merge = new MergeOr() - - input[4] ghost_sprites - input pacman_sprite - input block_list - input frenzy - input icon - input init_sprite - - reaction(icon) {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - =} - - start -> left.start - (ghost_sprites)+ -> right.ghost_sprites, left.ghost_sprites - (pacman_sprite)+ -> right.pacman_sprite, left.pacman_sprite - (block_list)+ -> right.block_list - frenzy -> left.frenzy - init_sprite -> right.init_sprite - - left.success -> merge.left - left.failure -> right.start - right.success -> merge.right - right.failure -> failure - merge.merged -> success + left = new PacMan1() + right = new EatPills() + merge = new MergeOr() + + input[4] ghost_sprites + input pacman_sprite + input block_list + input frenzy + input icon + input init_sprite + + reaction(icon) {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + =} + + start -> left.start + (ghost_sprites)+ -> right.ghost_sprites, left.ghost_sprites + (pacman_sprite)+ -> right.pacman_sprite, left.pacman_sprite + (block_list)+ -> right.block_list + frenzy -> left.frenzy + init_sprite -> right.init_sprite + + left.success -> merge.left + left.failure -> right.start + right.success -> merge.right + right.failure -> failure + merge.merged -> success } #sequence reactor PacMan1 extends BehaviorNode { - left = new GhostClose() - right = new PacMan2() - merge = new MergeOr() - - input pacman_sprite - input frenzy - input[4] ghost_sprites - - start -> left.start - (ghost_sprites)+ -> left.ghost_sprites, right.ghost_sprites - (pacman_sprite)+ -> left.pacman_sprite, right.pacman_sprite - frenzy -> right.frenzy - left.success -> right.start - left.failure -> merge.left - right.success -> success - right.failure -> merge.right - merge.merged -> failure + left = new GhostClose() + right = new PacMan2() + merge = new MergeOr() + + input pacman_sprite + input frenzy + input[4] ghost_sprites + + start -> left.start + (ghost_sprites)+ -> left.ghost_sprites, right.ghost_sprites + (pacman_sprite)+ -> left.pacman_sprite, right.pacman_sprite + frenzy -> right.frenzy + left.success -> right.start + left.failure -> merge.left + right.success -> success + right.failure -> merge.right + merge.merged -> failure } #fallback reactor PacMan2 extends BehaviorNode { - left = new PacMan3() - right = new AvoidGhost() - merge = new MergeOr() - - input[4] ghost_sprites - input pacman_sprite - input frenzy - - start -> left.start - (ghost_sprites)+ -> left.ghost_sprites, right.ghost_sprites - (pacman_sprite)+ -> left.pacman_sprite, right.pacman_sprite - frenzy -> left.frenzy - - left.success -> merge.left - left.failure -> right.start - right.success -> merge.right - right.failure -> failure - merge.merged -> success + left = new PacMan3() + right = new AvoidGhost() + merge = new MergeOr() + + input[4] ghost_sprites + input pacman_sprite + input frenzy + + start -> left.start + (ghost_sprites)+ -> left.ghost_sprites, right.ghost_sprites + (pacman_sprite)+ -> left.pacman_sprite, right.pacman_sprite + frenzy -> left.frenzy + + left.success -> merge.left + left.failure -> right.start + right.success -> merge.right + right.failure -> failure + merge.merged -> success } #sequence reactor PacMan3 extends BehaviorNode { - left = new GhostScared() - right = new ChaseGhost() - merge = new MergeOr() - - input[4] ghost_sprites - input pacman_sprite - input frenzy - - start -> left.start - frenzy -> left.frenzy - ghost_sprites -> right.ghost_sprites - pacman_sprite -> right.pacman_sprite - - left.success -> right.start - left.failure -> merge.left - right.success -> success - right.failure -> merge.right - merge.merged -> failure + left = new GhostScared() + right = new ChaseGhost() + merge = new MergeOr() + + input[4] ghost_sprites + input pacman_sprite + input frenzy + + start -> left.start + frenzy -> left.frenzy + ghost_sprites -> right.ghost_sprites + pacman_sprite -> right.pacman_sprite + + left.success -> right.start + left.failure -> merge.left + right.success -> success + right.failure -> merge.right + merge.merged -> failure } diff --git a/experimental/Python/src/Pac-Man/PacMan.lf b/experimental/Python/src/Pac-Man/PacMan.lf index 82c5f0d3..bd30a189 100644 --- a/experimental/Python/src/Pac-Man/PacMan.lf +++ b/experimental/Python/src/Pac-Man/PacMan.lf @@ -13,427 +13,427 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images"] + files: ["include/hbpacman.py", "include/images"] }; preamble {= - import os - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman + import os + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + + output tick + output[5] icon + + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) - - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(game_over) {= - #Grey background - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - # text2=font.render("To play again, press ENTER.", True, white) - # screen.blit(text2, [135, 303]) - # text3=font.render("To quit, press ESCAPE.", True, white) - # screen.blit(text3, [165, 333]) - - pacman.pygame.display.flip() - =} + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) + + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(game_over) {= + #Grey background + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + # text2=font.render("To play again, press ENTER.", True, white) + # screen.blit(text2, [135, 303]) + # text3=font.render("To quit, press ESCAPE.", True, white) + # screen.blit(text3, [165, 333]) + + pacman.pygame.display.flip() + =} } #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list + state character_instance + state _wall_list + state _gate_list - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ## Player # Should be replacable with an AI reactor Player extends BaseCharacter { - timer pygame_event(0, 100 msec) - reaction(pygame_event) -> sprite {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_LEFT: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_RIGHT: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_UP: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_DOWN: - self.character_instance.changespeed(0, 30) - - if event.type == pacman.pygame.KEYUP: - if event.key == pacman.pygame.K_LEFT: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_RIGHT: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_UP: - self.character_instance.changespeed(0, 30) - if event.key == pacman.pygame.K_DOWN: - self.character_instance.changespeed(0, -30) - - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - =} + timer pygame_event(0, 100 msec) + reaction(pygame_event) -> sprite {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_LEFT: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_RIGHT: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_UP: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_DOWN: + self.character_instance.changespeed(0, 30) + + if event.type == pacman.pygame.KEYUP: + if event.key == pacman.pygame.K_LEFT: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_RIGHT: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_UP: + self.character_instance.changespeed(0, 30) + if event.key == pacman.pygame.K_DOWN: + self.character_instance.changespeed(0, -30) + + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=})) extends BaseCharacter { - input tick - state turn(0) - state steps(0) - - reaction(tick) -> sprite {= - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} + input tick + state turn(0) + state steps(0) + + reaction(tick) -> sprite {= + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") - self._score_to_win = len(self._block_list) - =} - - reaction(pacman_sprite) -> score, game_over {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - request_stop() - - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} + + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} + + reaction(startup) -> block_list {= + # Draw the grid + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set("Lost!") - request_stop() + block = pacman.Block(pacman.yellow, 4, 4) - =} + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + self._score_to_win = len(self._block_list) + =} + + reaction(pacman_sprite) -> score, game_over {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + request_stop() + + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) - reaction(shutdown) {= - pacman.pygame.quit() - =} + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list: + game_over.set("Lost!") + request_stop() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + + reaction(shutdown) {= + pacman.pygame.quit() + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - - # Ghosts - pinky = new Ghost( - width = {=pacman.w=}, - height = {=pacman.m_h=}, - image = "images/Pinky.png", - directions = {=pacman.Pinky_directions=}, - character_class = ({=pacman.Ghost=}) - ) - blinky = new Ghost( - width = {=pacman.w=}, - height = {=pacman.b_h=}, - image = "images/Blinky.png", - directions = {=pacman.Blinky_directions=}, - character_class = ({=pacman.Ghost=}) - ) - inky = new Ghost( - width = {=pacman.i_w=}, - height = {=pacman.m_h=}, - image = "images/Inky.png", - directions = {=pacman.Inky_directions=}, - character_class = ({=pacman.Ghost=}) - ) - clyde = new Ghost( - width = {=pacman.c_w=}, - height = {=pacman.m_h=}, - image = "images/Clyde.png", - directions = {=pacman.Clyde_directions=}, - character_class = ({=pacman.Ghost=}) - ) - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> - pinky.wall_list, - blinky.wall_list, - inky.wall_list, - clyde.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - pinky.sprite, - blinky.sprite, - inky.sprite, - clyde.sprite -> - display.moving_sprites - - controller.game_over -> display.game_over - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - pinky.tick, - blinky.tick, - inky.tick, - clyde.tick - - player.sprite -> controller.pacman_sprite - + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + + # Ghosts + pinky = new Ghost( + width = {=pacman.w=}, + height = {=pacman.m_h=}, + image = "images/Pinky.png", + directions = {=pacman.Pinky_directions=}, + character_class = ({=pacman.Ghost=}) + ) + blinky = new Ghost( + width = {=pacman.w=}, + height = {=pacman.b_h=}, + image = "images/Blinky.png", + directions = {=pacman.Blinky_directions=}, + character_class = ({=pacman.Ghost=}) + ) + inky = new Ghost( + width = {=pacman.i_w=}, + height = {=pacman.m_h=}, + image = "images/Inky.png", + directions = {=pacman.Inky_directions=}, + character_class = ({=pacman.Ghost=}) + ) + clyde = new Ghost( + width = {=pacman.c_w=}, + height = {=pacman.m_h=}, + image = "images/Clyde.png", + directions = {=pacman.Clyde_directions=}, + character_class = ({=pacman.Ghost=}) + ) + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> + pinky.wall_list, + blinky.wall_list, + inky.wall_list, + clyde.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, pinky.sprite, - blinky.sprite, - inky.sprite, - clyde.sprite-> controller.ghost_sprites - - controller.score -> display.score - - pinky.icon_name, - blinky.icon_name, - inky.icon_name, - clyde.icon_name, - player.icon_name -> display.icon_name - - display.icon -> - pinky.icon, - blinky.icon, - inky.icon, - clyde.icon, player.icon + blinky.sprite, + inky.sprite, + clyde.sprite -> + display.moving_sprites + + controller.game_over -> display.game_over + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + pinky.tick, + blinky.tick, + inky.tick, + clyde.tick + + player.sprite -> controller.pacman_sprite + + pinky.sprite, + blinky.sprite, + inky.sprite, + clyde.sprite-> controller.ghost_sprites + + controller.score -> display.score + + pinky.icon_name, + blinky.icon_name, + inky.icon_name, + clyde.icon_name, + player.icon_name -> display.icon_name + + display.icon -> + pinky.icon, + blinky.icon, + inky.icon, + clyde.icon, player.icon } diff --git a/experimental/Python/src/Pac-Man/PacManKielTree.lf b/experimental/Python/src/Pac-Man/PacManKielTree.lf index f7d6b9d9..dbf69d9e 100644 --- a/experimental/Python/src/Pac-Man/PacManKielTree.lf +++ b/experimental/Python/src/Pac-Man/PacManKielTree.lf @@ -13,69 +13,69 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py", "KielPacManTree.lf"] + files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py", "KielPacManTree.lf"] }; import PacMan0 from "KielPacManTree.lf" preamble {= - import os - import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - import AIPacSupport as ai - - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + import AIPacSupport as ai + + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} @@ -83,164 +83,164 @@ preamble {= #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon - - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.Ghost): + #print("ghost ghost") + sprite_list.add(sprite.value) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + #print("got pacman") + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.Ghost): - #print("ghost ghost") - sprite_list.add(sprite.value) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - #print("got pacman") - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} -// reaction(playerpause) {= -// if playerpause.is_present and self._game_over == False: -// w = pacman.pygame.Surface((400,200)) # the size of your rect -// w.set_alpha(10) # alpha level -// w.fill((128,128,128)) # this fills the entire surface -// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates -// -// text2=self._font.render("The game is paused.", True, pacman.white) -// self._screen.blit(text2, [135, 303]) -// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) -// self._screen.blit(text3, [165, 333]) -// pacman.pygame.display.flip() -// =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("Paused. Press SPACE to continue,", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("M for Manual, and R for Robot.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - pyautogui.keyDown(' ') - #print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} - reaction(restart) {= - #if restart.value: - # self._active = True - # pyautogui.keyUp(' ') - self._game_over = False - =} +// reaction(playerpause) {= +// if playerpause.is_present and self._game_over == False: +// w = pacman.pygame.Surface((400,200)) # the size of your rect +// w.set_alpha(10) # alpha level +// w.fill((128,128,128)) # this fills the entire surface +// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates +// +// text2=self._font.render("The game is paused.", True, pacman.white) +// self._screen.blit(text2, [135, 303]) +// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) +// self._screen.blit(text3, [165, 333]) +// pacman.pygame.display.flip() +// =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("Paused. Press SPACE to continue,", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("M for Manual, and R for Robot.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + pyautogui.keyDown(' ') + #print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} + reaction(restart) {= + #if restart.value: + # self._active = True + # pyautogui.keyUp(' ') + self._game_over = False + =} } @@ -248,500 +248,500 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon - input initial_sprite - input pacman_sprite - - state _frenzy(False) - state _ghosts({=[]=}) - state _layout({=pacman.walls=}) - state _block_list({=[]=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _resetting(False) - state _eat_moves(0) - state _avoid_moves(0) - - output sprite - output init_sprite - output icon_name - output playerpause - output restart - - bt = new PacMan0(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - frenzy -> bt.frenzy - ghost_sprites -> bt.ghost_sprites - initial_sprite -> bt.init_sprite - block_list -> bt.block_list - pacman_sprite -> bt.pacman_sprite - icon -> bt.icon - bt.success -> sprite - - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> init_sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - init_sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> bt.start, playerpause, restart {= - if not self._pause and self._active and self._ai_control: - #bt.pacman_sprite.set(self.character_instance) - bt.start.set(True) - #sprite.set(bt.success) - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - #print(self.character_instance.rect.left) - if event.key == pacman.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == pacman.pygame.K_m: - self._pause = False - self._ai_control = False - if event.key == pacman.pygame.K_r: - self._pause = False - self._ai_control = True - playerpause.set(self._pause) - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for ghost in ghost_sprites: - if ghost.is_present: - self._ghosts.append(ghost.value) - =} - - reaction(block_list) {= - self._block_list = block_list.value - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon + input initial_sprite + input pacman_sprite + + state _frenzy(False) + state _ghosts({=[]=}) + state _layout({=pacman.walls=}) + state _block_list({=[]=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _resetting(False) + state _eat_moves(0) + state _avoid_moves(0) + + output sprite + output init_sprite + output icon_name + output playerpause + output restart + + bt = new PacMan0(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + frenzy -> bt.frenzy + ghost_sprites -> bt.ghost_sprites + initial_sprite -> bt.init_sprite + block_list -> bt.block_list + pacman_sprite -> bt.pacman_sprite + icon -> bt.icon + bt.success -> sprite + + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> init_sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + init_sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> bt.start, playerpause, restart {= + if not self._pause and self._active and self._ai_control: + #bt.pacman_sprite.set(self.character_instance) + bt.start.set(True) + #sprite.set(bt.success) + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + #print(self.character_instance.rect.left) + if event.key == pacman.pygame.K_SPACE: + if self._pause is False: + self._pause = True + else: + self._pause = False + if event.key == pacman.pygame.K_m: + self._pause = False + self._ai_control = False + if event.key == pacman.pygame.K_r: + self._pause = False + self._ai_control = True + playerpause.set(self._pause) + =} + + reaction(ghost_sprites) {= + self._ghosts = [] + for ghost in ghost_sprites: + if ghost.is_present: + self._ghosts.append(ghost.value) + =} + + reaction(block_list) {= + self._block_list = block_list.value + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - input frenzy - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - state _frenzy(False) - - #reaction(startup) {= - # self.image = "images/pacman.png" - #=} - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - # if self._resetting: - # self.character_instance.resetpos(self.name) - # self._resetting = False - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - + input tick + input playerpause # pause from player + input game_over + input restart + input frenzy + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + state _frenzy(False) + + #reaction(startup) {= + # self.image = "images/pacman.png" + #=} + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + # if self._resetting: + # self.character_instance.resetpos(self.name) + # self._resetting = False + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - input init_sprite - - logical action end_frenzy - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - state _frenzy(False) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + input init_sprite + + logical action end_frenzy + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + state _frenzy(False) # state _controllerpause({=False=}) #not necessary # initial mode One { - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite, init_sprite) {= - print("got pacman sprite") - if pacman_sprite.is_present: - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - else: - self._pacman_collide.empty() - self._pacman_collide.add(init_sprite.value) - self._pacman_sprite = init_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid + reaction(pacman_sprite, init_sprite) {= + print("got pacman sprite") + if pacman_sprite.is_present: + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + else: + self._pacman_collide.empty() + self._pacman_collide.add(init_sprite.value) + self._pacman_sprite = init_sprite.value + =} + + reaction(startup) -> block_list {= + # Draw the grid + + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} + + reaction(pacman_sprite, init_sprite) -> score, game_over, frenzy, end_frenzy {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + energizer_hit_list = pacman.pygame.sprite.RenderPlain() + #FIXME: can also make this more efficient + for block in blocks_hit_list: + if block.rect.width == 10: + energizer_hit_list.add(block) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + elif len(energizer_hit_list) > 0: + self._frenzy = True + print(self._frenzy) + frenzy.set(self._frenzy) + delay = randint(3, 7) + end_frenzy.schedule(SEC(delay)) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list and not self._frenzy: + game_over.set("Lost!") + elif monsta_hit_list and self._frenzy: + for monsta in monsta_hit_list: + monsta.moveoffgrid() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite, init_sprite) -> score, game_over, frenzy, end_frenzy {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - energizer_hit_list = pacman.pygame.sprite.RenderPlain() - #FIXME: can also make this more efficient - for block in blocks_hit_list: - if block.rect.width == 10: - energizer_hit_list.add(block) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - elif len(energizer_hit_list) > 0: - self._frenzy = True - print(self._frenzy) - frenzy.set(self._frenzy) - delay = randint(3, 7) - end_frenzy.schedule(SEC(delay)) - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list and not self._frenzy: - game_over.set("Lost!") - elif monsta_hit_list and self._frenzy: - for monsta in monsta_hit_list: - monsta.moveoffgrid() + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._frenzy = False + self._score = 0 + =} - =} + reaction(end_frenzy) -> frenzy {= + self._frenzy = False + print(self._frenzy, " and times up") + frenzy.set(self._frenzy) + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._frenzy = False - self._score = 0 - =} - - reaction(end_frenzy) -> frenzy {= - self._frenzy = False - print(self._frenzy, " and times up") - frenzy.set(self._frenzy) - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(shutdown) {= + pacman.pygame.quit() + =} # } } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - (player.sprite)+ -> controller.pacman_sprite, player.pacman_sprite - (player.init_sprite)+ -> controller.init_sprite, player.initial_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + (player.sprite)+ -> controller.pacman_sprite, player.pacman_sprite + (player.init_sprite)+ -> controller.init_sprite, player.initial_sprite - (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - - (controller.frenzy)+ -> player.frenzy, ghosts.frenzy - - controller.block_list -> player.block_list - + (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + + (controller.frenzy)+ -> player.frenzy, ghosts.frenzy + + controller.block_list -> player.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/PacManNMBT.lf b/experimental/Python/src/Pac-Man/PacManNMBT.lf index 99c4d7dd..e5a22bd9 100644 --- a/experimental/Python/src/Pac-Man/PacManNMBT.lf +++ b/experimental/Python/src/Pac-Man/PacManNMBT.lf @@ -13,23 +13,23 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] + files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] }; import Close from "ContainedPlayer.lf" import Eat from "ContainedPlayer.lf" @@ -38,205 +38,205 @@ import Avoid from "ContainedPlayer.lf" import Chase from "ContainedPlayer.lf" import Ticker from "ContainedPlayer.lf" preamble {= - import os - #import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - - #may need to import additional python files to support containedplayer - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + #import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + + #may need to import additional python files to support containedplayer + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon - - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} -// reaction(playerpause) {= -// if playerpause.is_present and self._game_over == False: -// w = pacman.pygame.Surface((400,200)) # the size of your rect -// w.set_alpha(10) # alpha level -// w.fill((128,128,128)) # this fills the entire surface -// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates -// -// text2=self._font.render("The game is paused.", True, pacman.white) -// self._screen.blit(text2, [135, 303]) -// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) -// self._screen.blit(text3, [165, 333]) -// pacman.pygame.display.flip() -// =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("The game is paused.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To continue, hit SPACE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - #pyautogui.keyDown(' ') - #print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - #pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} - reaction(restart) {= - #if restart.value: - # self._active = True - # pyautogui.keyUp(' ') - self._game_over = False - =} +// reaction(playerpause) {= +// if playerpause.is_present and self._game_over == False: +// w = pacman.pygame.Surface((400,200)) # the size of your rect +// w.set_alpha(10) # alpha level +// w.fill((128,128,128)) # this fills the entire surface +// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates +// +// text2=self._font.render("The game is paused.", True, pacman.white) +// self._screen.blit(text2, [135, 303]) +// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) +// self._screen.blit(text3, [165, 333]) +// pacman.pygame.display.flip() +// =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("The game is paused.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To continue, hit SPACE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + #pyautogui.keyDown(' ') + #print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + #pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} + reaction(restart) {= + #if restart.value: + # self._active = True + # pyautogui.keyUp(' ') + self._game_over = False + =} } @@ -244,56 +244,56 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } @@ -301,366 +301,366 @@ reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_ # Should be replacable with an AI #) reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon - - state character_instance - - output sprite - output icon_name - output playerpause - output restart - - ticker = new Ticker(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - c1 = new Close() - a1 = new Eat() - c2 = new Scared() - a2 = new Avoid() - a3 = new Chase() + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon + + state character_instance + + output sprite + output icon_name + output playerpause + output restart + + ticker = new Ticker(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + c1 = new Close() + a1 = new Eat() + c2 = new Scared() + a2 = new Avoid() + a3 = new Chase() + + (wall_list)+ -> ticker.wall_list, a1.wall_list + icon -> ticker.icon + gate_list -> ticker.gate_list + frenzy -> c2.frenzy + game_over -> ticker.game_over + block_list -> a1.block_list + (ghost_sprites)+ -> ticker.ghost_sprites + (ticker.sprite)+ -> sprite, a1.sprite + ticker.icon_name -> icon_name + ticker.playerpause -> playerpause + ticker.restart -> restart + ticker.result -> c1.tick + (c1.result)+ -> a1.tick, c2.tick + (c2.result)+ -> a3.tick, a2.tick + a1.result -> ticker.eat_result + a2.result -> ticker.chase_result + a3.result -> ticker.avoid_result - (wall_list)+ -> ticker.wall_list, a1.wall_list - icon -> ticker.icon - gate_list -> ticker.gate_list - frenzy -> c2.frenzy - game_over -> ticker.game_over - block_list -> a1.block_list - (ghost_sprites)+ -> ticker.ghost_sprites - (ticker.sprite)+ -> sprite, a1.sprite - ticker.icon_name -> icon_name - ticker.playerpause -> playerpause - ticker.restart -> restart - ticker.result -> c1.tick - (c1.result)+ -> a1.tick, c2.tick - (c2.result)+ -> a3.tick, a2.tick - a1.result -> ticker.eat_result - a2.result -> ticker.chase_result - a3.result -> ticker.avoid_result - } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - input frenzy - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - state _frenzy(False) - - #reaction(startup) {= - # self.image = "images/pacman.png" - #=} - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - # if self._resetting: - # self.character_instance.resetpos(self.name) - # self._resetting = False - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - + input tick + input playerpause # pause from player + input game_over + input restart + input frenzy + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + state _frenzy(False) + + #reaction(startup) {= + # self.image = "images/pacman.png" + #=} + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + # if self._resetting: + # self.character_instance.resetpos(self.name) + # self._resetting = False + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - logical action end_frenzy - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - state _frenzy(False) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + logical action end_frenzy + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + state _frenzy(False) # state _controllerpause({=False=}) #not necessary # initial mode One { - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") + reaction(startup) -> block_list {= + # Draw the grid + + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} + + reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + energizer_hit_list = pacman.pygame.sprite.RenderPlain() + #FIXME: can also make this more efficient + for block in blocks_hit_list: + if block.rect.width == 10: + energizer_hit_list.add(block) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + elif len(energizer_hit_list) > 0: + self._frenzy = True + print(self._frenzy) + frenzy.set(self._frenzy) + delay = randint(3, 7) + end_frenzy.schedule(SEC(delay)) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list and not self._frenzy: + game_over.set("Lost!") + elif monsta_hit_list and self._frenzy: + for monsta in monsta_hit_list: + monsta.moveoffgrid() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - energizer_hit_list = pacman.pygame.sprite.RenderPlain() - #FIXME: can also make this more efficient - for block in blocks_hit_list: - if block.rect.width == 10: - energizer_hit_list.add(block) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - elif len(energizer_hit_list) > 0: - self._frenzy = True - print(self._frenzy) - frenzy.set(self._frenzy) - delay = randint(3, 7) - end_frenzy.schedule(SEC(delay)) - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list and not self._frenzy: - game_over.set("Lost!") - elif monsta_hit_list and self._frenzy: - for monsta in monsta_hit_list: - monsta.moveoffgrid() + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._frenzy = False + self._score = 0 + =} - =} + reaction(end_frenzy) -> frenzy {= + self._frenzy = False + print(self._frenzy, " and times up") + frenzy.set(self._frenzy) + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._frenzy = False - self._score = 0 - =} - - reaction(end_frenzy) -> frenzy {= - self._frenzy = False - print(self._frenzy, " and times up") - frenzy.set(self._frenzy) - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(shutdown) {= + pacman.pygame.quit() + =} # } } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - - (controller.frenzy)+ -> player.frenzy, ghosts.frenzy - - controller.block_list -> player.block_list - - #ghosts.sprite -> player.ghost_sprites - + (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + + (controller.frenzy)+ -> player.frenzy, ghosts.frenzy + + controller.block_list -> player.block_list + + #ghosts.sprite -> player.ghost_sprites + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/PacManTesting.lf b/experimental/Python/src/Pac-Man/PacManTesting.lf index 2dc3f5de..9d8a8f02 100644 --- a/experimental/Python/src/Pac-Man/PacManTesting.lf +++ b/experimental/Python/src/Pac-Man/PacManTesting.lf @@ -13,223 +13,223 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] + files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] }; preamble {= - import os - import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon - - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} -// reaction(playerpause) {= -// if playerpause.is_present and self._game_over == False: -// w = pacman.pygame.Surface((400,200)) # the size of your rect -// w.set_alpha(10) # alpha level -// w.fill((128,128,128)) # this fills the entire surface -// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates -// -// text2=self._font.render("The game is paused.", True, pacman.white) -// self._screen.blit(text2, [135, 303]) -// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) -// self._screen.blit(text3, [165, 333]) -// pacman.pygame.display.flip() -// =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("The game is paused.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To continue, hit SPACE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - pyautogui.keyDown(' ') - #print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} - reaction(restart) {= - #if restart.value: - # self._active = True - # pyautogui.keyUp(' ') - self._game_over = False - =} +// reaction(playerpause) {= +// if playerpause.is_present and self._game_over == False: +// w = pacman.pygame.Surface((400,200)) # the size of your rect +// w.set_alpha(10) # alpha level +// w.fill((128,128,128)) # this fills the entire surface +// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates +// +// text2=self._font.render("The game is paused.", True, pacman.white) +// self._screen.blit(text2, [135, 303]) +// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) +// self._screen.blit(text3, [165, 333]) +// pacman.pygame.display.flip() +// =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("The game is paused.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To continue, hit SPACE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + pyautogui.keyDown(' ') + #print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} + reaction(restart) {= + #if restart.value: + # self._active = True + # pyautogui.keyUp(' ') + self._game_over = False + =} } @@ -237,465 +237,465 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } ## Player # Should be replacable with an AI reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input frenzy - output playerpause - output restart - - state _speed(30) - state _frenzy(False) - - - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _resetting(False) - - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - print(self.character_instance.rect.left) - - if event.key == pacman.pygame.K_SPACE and self._active: - if self._pause is False: - self._pause = True - else: - self._pause = False - print(self._pause) - elif self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed) - - if event.type == pacman.pygame.KEYUP and self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_b: - self._speed -= 10 - - if self._pause is False: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - playerpause.set(self._pause) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() -# ending.schedule(MSEC(500)) - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input frenzy + output playerpause + output restart + + state _speed(30) + state _frenzy(False) + + + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _resetting(False) + + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> sprite, playerpause, restart {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + print(self.character_instance.rect.left) + + if event.key == pacman.pygame.K_SPACE and self._active: + if self._pause is False: + self._pause = True + else: + self._pause = False + print(self._pause) + elif self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed) + + if event.type == pacman.pygame.KEYUP and self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_b: + self._speed -= 10 + + if self._pause is False: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + playerpause.set(self._pause) + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() +# ending.schedule(MSEC(500)) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - input frenzy - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - state _frenzy(False) - - #reaction(startup) {= - # self.image = "images/pacman.png" - #=} - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - # if self._resetting: - # self.character_instance.resetpos(self.name) - # self._resetting = False - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - + input tick + input playerpause # pause from player + input game_over + input restart + input frenzy + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + state _frenzy(False) + + #reaction(startup) {= + # self.image = "images/pacman.png" + #=} + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + # if self._resetting: + # self.character_instance.resetpos(self.name) + # self._resetting = False + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - logical action end_frenzy - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - state _frenzy(False) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + logical action end_frenzy + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + state _frenzy(False) # state _controllerpause({=False=}) #not necessary # initial mode One { - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - - # print("Finished drawing blocks") + reaction(startup) -> block_list {= + # Draw the grid + + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} + + reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + energizer_hit_list = pacman.pygame.sprite.RenderPlain() + #FIXME: can also make this more efficient + for block in blocks_hit_list: + if block.rect.width == 10: + energizer_hit_list.add(block) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + elif len(energizer_hit_list) > 0: + self._frenzy = True + print(self._frenzy) + frenzy.set(self._frenzy) + delay = randint(3, 7) + end_frenzy.schedule(SEC(delay)) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list and not self._frenzy: + game_over.set("Lost!") + elif monsta_hit_list and self._frenzy: + for monsta in monsta_hit_list: + monsta.moveoffgrid() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - energizer_hit_list = pacman.pygame.sprite.RenderPlain() - #FIXME: can also make this more efficient - for block in blocks_hit_list: - if block.rect.width == 10: - energizer_hit_list.add(block) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - elif len(energizer_hit_list) > 0: - self._frenzy = True - print(self._frenzy) - frenzy.set(self._frenzy) - delay = randint(3, 7) - end_frenzy.schedule(SEC(delay)) - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list and not self._frenzy: - game_over.set("Lost!") - elif monsta_hit_list and self._frenzy: - for monsta in monsta_hit_list: - monsta.moveoffgrid() + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._frenzy = False + self._score = 0 + =} - =} + reaction(end_frenzy) -> frenzy {= + self._frenzy = False + print(self._frenzy, " and times up") + frenzy.set(self._frenzy) + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._frenzy = False - self._score = 0 - =} - - reaction(end_frenzy) -> frenzy {= - self._frenzy = False - print(self._frenzy, " and times up") - frenzy.set(self._frenzy) - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(shutdown) {= + pacman.pygame.quit() + =} # } } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - ghosts.sprite -> controller.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - - (controller.frenzy)+ -> player.frenzy, ghosts.frenzy - - + ghosts.sprite -> controller.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + + (controller.frenzy)+ -> player.frenzy, ghosts.frenzy + + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/PacManWFrenzy.lf b/experimental/Python/src/Pac-Man/PacManWFrenzy.lf index 78e48f3d..750ec402 100644 --- a/experimental/Python/src/Pac-Man/PacManWFrenzy.lf +++ b/experimental/Python/src/Pac-Man/PacManWFrenzy.lf @@ -13,223 +13,223 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] + files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] }; preamble {= - import os - import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon - - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} -// reaction(playerpause) {= -// if playerpause.is_present and self._game_over == False: -// w = pacman.pygame.Surface((400,200)) # the size of your rect -// w.set_alpha(10) # alpha level -// w.fill((128,128,128)) # this fills the entire surface -// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates -// -// text2=self._font.render("The game is paused.", True, pacman.white) -// self._screen.blit(text2, [135, 303]) -// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) -// self._screen.blit(text3, [165, 333]) -// pacman.pygame.display.flip() -// =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("The game is paused.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To continue, hit SPACE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - pyautogui.keyDown(' ') - #print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} - reaction(restart) {= - #if restart.value: - # self._active = True - # pyautogui.keyUp(' ') - self._game_over = False - =} +// reaction(playerpause) {= +// if playerpause.is_present and self._game_over == False: +// w = pacman.pygame.Surface((400,200)) # the size of your rect +// w.set_alpha(10) # alpha level +// w.fill((128,128,128)) # this fills the entire surface +// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates +// +// text2=self._font.render("The game is paused.", True, pacman.white) +// self._screen.blit(text2, [135, 303]) +// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) +// self._screen.blit(text3, [165, 333]) +// pacman.pygame.display.flip() +// =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("The game is paused.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To continue, hit SPACE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + pyautogui.keyDown(' ') + #print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} + reaction(restart) {= + #if restart.value: + # self._active = True + # pyautogui.keyUp(' ') + self._game_over = False + =} } @@ -237,484 +237,484 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input frenzy - output playerpause - output restart - - state _speed(30) - state _frenzy(False) - - - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _resetting(False) - - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - print(self.character_instance.rect.left) - - if event.key == pacman.pygame.K_SPACE and self._active: - if self._pause is False: - self._pause = True - else: - self._pause = False - print(self._pause) - elif self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed) - - if event.type == pacman.pygame.KEYUP and self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_b: - self._speed -= 10 - - if self._pause is False: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - playerpause.set(self._pause) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() -# ending.schedule(MSEC(500)) - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input frenzy + output playerpause + output restart + + state _speed(30) + state _frenzy(False) + + + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _resetting(False) + + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> sprite, playerpause, restart {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + print(self.character_instance.rect.left) + + if event.key == pacman.pygame.K_SPACE and self._active: + if self._pause is False: + self._pause = True + else: + self._pause = False + print(self._pause) + elif self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed) + + if event.type == pacman.pygame.KEYUP and self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_b: + self._speed -= 10 + + if self._pause is False: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + playerpause.set(self._pause) + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() +# ending.schedule(MSEC(500)) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - input frenzy - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - state _frenzy(False) - - #reaction(startup) {= - # self.image = "images/pacman.png" - #=} - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - # if self._resetting: - # self.character_instance.resetpos(self.name) - # self._resetting = False - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - + input tick + input playerpause # pause from player + input game_over + input restart + input frenzy + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + state _frenzy(False) + + #reaction(startup) {= + # self.image = "images/pacman.png" + #=} + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + # if self._resetting: + # self.character_instance.resetpos(self.name) + # self._resetting = False + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - logical action end_frenzy - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - state _frenzy(False) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + logical action end_frenzy + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + state _frenzy(False) # state _controllerpause({=False=}) #not necessary # initial mode One { - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - - # print("Finished drawing blocks") + reaction(startup) -> block_list {= + # Draw the grid + + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} + + reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + energizer_hit_list = pacman.pygame.sprite.RenderPlain() + #FIXME: can also make this more efficient + for block in blocks_hit_list: + if block.rect.width == 10: + energizer_hit_list.add(block) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + elif len(energizer_hit_list) > 0: + self._frenzy = True + print(self._frenzy) + frenzy.set(self._frenzy) + delay = randint(3, 7) + end_frenzy.schedule(SEC(delay)) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list and not self._frenzy: + game_over.set("Lost!") + elif monsta_hit_list and self._frenzy: + for monsta in monsta_hit_list: + monsta.moveoffgrid() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - energizer_hit_list = pacman.pygame.sprite.RenderPlain() - #FIXME: can also make this more efficient - for block in blocks_hit_list: - if block.rect.width == 10: - energizer_hit_list.add(block) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - elif len(energizer_hit_list) > 0: - self._frenzy = True - print(self._frenzy) - frenzy.set(self._frenzy) - delay = randint(3, 7) - end_frenzy.schedule(SEC(delay)) - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list and not self._frenzy: - game_over.set("Lost!") - elif monsta_hit_list and self._frenzy: - for monsta in monsta_hit_list: - monsta.moveoffgrid() + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._frenzy = False + self._score = 0 + =} - =} + reaction(end_frenzy) -> frenzy {= + self._frenzy = False + print(self._frenzy, " and times up") + frenzy.set(self._frenzy) + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._frenzy = False - self._score = 0 - =} - - reaction(end_frenzy) -> frenzy {= - self._frenzy = False - print(self._frenzy, " and times up") - frenzy.set(self._frenzy) - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(shutdown) {= + pacman.pygame.quit() + =} # } } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - (ghosts.sprite)+ -> controller.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - - (controller.frenzy)+ -> player.frenzy, ghosts.frenzy - - #controller.block_list -> player.block_list + (ghosts.sprite)+ -> controller.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + + (controller.frenzy)+ -> player.frenzy, ghosts.frenzy + + #controller.block_list -> player.block_list } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/PacManWModalBT.lf b/experimental/Python/src/Pac-Man/PacManWModalBT.lf index 5ac54fca..8fa5ed1c 100644 --- a/experimental/Python/src/Pac-Man/PacManWModalBT.lf +++ b/experimental/Python/src/Pac-Man/PacManWModalBT.lf @@ -13,224 +13,224 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] + files: ["include/hbpacman.py", "include/images", "include/AIPacSupport.py"] }; preamble {= - import os - import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - import AIPacSupport as ai - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + import AIPacSupport as ai + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon - - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} -// reaction(playerpause) {= -// if playerpause.is_present and self._game_over == False: -// w = pacman.pygame.Surface((400,200)) # the size of your rect -// w.set_alpha(10) # alpha level -// w.fill((128,128,128)) # this fills the entire surface -// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates -// -// text2=self._font.render("The game is paused.", True, pacman.white) -// self._screen.blit(text2, [135, 303]) -// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) -// self._screen.blit(text3, [165, 333]) -// pacman.pygame.display.flip() -// =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("Paused. Press SPACE to continue,", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("M for Manual, and R for Robot.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - pyautogui.keyDown(' ') - #print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} - reaction(restart) {= - #if restart.value: - # self._active = True - # pyautogui.keyUp(' ') - self._game_over = False - =} +// reaction(playerpause) {= +// if playerpause.is_present and self._game_over == False: +// w = pacman.pygame.Surface((400,200)) # the size of your rect +// w.set_alpha(10) # alpha level +// w.fill((128,128,128)) # this fills the entire surface +// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates +// +// text2=self._font.render("The game is paused.", True, pacman.white) +// self._screen.blit(text2, [135, 303]) +// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) +// self._screen.blit(text3, [165, 333]) +// pacman.pygame.display.flip() +// =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("Paused. Press SPACE to continue,", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("M for Manual, and R for Robot.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + pyautogui.keyDown(' ') + #print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} + reaction(restart) {= + #if restart.value: + # self._active = True + # pyautogui.keyUp(' ') + self._game_over = False + =} } @@ -238,560 +238,560 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, "images/" + self.name + ".png")) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } ###Base Player reactor BasePlayer(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon - state character_instance + state character_instance - output sprite - output icon_name - output playerpause - output restart + output sprite + output icon_name + output playerpause + output restart } ## Player # Should be replacable with an AI reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over - input frenzy - input[4] ghost_sprites - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input block_list - input icon - - state _frenzy(False) - state _ghosts({=[]=}) - state _layout({=pacman.walls=}) - state _block_list({=[]=}) - state _ai_control(True) - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _resetting(False) - state _eat_moves(0) - state _avoid_moves(0) - - output sprite - output icon_name - output playerpause - output restart - - initial mode export { - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - #print(self.character_instance.rect.left) - if event.key == pacman.pygame.K_SPACE: - if self._pause is False: - self._pause = True - else: - self._pause = False - if event.key == pacman.pygame.K_m: - self._pause = False - self._ai_control = False - if event.key == pacman.pygame.K_r: - self._pause = False - self._ai_control = True - if not self._ai_control and not self._pause and self._active: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, 30) - - if event.type == pacman.pygame.KEYUP and not self._ai_control and not self._pause and self._active: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, 30) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_b: - self._speed -= 10 - - if not self._ai_control and not self._pause and self._active: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - playerpause.set(self._pause) - if not self._pause and self._active: - if self._ai_control: - Close.set() - - =} - - reaction(ghost_sprites) {= - self._ghosts = [] - for ghost in ghost_sprites: - if ghost.is_present: - self._ghosts.append(ghost.value) - =} - - reaction(block_list) {= - self._block_list = block_list.value - =} - - reaction(frenzy) {= - self._frenzy = frenzy.value - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() - =} - - } mode Close { - - reaction(reset) -> reset(Eat), reset(Scared), history(export) {= - if len(self._block_list) > 0 and len(self._ghosts) > 0: - if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 6)) > 5: - #print("ghost not close") - Eat.set() - else: - print("ghost close") - Scared.set() - else: - export.set() - =} - - } mode Eat { - - reaction(reset) -> history(export) {= - #print("food time") - self.character_instance.ai_eat(pacman.walls, self._ghosts, self._block_list, self._eat_moves) - self._eat_moves = self.character_instance.get_num_moves() - export.set() - =} - - } mode Scared { + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over + input frenzy + input[4] ghost_sprites + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input block_list + input icon + + state _frenzy(False) + state _ghosts({=[]=}) + state _layout({=pacman.walls=}) + state _block_list({=[]=}) + state _ai_control(True) + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _resetting(False) + state _eat_moves(0) + state _avoid_moves(0) + + output sprite + output icon_name + output playerpause + output restart + + initial mode export { + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} - reaction(reset) -> reset(Avoid), reset(Chase) {= - if self._frenzy: - #print("pac not scared") - Chase.set() + reaction(pygame_event) -> sprite, playerpause, restart, reset(Close) {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + #print(self.character_instance.rect.left) + if event.key == pacman.pygame.K_SPACE: + if self._pause is False: + self._pause = True else: - #print("pac is scared") - Avoid.set() - =} - - } mode Avoid { + self._pause = False + if event.key == pacman.pygame.K_m: + self._pause = False + self._ai_control = False + if event.key == pacman.pygame.K_r: + self._pause = False + self._ai_control = True + if not self._ai_control and not self._pause and self._active: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, 30) + + if event.type == pacman.pygame.KEYUP and not self._ai_control and not self._pause and self._active: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, 30) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_b: + self._speed -= 10 + + if not self._ai_control and not self._pause and self._active: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + playerpause.set(self._pause) + if not self._pause and self._active: + if self._ai_control: + Close.set() + + =} - reaction(reset) -> history(export) {= - print("avoid time") - self.character_instance.ai_avoid(self._layout, self._ghosts, 7) - #self._avoid_moves = self.character_instance.get_num_moves() - export.set() - =} + reaction(ghost_sprites) {= + self._ghosts = [] + for ghost in ghost_sprites: + if ghost.is_present: + self._ghosts.append(ghost.value) + =} - } mode Chase { + reaction(block_list) {= + self._block_list = block_list.value + =} - reaction(reset) -> history(export) {= - print("chase time") - self.character_instance.ai_chase(self._layout, self._ghosts, 7) - export.set() - =} + reaction(frenzy) {= + self._frenzy = frenzy.value + =} - } -} + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() + =} -## Ghosts -# FIXME: Different Ghosts should have different personalities -reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - input frenzy - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - state _frenzy(False) - - #reaction(startup) {= - # self.image = "images/pacman.png" - #=} - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value + } mode Close { + + reaction(reset) -> reset(Eat), reset(Scared), history(export) {= + if len(self._block_list) > 0 and len(self._ghosts) > 0: + if len(ai.closeghostdist(self._layout, self._ghosts, self.character_instance.rect.left, self.character_instance.rect.top, 6)) > 5: + #print("ghost not close") + Eat.set() + else: + print("ghost close") + Scared.set() + else: + export.set() =} - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - # if self._resetting: - # self.character_instance.resetpos(self.name) - # self._resetting = False - sprite.set(self.character_instance) + } mode Eat { + + reaction(reset) -> history(export) {= + #print("food time") + self.character_instance.ai_eat(pacman.walls, self._ghosts, self._block_list, self._eat_moves) + self._eat_moves = self.character_instance.get_num_moves() + export.set() =} - reaction(game_over) {= - self._active = False + } mode Scared { + + reaction(reset) -> reset(Avoid), reset(Chase) {= + if self._frenzy: + #print("pac not scared") + Chase.set() + else: + #print("pac is scared") + Avoid.set() =} - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) + } mode Avoid { + + reaction(reset) -> history(export) {= + print("avoid time") + self.character_instance.ai_avoid(self._layout, self._ghosts, 7) + #self._avoid_moves = self.character_instance.get_num_moves() + export.set() =} - - reaction(frenzy) {= - self._frenzy = frenzy.value + + } mode Chase { + + reaction(reset) -> history(export) {= + print("chase time") + self.character_instance.ai_chase(self._layout, self._ghosts, 7) + export.set() =} - + + } +} + +## Ghosts +# FIXME: Different Ghosts should have different personalities +reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { + input tick + input playerpause # pause from player + input game_over + input restart + input frenzy + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + state _frenzy(False) + + #reaction(startup) {= + # self.image = "images/pacman.png" + #=} + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + # if self._resetting: + # self.character_instance.resetpos(self.name) + # self._resetting = False + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + + reaction(frenzy) {= + self._frenzy = frenzy.value + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - logical action end_frenzy - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) - state _frenzy(False) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + logical action end_frenzy + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) + state _frenzy(False) # state _controllerpause({=False=}) #not necessary # initial mode One { - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} + + reaction(startup) -> block_list {= + # Draw the grid + + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} + + reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + energizer_hit_list = pacman.pygame.sprite.RenderPlain() + #FIXME: can also make this more efficient + for block in blocks_hit_list: + if block.rect.width == 10: + energizer_hit_list.add(block) - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + elif len(energizer_hit_list) > 0: + self._frenzy = True + print(self._frenzy) + frenzy.set(self._frenzy) + delay = randint(3, 7) + end_frenzy.schedule(SEC(delay)) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list and not self._frenzy: + game_over.set("Lost!") + elif monsta_hit_list and self._frenzy: + for monsta in monsta_hit_list: + monsta.moveoffgrid() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite) -> score, game_over, frenzy, end_frenzy {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - energizer_hit_list = pacman.pygame.sprite.RenderPlain() - #FIXME: can also make this more efficient - for block in blocks_hit_list: - if block.rect.width == 10: - energizer_hit_list.add(block) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - elif len(energizer_hit_list) > 0: - self._frenzy = True - print(self._frenzy) - frenzy.set(self._frenzy) - delay = randint(3, 7) - end_frenzy.schedule(SEC(delay)) - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list and not self._frenzy: - game_over.set("Lost!") - elif monsta_hit_list and self._frenzy: - for monsta in monsta_hit_list: - monsta.moveoffgrid() + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._frenzy = False + self._score = 0 + =} - =} + reaction(end_frenzy) -> frenzy {= + self._frenzy = False + print(self._frenzy, " and times up") + frenzy.set(self._frenzy) + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._frenzy = False - self._score = 0 - =} - - reaction(end_frenzy) -> frenzy {= - self._frenzy = False - print(self._frenzy, " and times up") - frenzy.set(self._frenzy) - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(shutdown) {= + pacman.pygame.quit() + =} # } } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - # image = {= ghost_specs[bank_index]["image"] =} - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + #width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png" + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + # image = {= ghost_specs[bank_index]["image"] =} + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - - (controller.frenzy)+ -> player.frenzy, ghosts.frenzy - - controller.block_list -> player.block_list - + (ghosts.sprite)+ -> controller.ghost_sprites, player.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + + (controller.frenzy)+ -> player.frenzy, ghosts.frenzy + + controller.block_list -> player.block_list + } \ No newline at end of file diff --git a/experimental/Python/src/Pac-Man/PacManWRestart.lf b/experimental/Python/src/Pac-Man/PacManWRestart.lf index 0131a86c..51c66663 100644 --- a/experimental/Python/src/Pac-Man/PacManWRestart.lf +++ b/experimental/Python/src/Pac-Man/PacManWRestart.lf @@ -13,428 +13,428 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images"] + files: ["include/hbpacman.py", "include/images"] }; preamble {= - import os - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman + import os + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + + output tick + output[5] icon + + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) - - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(game_over) {= - #Grey background - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - # text2=font.render("To play again, press ENTER.", True, white) - # screen.blit(text2, [135, 303]) - # text3=font.render("To quit, press ESCAPE.", True, white) - # screen.blit(text3, [165, 333]) - - pacman.pygame.display.flip() - =} + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) + + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(game_over) {= + #Grey background + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + # text2=font.render("To play again, press ENTER.", True, white) + # screen.blit(text2, [135, 303]) + # text3=font.render("To quit, press ESCAPE.", True, white) + # screen.blit(text3, [165, 333]) + + pacman.pygame.display.flip() + =} } #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon - output sprite - output icon_name + output sprite + output icon_name - state character_instance - state _wall_list - state _gate_list + state character_instance + state _wall_list + state _gate_list - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} } ## Player # Should be replacable with an AI reactor Player extends BaseCharacter { - timer pygame_event(0, 100 msec) - reaction(pygame_event) -> sprite {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_LEFT: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_RIGHT: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_UP: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_DOWN: - self.character_instance.changespeed(0, 30) - - if event.type == pacman.pygame.KEYUP: - if event.key == pacman.pygame.K_LEFT: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_RIGHT: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_UP: - self.character_instance.changespeed(0, 30) - if event.key == pacman.pygame.K_DOWN: - self.character_instance.changespeed(0, -30) - - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - =} + timer pygame_event(0, 100 msec) + reaction(pygame_event) -> sprite {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_LEFT: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_RIGHT: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_UP: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_DOWN: + self.character_instance.changespeed(0, 30) + + if event.type == pacman.pygame.KEYUP: + if event.key == pacman.pygame.K_LEFT: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_RIGHT: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_UP: + self.character_instance.changespeed(0, 30) + if event.key == pacman.pygame.K_DOWN: + self.character_instance.changespeed(0, -30) + + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=})) extends BaseCharacter { - input tick - state turn(0) - state steps(0) - - reaction(tick) -> sprite {= - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} + input tick + state turn(0) + state steps(0) + + reaction(tick) -> sprite {= + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") - self._score_to_win = len(self._block_list) - =} - - reaction(pacman_sprite) -> score, game_over {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - request_stop() - - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} + + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} + + reaction(startup) -> block_list {= + # Draw the grid + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + block = pacman.Block(pacman.yellow, 4, 4) - if monsta_hit_list: - game_over.set("Lost!") - request_stop() + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 - =} - - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + self._score_to_win = len(self._block_list) + =} + + reaction(pacman_sprite) -> score, game_over {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + request_stop() + + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) - reaction(shutdown) {= - pacman.pygame.quit() - =} + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list: + game_over.set("Lost!") + request_stop() + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + + reaction(shutdown) {= + pacman.pygame.quit() + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - - # Ghosts - pinky = new Ghost( - width = {=pacman.w=}, - height = {=pacman.m_h=}, - image = "images/Pinky.png", - directions = {=pacman.Pinky_directions=}, - character_class = ({=pacman.Ghost=}) - ) - blinky = new Ghost( - width = {=pacman.w=}, - height = {=pacman.b_h=}, - image = "images/Blinky.png", - directions = {=pacman.Blinky_directions=}, - character_class = ({=pacman.Ghost=}) - ) - inky = new Ghost( - width = {=pacman.i_w=}, - height = {=pacman.m_h=}, - image = "images/Inky.png", - directions = {=pacman.Inky_directions=}, - character_class = ({=pacman.Ghost=}) - ) - clyde = new Ghost( - width = {=pacman.c_w=}, - height = {=pacman.m_h=}, - image = "images/Clyde.png", - directions = {=pacman.Clyde_directions=}, - character_class = ({=pacman.Ghost=}) - ) - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> - pinky.wall_list, - blinky.wall_list, - inky.wall_list, - clyde.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - pinky.sprite, - blinky.sprite, - inky.sprite, - clyde.sprite -> - display.moving_sprites - - controller.game_over -> display.game_over - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - pinky.tick, - blinky.tick, - inky.tick, - clyde.tick - - player.sprite -> controller.pacman_sprite - + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + + # Ghosts + pinky = new Ghost( + width = {=pacman.w=}, + height = {=pacman.m_h=}, + image = "images/Pinky.png", + directions = {=pacman.Pinky_directions=}, + character_class = ({=pacman.Ghost=}) + ) + blinky = new Ghost( + width = {=pacman.w=}, + height = {=pacman.b_h=}, + image = "images/Blinky.png", + directions = {=pacman.Blinky_directions=}, + character_class = ({=pacman.Ghost=}) + ) + inky = new Ghost( + width = {=pacman.i_w=}, + height = {=pacman.m_h=}, + image = "images/Inky.png", + directions = {=pacman.Inky_directions=}, + character_class = ({=pacman.Ghost=}) + ) + clyde = new Ghost( + width = {=pacman.c_w=}, + height = {=pacman.m_h=}, + image = "images/Clyde.png", + directions = {=pacman.Clyde_directions=}, + character_class = ({=pacman.Ghost=}) + ) + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> + pinky.wall_list, + blinky.wall_list, + inky.wall_list, + clyde.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, pinky.sprite, - blinky.sprite, - inky.sprite, - clyde.sprite-> controller.ghost_sprites - - controller.score -> display.score - - pinky.icon_name, - blinky.icon_name, - inky.icon_name, - clyde.icon_name, - player.icon_name -> display.icon_name - - display.icon -> - pinky.icon, - blinky.icon, - inky.icon, - clyde.icon, player.icon + blinky.sprite, + inky.sprite, + clyde.sprite -> + display.moving_sprites + + controller.game_over -> display.game_over + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + pinky.tick, + blinky.tick, + inky.tick, + clyde.tick + + player.sprite -> controller.pacman_sprite + + pinky.sprite, + blinky.sprite, + inky.sprite, + clyde.sprite-> controller.ghost_sprites + + controller.score -> display.score + + pinky.icon_name, + blinky.icon_name, + inky.icon_name, + clyde.icon_name, + player.icon_name -> display.icon_name + + display.icon -> + pinky.icon, + blinky.icon, + inky.icon, + clyde.icon, player.icon } diff --git a/experimental/Python/src/Pac-Man/PacManWithBank.lf b/experimental/Python/src/Pac-Man/PacManWithBank.lf index 642cadf3..b5da31c9 100644 --- a/experimental/Python/src/Pac-Man/PacManWithBank.lf +++ b/experimental/Python/src/Pac-Man/PacManWithBank.lf @@ -13,488 +13,488 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images"] + files: ["include/hbpacman.py", "include/images"] }; preamble {= - import os - import pyautogui - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - #input controllerpause - - output tick - output[5] icon + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + #input controllerpause + + output tick + output[5] icon + + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) - - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - #if self._active: - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - #if self._active: - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(game_over) {= - #self._active = False - =} - - reaction(game_over) {= - #Grey background - print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=font.render("To play again, press ENTER.", True, white) - screen.blit(text2, [135, 303]) - text3=font.render("To quit, press ESCAPE.", True, white) - screen.blit(text3, [165, 333]) - - pacman.pygame.display.flip() - =} + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + #if self._active: + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + #if self._active: + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) + + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(game_over) {= + #self._active = False + =} + + reaction(game_over) {= + #Grey background + print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=font.render("To play again, press ENTER.", True, white) + screen.blit(text2, [135, 303]) + text3=font.render("To quit, press ESCAPE.", True, white) + screen.blit(text3, [165, 333]) + + pacman.pygame.display.flip() + =} } #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } ## Player # Should be replacable with an AI reactor Player extends BaseCharacter { - timer pygame_event(0, 100 msec) - state _active(True) - - #input game_over - - output playerpause - - /*reaction(game_over) -> playerpause {= - self._active(False) - self._pause = True - playerpause.set(self._pause) - =}*/ - - reaction(pygame_event) -> sprite, playerpause {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_SPACE and self._active: - if self._pause is False: - self._pause = True - else: - self._pause = False - print(self._pause) - elif self._pause is False and self._active: - if event.key == pacman.pygame.K_LEFT: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_RIGHT: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_UP: - self.character_instance.changespeed(0, -30) - if event.key == pacman.pygame.K_DOWN: - self.character_instance.changespeed(0, 30) - - - if event.type == pacman.pygame.KEYUP and self._pause is False and self._active: - if event.key == pacman.pygame.K_LEFT: - self.character_instance.changespeed(30, 0) - if event.key == pacman.pygame.K_RIGHT: - self.character_instance.changespeed(-30, 0) - if event.key == pacman.pygame.K_UP: - self.character_instance.changespeed(0, 30) - if event.key == pacman.pygame.K_DOWN: - self.character_instance.changespeed(0, -30) - if self._pause is False and self._active: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - playerpause.set(self._pause) - =} + timer pygame_event(0, 100 msec) + state _active(True) + + #input game_over + + output playerpause + + /*reaction(game_over) -> playerpause {= + self._active(False) + self._pause = True + playerpause.set(self._pause) + =}*/ + + reaction(pygame_event) -> sprite, playerpause {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_SPACE and self._active: + if self._pause is False: + self._pause = True + else: + self._pause = False + print(self._pause) + elif self._pause is False and self._active: + if event.key == pacman.pygame.K_LEFT: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_RIGHT: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_UP: + self.character_instance.changespeed(0, -30) + if event.key == pacman.pygame.K_DOWN: + self.character_instance.changespeed(0, 30) + + + if event.type == pacman.pygame.KEYUP and self._pause is False and self._active: + if event.key == pacman.pygame.K_LEFT: + self.character_instance.changespeed(30, 0) + if event.key == pacman.pygame.K_RIGHT: + self.character_instance.changespeed(-30, 0) + if event.key == pacman.pygame.K_UP: + self.character_instance.changespeed(0, 30) + if event.key == pacman.pygame.K_DOWN: + self.character_instance.changespeed(0, -30) + if self._pause is False and self._active: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + playerpause.set(self._pause) + =} } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=})) extends BaseCharacter { - input tick - input playerpause # pause from player - - state turn(0) - state steps(0) - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - print(self.turn, self.steps, self.image) - if self._pause is False: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - sprite.set(self.character_instance) - =} - + input tick + input playerpause # pause from player + + state turn(0) + state steps(0) + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + print(self.turn, self.steps, self.image) + if self._pause is False: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + sprite.set(self.character_instance) + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) # state _controllerpause({=False=}) #not necessary + + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) + =} + + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} + + reaction(startup) -> block_list {= + # Draw the grid + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 - wall_list.set(self._wall_list) - gate.set(self._gate) + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") - self._score_to_win = len(self._block_list) - =} - - reaction(pacman_sprite) -> score, game_over {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set("Lost!") - print("Lost") - pyautogui.keyDown(' ') - #print("Lost") - #self._controllerpause = True - #Pause the game instead of stopping it - #controllerpause.set(True) - #request_stop() #remove once above finished - - =} - - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + self._score_to_win = len(self._block_list) + =} + + reaction(pacman_sprite) -> score, game_over {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) - reaction(shutdown) {= - pacman.pygame.quit() - =} + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list: + game_over.set("Lost!") + print("Lost") + pyautogui.keyDown(' ') + #print("Lost") + #self._controllerpause = True + #Pause the game instead of stopping it + #controllerpause.set(True) + #request_stop() #remove once above finished + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + + reaction(shutdown) {= + pacman.pygame.quit() + =} } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - image = {= ghost_specs[bank_index]["image"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - character_class = ({=pacman.Ghost=}) - ) - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + image = {= ghost_specs[bank_index]["image"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + character_class = ({=pacman.Ghost=}) + ) + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - ghosts.sprite -> controller.ghost_sprites - - controller.score -> display.score + ghosts.sprite -> controller.ghost_sprites + + controller.score -> display.score - ghosts.icon_name, player.icon_name -> display.icon_name + ghosts.icon_name, player.icon_name -> display.icon_name - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - controller.game_over -> display.game_over - + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + controller.game_over -> display.game_over + } diff --git a/experimental/Python/src/Pac-Man/oldWBank.lf b/experimental/Python/src/Pac-Man/oldWBank.lf index d31e8fe9..13facf8e 100644 --- a/experimental/Python/src/Pac-Man/oldWBank.lf +++ b/experimental/Python/src/Pac-Man/oldWBank.lf @@ -13,223 +13,223 @@ * 3- Add the ability to restart the game after win/lose. * 4- Make the game logic more efficient if possible. * 5- Add personalities for each ghost instead of following pre-determined - * directions. + * directions. * 6- Add modes for ghosts (exploring, chasing, running away). * 7- Replace the player with an AI. * 8- Enable federated execution if possible. * 9- Explore: - * - What to do in the case of communication failure? - * - What are other possible fault scenarios? - * - What should the AI and the ghosts see? Should they be able to see all the - * walls or just walls close to them? - * - Add an external observer that is responsible for veryfing safety - * properties. - * - Explore consistency vs. availability tradeoffs in the game design. - * See https://arxiv.org/abs/2109.07771 . + * - What to do in the case of communication failure? + * - What are other possible fault scenarios? + * - What should the AI and the ghosts see? Should they be able to see all the + * walls or just walls close to them? + * - Add an external observer that is responsible for veryfing safety + * properties. + * - Explore consistency vs. availability tradeoffs in the game design. + * See https://arxiv.org/abs/2109.07771 . * **/ target Python { - files: ["include/hbpacman.py", "include/images"] + files: ["include/hbpacman.py", "include/images"] }; preamble {= - import os - import pyautogui - from random import randint - curr_dirname = os.path.dirname(__file__) - sys.path.append(curr_dirname) - import hbpacman as pacman - - # Construct a table of ghost characteristics to access - # using the bank member as the index. - ghost_specs = [ - { - "name": "Pinky", - "directions": pacman.Pinky_directions, - "width": pacman.w, - "height": pacman.m_h, - "image": "images/Pinky.png" - }, - { - "name": "Blinky", - "directions": pacman.Blinky_directions, - "width": pacman.w, - "height": pacman.b_h, - "image": "images/Blinky.png" - }, - { - "name": "Inky", - "directions": pacman.Inky_directions, - "width": pacman.i_w, - "height": pacman.m_h, - "image": "images/Inky.png" - }, - { - "name": "Clyde", - "directions": pacman.Clyde_directions, - "width": pacman.c_w, - "height": pacman.m_h, - "image": "images/Clyde.png" - } - ] + import os + import pyautogui + from random import randint + curr_dirname = os.path.dirname(__file__) + sys.path.append(curr_dirname) + import hbpacman as pacman + + # Construct a table of ghost characteristics to access + # using the bank member as the index. + ghost_specs = [ + { + "name": "Pinky", + "directions": pacman.Pinky_directions, + "width": pacman.w, + "height": pacman.m_h, + "image": "images/Pinky.png" + }, + { + "name": "Blinky", + "directions": pacman.Blinky_directions, + "width": pacman.w, + "height": pacman.b_h, + "image": "images/Blinky.png" + }, + { + "name": "Inky", + "directions": pacman.Inky_directions, + "width": pacman.i_w, + "height": pacman.m_h, + "image": "images/Inky.png" + }, + { + "name": "Clyde", + "directions": pacman.Clyde_directions, + "width": pacman.c_w, + "height": pacman.m_h, + "image": "images/Clyde.png" + } + ] =} #### View reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/pacman.png")) { - input[num_moving_sprites] moving_sprites - input[num_static_sprites] static_sprites - input game_over - input score - input[5] icon_name - input playerpause - input restart - #logical action announcement - #logical action announcementval - #input controllerpause - - output tick - output[5] icon - - state _game_over(False) - state _screen - state _font - state _clock - state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) - state _top_corner_text - state _active(True) - state _announcement(True) - - reaction(startup) {= - dirname = os.path.dirname(__file__) - pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) - pacman.pygame.display.set_icon(pacman_icon) - - self._clock = pacman.pygame.time.Clock() - # Create an 606x606 sized screen - self._screen = pacman.pygame.display.set_mode([606, 606]) - # Set the title of the window - pacman.pygame.display.set_caption("Pacman") - # Create a surface we can draw on - background = pacman.pygame.Surface(self._screen.get_size()) - # Used for converting color maps and such - background = background.convert() - # Fill the screen with a black background - background.fill(pacman.black) - pacman.pygame.font.init() - self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) - self._screen.fill(pacman.black) + input[num_moving_sprites] moving_sprites + input[num_static_sprites] static_sprites + input game_over + input score + input[5] icon_name + input playerpause + input restart + #logical action announcement + #logical action announcementval + #input controllerpause + + output tick + output[5] icon + + state _game_over(False) + state _screen + state _font + state _clock + state _static_sprites({=pacman.pygame.sprite.RenderPlain()=}) + state _top_corner_text + state _active(True) + state _announcement(True) + + reaction(startup) {= + dirname = os.path.dirname(__file__) + pacman_icon=pacman.pygame.image.load(os.path.join(dirname, self.nav_icon)) + pacman.pygame.display.set_icon(pacman_icon) + + self._clock = pacman.pygame.time.Clock() + # Create an 606x606 sized screen + self._screen = pacman.pygame.display.set_mode([606, 606]) + # Set the title of the window + pacman.pygame.display.set_caption("Pacman") + # Create a surface we can draw on + background = pacman.pygame.Surface(self._screen.get_size()) + # Used for converting color maps and such + background = background.convert() + # Fill the screen with a black background + background.fill(pacman.black) + pacman.pygame.font.init() + self._font = pacman.pygame.font.Font("freesansbold.ttf", 24) + self._screen.fill(pacman.black) + + =} + + reaction (icon_name) -> icon {= + for (idx, name) in enumerate(icon_name): + if name.is_present: + icon[idx].set(pacman.pygame.image.load(name.value).convert()) + =} + + timer pygame_tick(0, 100 msec) # 10 FPS + reaction(pygame_tick) -> tick {= + pacman.pygame.display.flip() + self._clock.tick() + tick.set(True) + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.VIDEORESIZE: + # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) + =} + + reaction(static_sprites) {= + # if self._active: + for sprite in static_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + self._static_sprites.add(sprite.value.sprites()) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + self._static_sprites.add(sprite.value) + + self._static_sprites.draw(self._screen) + =} + + reaction(score) {= + self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + reaction(moving_sprites) {= + self._screen.fill(pacman.black) + sprite_list = pacman.pygame.sprite.RenderPlain() + for sprite in moving_sprites: + if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): + sprite.value.draw(self._screen) + elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): + sprite_list.add(sprite.value) - =} - - reaction (icon_name) -> icon {= - for (idx, name) in enumerate(icon_name): - if name.is_present: - icon[idx].set(pacman.pygame.image.load(name.value).convert()) - =} - - timer pygame_tick(0, 100 msec) # 10 FPS - reaction(pygame_tick) -> tick {= - pacman.pygame.display.flip() - self._clock.tick() - tick.set(True) - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.VIDEORESIZE: - # self._screen = pacman.pygame.display.set_mode(event.size, pacman.pygame.RESIZABLE) - =} - - reaction(static_sprites) {= - # if self._active: - for sprite in static_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - self._static_sprites.add(sprite.value.sprites()) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - self._static_sprites.add(sprite.value) - - self._static_sprites.draw(self._screen) - =} - - reaction(score) {= - self._top_corner_text=self._font.render("Score: "+str(score.value), True, pacman.red) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - reaction(moving_sprites) {= - self._screen.fill(pacman.black) - sprite_list = pacman.pygame.sprite.RenderPlain() - for sprite in moving_sprites: - if sprite.is_present and isinstance(sprite.value, pacman.pygame.sprite.Group): - sprite.value.draw(self._screen) - elif isinstance(sprite.value, pacman.pygame.sprite.Sprite): - sprite_list.add(sprite.value) - - sprite_list.draw(self._screen) - self._static_sprites.draw(self._screen) - self._screen.blit(self._top_corner_text, [10, 10]) - =} - - #reaction(game_over) {= - #self._active = False + sprite_list.draw(self._screen) + self._static_sprites.draw(self._screen) + self._screen.blit(self._top_corner_text, [10, 10]) + =} + + #reaction(game_over) {= + #self._active = False # =} -// reaction(playerpause) {= -// if playerpause.is_present and self._game_over == False: -// w = pacman.pygame.Surface((400,200)) # the size of your rect -// w.set_alpha(10) # alpha level -// w.fill((128,128,128)) # this fills the entire surface -// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates -// -// text2=self._font.render("The game is paused.", True, pacman.white) -// self._screen.blit(text2, [135, 303]) -// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) -// self._screen.blit(text3, [165, 333]) -// pacman.pygame.display.flip() -// =} - - reaction(playerpause) {= - if playerpause.is_present and playerpause.value == True and self._game_over == False: - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - text2=self._font.render("The game is paused.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To continue, hit SPACE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pacman.pygame.display.flip() - =} - - reaction(pygame_tick, game_over) {= - #Grey background - if game_over.is_present: - self._game_over = True - pyautogui.keyDown(' ') - #print("The display is ", self._active) - w = pacman.pygame.Surface((400,200)) # the size of your rect - w.set_alpha(10) # alpha level - w.fill((128,128,128)) # this fills the entire surface - self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates - - #Won or lost - text1=self._font.render(game_over.value, True, pacman.white) - self._screen.blit(text1, [235, 233]) - - text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) - self._screen.blit(text2, [135, 303]) - text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) - self._screen.blit(text3, [165, 333]) - pyautogui.keyUp(' ') - pacman.pygame.display.flip() - - #for event in pacman.pygame.event.get(): - # if event.type == pacman.pygame.KEYDOWN: - # print(1) - =} - reaction(restart) {= - #if restart.value: - # self._active = True - # pyautogui.keyUp(' ') - self._game_over = False - =} +// reaction(playerpause) {= +// if playerpause.is_present and self._game_over == False: +// w = pacman.pygame.Surface((400,200)) # the size of your rect +// w.set_alpha(10) # alpha level +// w.fill((128,128,128)) # this fills the entire surface +// self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates +// +// text2=self._font.render("The game is paused.", True, pacman.white) +// self._screen.blit(text2, [135, 303]) +// text3=self._font.render("To continue, hit SPACE.", True, pacman.white) +// self._screen.blit(text3, [165, 333]) +// pacman.pygame.display.flip() +// =} + + reaction(playerpause) {= + if playerpause.is_present and playerpause.value == True and self._game_over == False: + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + text2=self._font.render("The game is paused.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To continue, hit SPACE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pacman.pygame.display.flip() + =} + + reaction(pygame_tick, game_over) {= + #Grey background + if game_over.is_present: + self._game_over = True + pyautogui.keyDown(' ') + #print("The display is ", self._active) + w = pacman.pygame.Surface((400,200)) # the size of your rect + w.set_alpha(10) # alpha level + w.fill((128,128,128)) # this fills the entire surface + self._screen.blit(w, (100,200)) # (0,0) are the top-left coordinates + + #Won or lost + text1=self._font.render(game_over.value, True, pacman.white) + self._screen.blit(text1, [235, 233]) + + text2=self._font.render("To play again, press ENTER or R.", True, pacman.white) + self._screen.blit(text2, [135, 303]) + text3=self._font.render("To quit, press ESCAPE.", True, pacman.white) + self._screen.blit(text3, [165, 333]) + pyautogui.keyUp(' ') + pacman.pygame.display.flip() + + #for event in pacman.pygame.event.get(): + # if event.type == pacman.pygame.KEYDOWN: + # print(1) + =} + reaction(restart) {= + #if restart.value: + # self._active = True + # pyautogui.keyUp(' ') + self._game_over = False + =} } @@ -237,457 +237,457 @@ reactor Display(num_moving_sprites(0), num_static_sprites(0), nav_icon("images/p #### Model ## Base of every character reactor BaseCharacter(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - # reaction to update pause from controller - /**reaction(controllerpause) {= - self._pause = controllerpause - =}**/ + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + # reaction to update pause from controller + /**reaction(controllerpause) {= + self._pause = controllerpause + =}**/ } ## Player # Should be replacable with an AI reactor Player(width(0), height(0), image("images/Trollman.png"), character_class({=pacman.Player=})) { - timer pygame_event(0, 100 msec) - state _active(True) - - input game_over -// logical action ending - output playerpause - output restart - - state _speed(30) - state _boosted(False) - - -// -// reaction(ending) {= -// print(1) -// =} - - input wall_list # Receive updated wall list - input gate_list # Receive updated gate list - input icon - #input controllerpause # from controller to pause at end - - output sprite - output icon_name - - state character_instance - state _wall_list - state _gate_list - state _pause({=False=}) - state _resetting(False) - - - reaction(startup) -> icon_name {= - dirname = os.path.dirname(__file__) - icon_name.set(os.path.join(dirname, self.image)) - =} - - reaction(icon) -> sprite {= - self.character_instance = self.character_class(self.width, self.height, icon.value) - sprite.set(self.character_instance) - =} - - reaction(wall_list, gate_list) {= - self._wall_list = wall_list.value - self._gate_list = gate_list.value - =} - - reaction(pygame_event) -> sprite, playerpause, restart {= - keyboard_events = pacman.pygame.event.get() - for event in keyboard_events: - if event.type == pacman.pygame.QUIT: - request_stop() - - if event.type == pacman.pygame.KEYDOWN: - if event.key == pacman.pygame.K_ESCAPE: - request_stop() - if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: - restart.set(True) - self.character_instance.resetpos() - self._pause = False - self._active = True - print(self.character_instance.rect.left) - if event.key == pacman.pygame.K_b and self._boosted == False: - self._speed += 15 - self._boosted = True - elif event.key == pacman.pygame.K_b and self._boosted == True: - self._speed -= 15 - self._boosted = False - - if event.key == pacman.pygame.K_SPACE and self._active: - if self._pause is False: - self._pause = True - else: - self._pause = False - print(self._pause) - elif event.key == pacman.pygame.K_SPACE and not self._active: - #self.character_instance.resetpos() - #restart.set(True) - print("do not remove comments") - elif self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed) - - if event.type == pacman.pygame.KEYUP and self._pause is False: - if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: - self.character_instance.changespeed(self._speed, 0) - if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: - self.character_instance.changespeed(self._speed * -1, 0) - if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: - self.character_instance.changespeed(0, self._speed) - if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: - self.character_instance.changespeed(0, self._speed * -1) - if event.key == pacman.pygame.K_b: - self._speed -= 10 - - if self._pause is False: - self.character_instance.update( - self._wall_list, - self._gate_list - ) - sprite.set(self.character_instance) - playerpause.set(self._pause) - =} - - reaction(game_over) {= - self._active = False - self._pause = True - self.character_instance.speedzero() -# ending.schedule(MSEC(500)) - =} - + timer pygame_event(0, 100 msec) + state _active(True) + + input game_over +// logical action ending + output playerpause + output restart + + state _speed(30) + state _boosted(False) + + +// +// reaction(ending) {= +// print(1) +// =} + + input wall_list # Receive updated wall list + input gate_list # Receive updated gate list + input icon + #input controllerpause # from controller to pause at end + + output sprite + output icon_name + + state character_instance + state _wall_list + state _gate_list + state _pause({=False=}) + state _resetting(False) + + + reaction(startup) -> icon_name {= + dirname = os.path.dirname(__file__) + icon_name.set(os.path.join(dirname, self.image)) + =} + + reaction(icon) -> sprite {= + self.character_instance = self.character_class(self.width, self.height, icon.value) + sprite.set(self.character_instance) + =} + + reaction(wall_list, gate_list) {= + self._wall_list = wall_list.value + self._gate_list = gate_list.value + =} + + reaction(pygame_event) -> sprite, playerpause, restart {= + keyboard_events = pacman.pygame.event.get() + for event in keyboard_events: + if event.type == pacman.pygame.QUIT: + request_stop() + + if event.type == pacman.pygame.KEYDOWN: + if event.key == pacman.pygame.K_ESCAPE: + request_stop() + if event.key == pacman.pygame.K_r or event.key == pacman.pygame.K_RETURN: + restart.set(True) + self.character_instance.resetpos() + self._pause = False + self._active = True + print(self.character_instance.rect.left) + if event.key == pacman.pygame.K_b and self._boosted == False: + self._speed += 15 + self._boosted = True + elif event.key == pacman.pygame.K_b and self._boosted == True: + self._speed -= 15 + self._boosted = False + + if event.key == pacman.pygame.K_SPACE and self._active: + if self._pause is False: + self._pause = True + else: + self._pause = False + print(self._pause) + elif event.key == pacman.pygame.K_SPACE and not self._active: + #self.character_instance.resetpos() + #restart.set(True) + print("do not remove comments") + elif self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed) + + if event.type == pacman.pygame.KEYUP and self._pause is False: + if event.key == pacman.pygame.K_LEFT or event.key == pacman.pygame.K_a: + self.character_instance.changespeed(self._speed, 0) + if event.key == pacman.pygame.K_RIGHT or event.key == pacman.pygame.K_d: + self.character_instance.changespeed(self._speed * -1, 0) + if event.key == pacman.pygame.K_UP or event.key == pacman.pygame.K_w: + self.character_instance.changespeed(0, self._speed) + if event.key == pacman.pygame.K_DOWN or event.key == pacman.pygame.K_s: + self.character_instance.changespeed(0, self._speed * -1) + if event.key == pacman.pygame.K_b: + self._speed -= 10 + + if self._pause is False: + self.character_instance.update( + self._wall_list, + self._gate_list + ) + sprite.set(self.character_instance) + playerpause.set(self._pause) + =} + + reaction(game_over) {= + self._active = False + self._pause = True + self.character_instance.speedzero() +# ending.schedule(MSEC(500)) + =} + } ## Ghosts # FIXME: Different Ghosts should have different personalities reactor Ghost (directions({=()=}), name("Stinky")) extends BaseCharacter { - input tick - input playerpause # pause from player - input game_over - input restart - - state turn(0) - state steps(0) - state _active(True) - state _resetting(False) - - reaction(playerpause) {= - # if controllerpause.is_present: - # self._pause = controllerpause.value - if playerpause.is_present: - self._pause = playerpause.value - =} - - reaction(tick) -> sprite {= - sprite.set(self.character_instance) - self._resetting = False - if self._pause is False and self._active: - returned = self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.turn = returned[0] - self.steps = returned[1] - self.character_instance.changespeed( - self.directions, - False, - self.turn, - self.steps, - len(self.directions)-1 - ) - self.character_instance.update( - self._wall_list, - False - ) - # if self._resetting: - # self.character_instance.resetpos(self.name) - # self._resetting = False - sprite.set(self.character_instance) - =} - - reaction(game_over) {= - self._active = False - =} - - reaction(restart) {= - self._resetting = True - self._active = True - self.turn = 0 - self.steps = 0 - self.character_instance.resetpos(self.name) - =} - + input tick + input playerpause # pause from player + input game_over + input restart + + state turn(0) + state steps(0) + state _active(True) + state _resetting(False) + + reaction(playerpause) {= + # if controllerpause.is_present: + # self._pause = controllerpause.value + if playerpause.is_present: + self._pause = playerpause.value + =} + + reaction(tick) -> sprite {= + sprite.set(self.character_instance) + self._resetting = False + if self._pause is False and self._active: + returned = self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.turn = returned[0] + self.steps = returned[1] + self.character_instance.changespeed( + self.directions, + False, + self.turn, + self.steps, + len(self.directions)-1 + ) + self.character_instance.update( + self._wall_list, + False + ) + # if self._resetting: + # self.character_instance.resetpos(self.name) + # self._resetting = False + sprite.set(self.character_instance) + =} + + reaction(game_over) {= + self._active = False + =} + + reaction(restart) {= + self._resetting = True + self._active = True + self.turn = 0 + self.steps = 0 + self.character_instance.resetpos(self.name) + =} + } #### Controller reactor GameController(number_of_ghosts(4)) { - output wall_list # List of walls on the map - output gate - output block_list # List of yummy dots for Pac-Man - output score # The game score - output game_over - output frenzy - #output controllerpause - - input[number_of_ghosts] ghost_sprites - input pacman_sprite - input tick # The game tick - input restart - - state _wall_list - state _gate - state _block_list({=pacman.pygame.sprite.RenderPlain()=}) - state _score_to_win(0) - state _score(0) - state _pacman_sprite - state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) - state _energizer_indices({=[0, 0]=}) + output wall_list # List of walls on the map + output gate + output block_list # List of yummy dots for Pac-Man + output score # The game score + output game_over + output frenzy + #output controllerpause + + input[number_of_ghosts] ghost_sprites + input pacman_sprite + input tick # The game tick + input restart + + state _wall_list + state _gate + state _block_list({=pacman.pygame.sprite.RenderPlain()=}) + state _score_to_win(0) + state _score(0) + state _pacman_sprite + state _pacman_collide({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_list({=pacman.pygame.sprite.RenderPlain()=}) + state _energizer_indices({=[0, 0]=}) # state _controllerpause({=False=}) #not necessary # initial mode One { - reaction(startup) -> wall_list, gate {= - _all_sprites_list = pacman.pygame.sprite.RenderPlain() - self._wall_list = pacman.setupRoomOne(_all_sprites_list) - self._gate = pacman.setupGate(_all_sprites_list) - - wall_list.set(self._wall_list) - gate.set(self._gate) - - =} - - reaction(pacman_sprite) {= - self._pacman_collide.empty() - self._pacman_collide.add(pacman_sprite.value) - self._pacman_sprite = pacman_sprite.value - =} - - reaction(startup) -> block_list {= - # Draw the grid - - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) + reaction(startup) -> wall_list, gate {= + _all_sprites_list = pacman.pygame.sprite.RenderPlain() + self._wall_list = pacman.setupRoomOne(_all_sprites_list) + self._gate = pacman.setupGate(_all_sprites_list) + + wall_list.set(self._wall_list) + gate.set(self._gate) + + =} - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 + reaction(pacman_sprite) {= + self._pacman_collide.empty() + self._pacman_collide.add(pacman_sprite.value) + self._pacman_sprite = pacman_sprite.value + =} - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - block_list.set(block) # Send it to be drawn - # print("Finished drawing blocks") + reaction(startup) -> block_list {= + # Draw the grid + + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) + + if b_collide: + continue + if p_collide: + continue + + # Add the block to the list of objects + self._block_list.add(block) + block_list.set(block) # Send it to be drawn + # print("Finished drawing blocks") + + self._score_to_win = len(self._block_list) + print(self._energizer_list) + print(self._score_to_win) + =} + + reaction(pacman_sprite) -> score, game_over, frenzy {= + blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) + energizer_hit_list = pacman.pygame.sprite.RenderPlain() + #FIXME: can also make this more efficient + for block in blocks_hit_list: + if block.rect.width == 10: + energizer_hit_list.add(block) + print(len(energizer_hit_list)) + + # Check the list of collisions. + if len(blocks_hit_list) > 0: + self._score +=len(blocks_hit_list) + + if self._score == self._score_to_win: + game_over.set("Won!") + #pyautogui.keyDown(' ') + #print("Won") + #self._controllerpause = True + #pause isntead of stop game + #controllerpause.set(True) + #request_stop() #remove once above finished + elif len(energizer_hit_list) >= 1: + print("frenzy time") + frenzy.set(True) + + score.set(self._score) + =} + + reaction(ghost_sprites) -> game_over {= + # FIXME: Make this more efficient. + monsta_list = pacman.pygame.sprite.RenderPlain() + for ghost in ghost_sprites: + if ghost.is_present: + monsta_list.add(ghost.value) + + monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) + + if monsta_hit_list: + game_over.set("Lost!") + #print("Lost") + #pyautogui.keyDown(' ') + #print("Lost") + #self._controllerpause = True + #Pause the game instead of stopping it + #controllerpause.set(True) + #request_stop() #remove once above finished + + =} + + # Send the updated blocks + reaction(tick) -> block_list {= + block_list.set(self._block_list) + =} + + reaction(restart) {= + if restart.value: + self._block_list = pacman.pygame.sprite.RenderPlain() + self._energizer_list = pacman.pygame.sprite.RenderPlain() + for row in range(19): + for column in range(19): + if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): + continue + if randint(0, 361) > 358: + block = pacman.Block(pacman.red, 10, 10) + self._energizer_list.add(block) + else: + block = pacman.Block(pacman.yellow, 4, 4) + + # Set a random location for the block + block.rect.x = (30*column+6)+26 + block.rect.y = (30*row+6)+26 + + b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) + p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - print(self._score_to_win) - =} - - reaction(pacman_sprite) -> score, game_over, frenzy {= - blocks_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, self._block_list, True) - energizer_hit_list = pacman.pygame.sprite.RenderPlain() - #FIXME: can also make this more efficient - for block in blocks_hit_list: - if block.rect.width == 10: - energizer_hit_list.add(block) - print(len(energizer_hit_list)) - - # Check the list of collisions. - if len(blocks_hit_list) > 0: - self._score +=len(blocks_hit_list) - - if self._score == self._score_to_win: - game_over.set("Won!") - #pyautogui.keyDown(' ') - #print("Won") - #self._controllerpause = True - #pause isntead of stop game - #controllerpause.set(True) - #request_stop() #remove once above finished - elif len(energizer_hit_list) >= 1: - print("frenzy time") - frenzy.set(True) - - score.set(self._score) - =} - - reaction(ghost_sprites) -> game_over {= - # FIXME: Make this more efficient. - monsta_list = pacman.pygame.sprite.RenderPlain() - for ghost in ghost_sprites: - if ghost.is_present: - monsta_list.add(ghost.value) + if b_collide: + continue + if p_collide: + continue - monsta_hit_list = pacman.pygame.sprite.spritecollide(self._pacman_sprite, monsta_list, False) - - if monsta_hit_list: - game_over.set("Lost!") - #print("Lost") - #pyautogui.keyDown(' ') - #print("Lost") - #self._controllerpause = True - #Pause the game instead of stopping it - #controllerpause.set(True) - #request_stop() #remove once above finished - - =} + # Add the block to the list of objects + self._block_list.add(block) + self._score_to_win = len(self._block_list) + print(self._energizer_list) + self._score = 0 + =} - # Send the updated blocks - reaction(tick) -> block_list {= - block_list.set(self._block_list) - =} - - reaction(restart) {= - if restart.value: - self._block_list = pacman.pygame.sprite.RenderPlain() - self._energizer_list = pacman.pygame.sprite.RenderPlain() - for row in range(19): - for column in range(19): - if (row == 7 or row == 8) and (column == 8 or column == 9 or column == 10): - continue - if randint(0, 361) > 358: - block = pacman.Block(pacman.red, 10, 10) - self._energizer_list.add(block) - else: - block = pacman.Block(pacman.yellow, 4, 4) - - # Set a random location for the block - block.rect.x = (30*column+6)+26 - block.rect.y = (30*row+6)+26 - - b_collide = pacman.pygame.sprite.spritecollide(block, self._wall_list, False) - p_collide = pacman.pygame.sprite.spritecollide(block, self._pacman_collide, False) - - if b_collide: - continue - if p_collide: - continue - - # Add the block to the list of objects - self._block_list.add(block) - self._score_to_win = len(self._block_list) - print(self._energizer_list) - self._score = 0 - =} - - reaction(shutdown) {= - pacman.pygame.quit() - =} + reaction(shutdown) {= + pacman.pygame.quit() + =} # } } main reactor { - ### Controller - controller = new GameController() - - ### Model(s) - player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") - - # Ghosts - ghosts = new[4] Ghost( - width = {= ghost_specs[bank_index]["width"] =}, - height = {= ghost_specs[bank_index]["height"] =}, - image = {= ghost_specs[bank_index]["image"] =}, - directions = {= ghost_specs[bank_index]["directions"] =}, - name = {= ghost_specs[bank_index]["name"] =}, - character_class = ({=pacman.Ghost=}) - ) - - ### View - display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) - - # Send the list of walls to the ghosts so that they can avoid running into walls - (controller.wall_list)+ -> ghosts.wall_list - - # Send the sprites to the display to be drawn - - controller.block_list, - player.sprite, - ghosts.sprite -> - display.moving_sprites - - (controller.wall_list, controller.gate)+ -> - player.wall_list, player.gate_list, display.static_sprites - - (display.tick)+ -> - controller.tick, - ghosts.tick - - #Send pause player to game controller and ghosts - (player.playerpause)+ -> ghosts.playerpause, display.playerpause - - #Send pause controller to player and ghosts - #controller.controllerpause -> player.controllerpause - - player.sprite -> controller.pacman_sprite + ### Controller + controller = new GameController() + + ### Model(s) + player = new Player(width = {=pacman.w=}, height = {=pacman.p_h=}, image = "images/pacman.png") + + # Ghosts + ghosts = new[4] Ghost( + width = {= ghost_specs[bank_index]["width"] =}, + height = {= ghost_specs[bank_index]["height"] =}, + image = {= ghost_specs[bank_index]["image"] =}, + directions = {= ghost_specs[bank_index]["directions"] =}, + name = {= ghost_specs[bank_index]["name"] =}, + character_class = ({=pacman.Ghost=}) + ) + + ### View + display = new Display( num_moving_sprites = 6, num_static_sprites = 2 ) + + # Send the list of walls to the ghosts so that they can avoid running into walls + (controller.wall_list)+ -> ghosts.wall_list + + # Send the sprites to the display to be drawn + + controller.block_list, + player.sprite, + ghosts.sprite -> + display.moving_sprites + + (controller.wall_list, controller.gate)+ -> + player.wall_list, player.gate_list, display.static_sprites + + (display.tick)+ -> + controller.tick, + ghosts.tick + + #Send pause player to game controller and ghosts + (player.playerpause)+ -> ghosts.playerpause, display.playerpause + + #Send pause controller to player and ghosts + #controller.controllerpause -> player.controllerpause + + player.sprite -> controller.pacman_sprite - ghosts.sprite -> controller.ghost_sprites - - controller.score -> display.score - - ghosts.icon_name, player.icon_name -> display.icon_name - - display.icon -> ghosts.icon, player.icon - - #sending game_over to player causes problem - (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over - - - (player.restart)+ -> controller.restart, display.restart, ghosts.restart - + ghosts.sprite -> controller.ghost_sprites + + controller.score -> display.score + + ghosts.icon_name, player.icon_name -> display.icon_name + + display.icon -> ghosts.icon, player.icon + + #sending game_over to player causes problem + (controller.game_over)+ -> display.game_over, player.game_over, ghosts.game_over + + + (player.restart)+ -> controller.restart, display.restart, ghosts.restart + } \ No newline at end of file diff --git a/experimental/Python/src/Testing/HistoryMRTest.lf b/experimental/Python/src/Testing/HistoryMRTest.lf index 50743fac..fb60b921 100644 --- a/experimental/Python/src/Testing/HistoryMRTest.lf +++ b/experimental/Python/src/Testing/HistoryMRTest.lf @@ -3,55 +3,55 @@ target Python {}; preamble{= - from random import randint + from random import randint =} ### Ticking Reactor reactor Ticker { - timer tick(0, 500msec) + timer tick(0, 500msec) - output click + output click - reaction(tick) -> click {= - click.set(randint(0, 9)) - =} + reaction(tick) -> click {= + click.set(randint(0, 9)) + =} } ### Modal Reactor reactor Mody { - input click - - method setter(click, mod) -> reset(One), reset(Two) {= - if click <= 4: - print("In mode " + str(mod) + ", val: ", str(click)) - else: - print(click, " is val, switching mode") - if mod == 1: - Two.set() - else: - One.set() + input click + + method setter(click, mod) -> reset(One), reset(Two) {= + if click <= 4: + print("In mode " + str(mod) + ", val: ", str(click)) + else: + print(click, " is val, switching mode") + if mod == 1: + Two.set() + else: + One.set() + =} + + initial mode One { + reaction(startup) {= + print("was started") =} - - initial mode One { - reaction(startup) {= - print("was started") - =} - reaction(click) -> reset(Two) {= - self.setter(click.value, 1) - =} - } mode Two { - reaction(startup) {= - print("was started") - =} - reaction(click) -> reset(One) {= - self.setter(click.value, 2) - =} - } + reaction(click) -> reset(Two) {= + self.setter(click.value, 1) + =} + } mode Two { + reaction(startup) {= + print("was started") + =} + reaction(click) -> reset(One) {= + self.setter(click.value, 2) + =} + } } main reactor { - ticky = new Ticker() - moody = new Mody() + ticky = new Ticker() + moody = new Mody() - ticky.click -> moody.click -} \ No newline at end of file + ticky.click -> moody.click +} From 44b5896e6f5452a5adec11d6a9772693475a4e7d Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Tue, 20 Jun 2023 17:29:06 -0700 Subject: [PATCH 3/3] Reformat according to new state of formatter. --- C/src/ChatApplication/SimpleChat.lf | 18 +- C/src/Delay.lf | 67 ++-- .../DistributedDatabase/FederatedDatabase.lf | 28 +- .../DistributedDatabase/ReplicatedDatabase.lf | 289 +++++++-------- .../ReplicatedDatabaseThree.lf | 36 +- C/src/DistributedHelloWorld/HelloWorld.lf | 62 ++-- .../DistributedHelloWorld/HelloWorldAfter.lf | 16 +- .../HelloWorldDecentralized.lf | 46 ++- .../HelloWorldDecentralizedSTP.lf | 55 ++- .../HelloWorldPhysical.lf | 30 +- .../HelloWorldPhysicalAfter.lf | 24 +- .../docker/HelloWorldContainerized.lf | 17 +- .../FederatedResourceManagement.lf | 75 ++-- .../ResourceManagement.lf | 153 ++++---- C/src/Parallelism/ForkJoin.lf | 82 +++-- C/src/Parallelism/Pipeline.lf | 84 ++--- C/src/ProtocolBuffers/HelloProtocolBuffers.lf | 47 ++- C/src/ROS/BasicROS.lf | 332 +++++++++-------- C/src/ROS/PTIDES-ROS.lf | 344 +++++++++--------- C/src/ReflexGame/ReflexGame.lf | 22 +- C/src/ReflexGame/ReflexGameTest.lf | 15 +- C/src/RockPaperScissors.lf | 11 +- C/src/SleepingBarber.lf | 262 ++++++------- C/src/Smokers.lf | 191 +++++----- C/src/TrainDoor/TrainDoor.lf | 60 +-- C/src/TrainDoor/TrainDoorAsymmetric.lf | 75 ++-- C/src/TrainDoor/TrainDoorSimplest.lf | 52 +-- C/src/TrainDoor/TrainDoorWithDeadlines.lf | 98 ++--- C/src/TrainDoor/TrainDoorWithDoorOpenState.lf | 80 ++-- C/src/TrainDoor/TrainDoorWithOpen.lf | 79 ++-- C/src/browser-ui/BrowserUI.lf | 235 ++++++------ C/src/browser-ui/WebSocket.lf | 27 +- C/src/browser-ui/WebSocketServer.lf | 51 ++- C/src/car-brake/CarBrake.lf | 60 ++- C/src/car-brake/CarBrake2.lf | 19 +- C/src/car-brake/CarBrake3.lf | 19 +- C/src/deadlines/AnytimePrime.lf | 23 +- C/src/deadlines/Deadline.lf | 9 +- C/src/deadlines/PeriodicDeadline.lf | 72 ++-- C/src/keyboard/Keyboard.lf | 98 ++--- C/src/leader-election/Election.lf | 54 ++- C/src/lib/PoissonClock.lf | 29 +- C/src/lib/PrintToFile.lf | 18 +- C/src/lib/Random.lf | 43 +-- C/src/lib/RandomDelay.lf | 24 +- .../FurutaPendulum/FurutaPendulum.lf | 55 ++- .../FurutaPendulumDisturbance.lf | 43 ++- .../FurutaPendulum/PendulumController.lf | 94 ++--- .../FurutaPendulum/PendulumSimulation.lf | 112 +++--- C/src/modal_models/FurutaPendulum/Print.lf | 24 +- C/src/mqtt/MQTTDistributed.lf | 79 ++-- C/src/mqtt/MQTTDistributedActivity.lf | 59 ++- C/src/mqtt/MQTTLegacy.lf | 83 ++--- C/src/mqtt/MQTTLogical.lf | 64 ++-- C/src/mqtt/MQTTPhysical.lf | 59 ++- C/src/mqtt/lib/MQTTPublisher.lf | 131 ++++--- C/src/mqtt/lib/MQTTSubscriber.lf | 147 ++++---- C/src/mqtt/lib/MQTTTestReactors.lf | 23 +- C/src/patterns/Chain_01_SendReceive.lf | 16 +- C/src/patterns/Chain_02_Pipeline.lf | 39 +- C/src/patterns/FullyConnected_00_Broadcast.lf | 44 +-- .../patterns/FullyConnected_01_Addressable.lf | 42 +-- C/src/patterns/Loop_01_Single.lf | 20 +- C/src/patterns/Loop_02_SingleDelay.lf | 27 +- C/src/patterns/lib/SendersAndReceivers.lf | 180 ++++----- C/src/patterns/lib/TakeTime.lf | 34 +- C/src/rhythm/PlayWaveform.lf | 148 ++++---- C/src/rhythm/Rhythm.lf | 94 ++--- C/src/rhythm/RhythmDistributed.lf | 80 ++-- C/src/rhythm/RhythmDistributedNoUI.lf | 64 ++-- C/src/rhythm/SensorSimulator.lf | 30 +- C/src/rhythm/Sound.lf | 30 +- C/src/robot/CompositeRobot.lf | 89 ++--- C/src/rosace/AircraftSimulator.lf | 218 +++++------ C/src/rosace/Rosace.lf | 102 +++--- C/src/rosace/RosaceController.lf | 266 ++++++-------- C/src/rosace/RosaceWithUI.lf | 106 +++--- C/src/sdv/ParkingAssist.lf | 203 +++++------ C/src/simulation/MemoryHierarchy.lf | 126 +++---- C/src/simulation/PoissonProcess.lf | 21 +- CCpp/src/DoorLock/DoorLock.lf | 85 +++-- CCpp/src/DoorLock/lib/AuthSim.lf | 13 +- CCpp/src/DoorLock/lib/PropagationDelaySim.lf | 16 +- CCpp/src/DoorLock/lib/UserInteraction.lf | 37 +- .../ROS/MigrationGuide/lf-project/src/Main.lf | 9 +- .../MigrationGuide/lf-project/src/Receiver.lf | 11 +- .../MigrationGuide/lf-project/src/Sender.lf | 14 +- CCpp/src/ROS/ROSBuiltInSerialization.lf | 81 ++--- CCpp/src/ROS/ROSSerialization.lf | 72 ++-- Cpp/AlarmClock/src/AlarmClock.lf | 55 ++- Cpp/AlarmClock/src/Clock.lf | 77 ++-- Cpp/AlarmClock/src/Network.lf | 68 ++-- Cpp/CarBrake/src/CarBrake.lf | 76 ++-- Cpp/CarBrake/src/CarBrake2.lf | 64 ++-- .../src/FullyConnected_00_Broadcast.lf | 20 +- .../src/FullyConnected_01_Addressable.lf | 23 +- .../src/MatrixConnectedRowsAndColumns.lf | 31 +- Cpp/ROS2/src/MinimalPublisher.lf | 19 +- Cpp/ROS2/src/MinimalSubscriber.lf | 21 +- Cpp/ReflexGame/src/ReflexGame.lf | 68 ++-- Cpp/RequestResponse/src/Add.lf | 25 +- Cpp/RequestResponse/src/AddWithContext.lf | 18 +- Cpp/RequestResponse/src/ContextManager.lf | 4 +- Cpp/RequestResponse/src/MAC.lf | 35 +- .../DoubleUnlock/DoubleUnlockDemo.lf | 85 ++--- .../src/DigitalTwin/DoubleUnlock/Simulator.lf | 41 +-- Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf | 48 ++- Python/src/Piano/Piano.lf | 201 +++++----- .../src/ROS/PythonMigration/lf-python/Main.lf | 24 +- .../ROS/PythonMigration/lf-python/Receiver.lf | 26 +- .../ROS/PythonMigration/lf-python/Sender.lf | 33 +- Python/src/ReflexGame/ReflexGame.lf | 163 ++++----- Python/src/TrainDoor/TrainDoor.lf | 36 +- Python/src/YOLOv5/YOLOv5_Webcam.lf | 130 +++---- Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf | 46 +-- Python/src/acas/ACASXu.lf | 80 ++-- Python/src/acas/ACASXu2.lf | 71 ++-- Python/src/acas/lib/ACASController.lf | 91 +++-- Python/src/acas/lib/ACASNN.lf | 63 ++-- Python/src/acas/lib/Aircraft.lf | 67 ++-- Python/src/acas/lib/XYPlotter.lf | 36 +- Rust/src/CounterProgram.lf | 27 +- Rust/src/Snake/KeyboardEvents.lf | 17 +- Rust/src/Snake/Snake.lf | 54 +-- TypeScript/src/ChatApplication/SimpleChat.lf | 54 ++- .../src/DistributedHelloWorld/HelloWorld.lf | 60 +-- .../src/HTTPSRequestReactor/HTTPSRequest.lf | 36 +- .../src/HTTPServerReactor/HTTPServer.lf | 42 ++- TypeScript/src/SimpleWebserver.lf | 36 +- 129 files changed, 4211 insertions(+), 4781 deletions(-) diff --git a/C/src/ChatApplication/SimpleChat.lf b/C/src/ChatApplication/SimpleChat.lf index 10bcb7ab..9e9a8a90 100644 --- a/C/src/ChatApplication/SimpleChat.lf +++ b/C/src/ChatApplication/SimpleChat.lf @@ -1,14 +1,12 @@ /** - * This program is a simple federated chat application written in Lingua Franca - * (LF) for two users, represented by two instances of the `ChatHandler` - * reactor, `a` and `b`. Each `ChatHandler` consists of an `InputHandler` and a - * `Printer`. The `InputHandler` is responsible for receiving user input from - * the console and sending it as a message. When the user enters a message, the - * `InputHandler` reads it, prints it, and then sends it via its `out` port. The - * message then gets relayed by the `ChatHandler` via its `send` port. On the - * receiving side, the `ChatHandler` directs the incoming message through its - * `receive` port to the `Printer` reactor, which prints the received message. - * This process creates a two-way communication where `a` and `b` can send and + * This program is a simple federated chat application written in Lingua Franca (LF) for two users, + * represented by two instances of the `ChatHandler` reactor, `a` and `b`. Each `ChatHandler` + * consists of an `InputHandler` and a `Printer`. The `InputHandler` is responsible for receiving + * user input from the console and sending it as a message. When the user enters a message, the + * `InputHandler` reads it, prints it, and then sends it via its `out` port. The message then gets + * relayed by the `ChatHandler` via its `send` port. On the receiving side, the `ChatHandler` + * directs the incoming message through its `receive` port to the `Printer` reactor, which prints + * the received message. This process creates a two-way communication where `a` and `b` can send and * receive messages from each other. * * @author Byeonggil Jun (junbg@hanyang.ac.kr) diff --git a/C/src/Delay.lf b/C/src/Delay.lf index 4538f4d0..69f55614 100644 --- a/C/src/Delay.lf +++ b/C/src/Delay.lf @@ -1,58 +1,53 @@ /** - * This (rather trivial) example illustrates a logical action used - * to model a delay. The delay is also realized a second time - * using the `after` keyword. - * + * This (rather trivial) example illustrates a logical action used to model a delay. The delay is + * also realized a second time using the `after` keyword. + * * @author Marten Lohstroh * @author Edward A. Lee */ -target C {timeout: 1 sec}; +target C { + timeout: 1 sec +} main reactor { - ramp = new Ramp(); - delay = new Delay2(); - print = new Print(); - ramp.y -> delay.x; - delay.y -> print.x; - - ramp2 = new Ramp(); - print2 = new Print(); - ramp2.y -> print2.x after 50 msec; + ramp = new Ramp() + delay = new Delay2() + print = new Print() + ramp.y -> delay.x + delay.y -> print.x + + ramp2 = new Ramp() + print2 = new Print() + ramp2.y -> print2.x after 50 msec } -/** - * Generate a counting sequence with outputs every 100 msec. - */ +/** Generate a counting sequence with outputs every 100 msec. */ reactor Ramp { - timer t(0, 100 msec); - output y:int; - state count:int(0); + timer t(0, 100 msec) + output y: int + state count: int = 0 + reaction(t) -> y {= lf_set(y, self->count); self->count++; =} } -/** - * Realize a logical delay of 50 msec. - */ +/** Realize a logical delay of 50 msec. */ reactor Delay2 { - logical action a(50 msec):int; - input x:int; - output y:int; - reaction(a) -> y {= - lf_set(y, a->value); - =} - reaction(x) -> a {= - lf_schedule_int(a, 0, x->value); - =} + logical action a(50 msec): int + input x: int + output y: int + + reaction(a) -> y {= lf_set(y, a->value); =} + + reaction(x) -> a {= lf_schedule_int(a, 0, x->value); =} } -/** - * Print the (elapsed) logical and physical times at which inputs are received. - */ +/** Print the (elapsed) logical and physical times at which inputs are received. */ reactor Print { - input x:int; + input x: int + reaction(x) {= printf("Logical time: %lld, Physical time %lld" ", Value: %d\n", diff --git a/C/src/DistributedDatabase/FederatedDatabase.lf b/C/src/DistributedDatabase/FederatedDatabase.lf index ff3a1ef8..b79e330c 100644 --- a/C/src/DistributedDatabase/FederatedDatabase.lf +++ b/C/src/DistributedDatabase/FederatedDatabase.lf @@ -1,7 +1,6 @@ /** - * Federated version of ReplicatedDatabase. - * This is identical, except that it is federated. - * + * Federated version of ReplicatedDatabase. This is identical, except that it is federated. + * * @author Edward A. Lee * @author Soroush Bateni */ @@ -12,22 +11,19 @@ target C { import Platform from "ReplicatedDatabase.lf" -federated reactor ( - query_period:time(1 sec), - num_remote_inputs:int(1) -) { +federated reactor(query_period: time = 1 sec, num_remote_inputs: int = 1) { a = new Platform( - query_period = query_period, + query_period=query_period, update_period = 5 sec, - update_amount = 100, + update_amount=100, name = "San Francisco", - num_remote_inputs = num_remote_inputs); + num_remote_inputs=num_remote_inputs) b = new Platform( - query_period = query_period, + query_period=query_period, update_period = 1 sec, - update_amount = -20, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - b.publish -> a.update; - a.publish -> b.update; + update_amount=-20, + name="Berkeley", + num_remote_inputs=num_remote_inputs) + b.publish -> a.update + a.publish -> b.update } diff --git a/C/src/DistributedDatabase/ReplicatedDatabase.lf b/C/src/DistributedDatabase/ReplicatedDatabase.lf index b10696c5..4b9b1730 100644 --- a/C/src/DistributedDatabase/ReplicatedDatabase.lf +++ b/C/src/DistributedDatabase/ReplicatedDatabase.lf @@ -1,16 +1,15 @@ /** - * Test program illustrating the architecture of a replicated distributed - * database. This models a simple banking system that maintains a single - * balance in multiple locations. Deposits and withdrawals (updates) can - * be performed at any of the locations. In the model, these updates - * are performed by a "Server" model emulating a web server. The server - * also periodically queries for the balance. All locations are required - * to report the same balance given the same time-stamped query. - * - * If the two servers simultaneously update the record, then both updates - * are applied. Any query at the time of these updates is required to - * report the result after both updates have been performed. - * + * Test program illustrating the architecture of a replicated distributed database. This models a + * simple banking system that maintains a single balance in multiple locations. Deposits and + * withdrawals (updates) can be performed at any of the locations. In the model, these updates are + * performed by a "Server" model emulating a web server. The server also periodically queries for + * the balance. All locations are required to report the same balance given the same time-stamped + * query. + * + * If the two servers simultaneously update the record, then both updates are applied. Any query at + * the time of these updates is required to report the result after both updates have been + * performed. + * * @author Edward A. Lee * @author Soroush Bateni */ @@ -18,96 +17,88 @@ target C { timeout: 5 sec } -main reactor ( - query_period:time(1 sec), - num_remote_inputs:int(1) -) { +main reactor(query_period: time = 1 sec, num_remote_inputs: int = 1) { a = new Platform( - query_period = query_period, + query_period=query_period, update_period = 5 sec, - update_amount = 100, + update_amount=100, name = "San Francisco", - num_remote_inputs = num_remote_inputs); + num_remote_inputs=num_remote_inputs) b = new Platform( - query_period = query_period, + query_period=query_period, update_period = 1 sec, - update_amount = -20, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - b.publish -> a.update; - a.publish -> b.update; + update_amount=-20, + name="Berkeley", + num_remote_inputs=num_remote_inputs) + b.publish -> a.update + a.publish -> b.update } /** - * Mockup for a web server that issues deposits or withdrawals - * as well as periodic queries for the balance in the database. - * The queries and updates are issued periodically with the period - * and amount of the update controlled by parameters. - * In a real server, these outputs would not be periodic, but - * rather would be triggered by external events, such as incoming - * HTTP requests. - * - * This reactor expects a reply to each query. It prints those - * replies. It produces an error in its shutdown reaction if - * the number of replies does not match the number of queries. - * + * Mockup for a web server that issues deposits or withdrawals as well as periodic queries for the + * balance in the database. The queries and updates are issued periodically with the period and + * amount of the update controlled by parameters. In a real server, these outputs would not be + * periodic, but rather would be triggered by external events, such as incoming HTTP requests. + * + * This reactor expects a reply to each query. It prints those replies. It produces an error in its + * shutdown reaction if the number of replies does not match the number of queries. + * * @param query_period The period of query outputs. * @param update_period The period of update outputs. * @param update_amount The amount of each deposit (or withdrawal if negative). * @param server_name The name (for reporting). - * + * * @input reply Accepts the reply to a query for the balance. - * + * * @output query Issue a query, expecting a reply. * @output update Issue an update. */ reactor Server( - name:char*("unnamed server"), - query_period:time(150 msec), - update_period:time(100 msec), - update_deadline:time(200 msec), - update_amount:int(0), - server_name:char*("unnamed server") -) { - timer query_trigger(0, query_period); - timer update_trigger(0, update_period); - input reply:int; - output query:bool; - output update:int; - state queries_outstanding:int(0); + name: char* = "unnamed server", + query_period: time = 150 msec, + update_period: time = 100 msec, + update_deadline: time = 200 msec, + update_amount: int = 0, + server_name: char* = "unnamed server") { + timer query_trigger(0, query_period) + timer update_trigger(0, update_period) + input reply: int + output query: bool + output update: int + state queries_outstanding: int = 0 + reaction(query_trigger) -> query {= lf_set(query, true); self->queries_outstanding++; =} - - reaction(update_trigger) -> update {= - lf_set(update, self->update_amount); - =} deadline(update_deadline) {= + + reaction(update_trigger) -> update {= lf_set(update, self->update_amount); =} deadline( + update_deadline) {= tag_t tag = lf_tag(); lf_print_error("At tag (%lld, %u), deadline missed at database \"%s\". Rejecting update.\n" " Elapsed physical time is %lld.", - tag.time - lf_time_start(), + tag.time - lf_time_start(), tag.microstep, self->name, lf_time_physical_elapsed() ); =} - + reaction(reply) {= lf_print("***** At tag (%lld, %u), server \"%s\" reports balance: %d.", lf_time_logical_elapsed(), lf_tag().microstep, self->server_name, reply->value ); self->queries_outstanding--; =} - + reaction(shutdown) {= if (self->queries_outstanding != 0) { - lf_print_error("Server \"%s\": Number of queries with no reply: %d.", + lf_print_error("Server \"%s\": Number of queries with no reply: %d.", self->server_name, self->queries_outstanding ); } else { - lf_print("Server \"%s\" successfully replied to all queries.", + lf_print("Server \"%s\" successfully replied to all queries.", self->server_name ); } @@ -115,37 +106,31 @@ reactor Server( } /** - * A mockup for a replicated database. This simple database contains - * only one record, and the value of that record is an integer. - * It represents a bank balance, where deposits and withdrawals - * (updates) can occur at any node in the system. - * If two or more updates occur at the same logical time, then all - * are applied. - * - * This reactor has two update inputs, `local_update` and `remote_update`. - * The first is intended to be used for updates to the database that are - * generated on the local platform. The second receives notifications - * of updates on remote platforms. When a `local_update` is received, - * the update will also be sent to the `publish` output so that it can - * be forwarded to other replicas. When any `remote_update` input arrives, - * its value will be added to the balance. + * A mockup for a replicated database. This simple database contains only one record, and the value + * of that record is an integer. It represents a bank balance, where deposits and withdrawals + * (updates) can occur at any node in the system. If two or more updates occur at the same logical + * time, then all are applied. + * + * This reactor has two update inputs, `local_update` and `remote_update`. The first is intended to + * be used for updates to the database that are generated on the local platform. The second receives + * notifications of updates on remote platforms. When a `local_update` is received, the update will + * also be sent to the `publish` output so that it can be forwarded to other replicas. When any + * `remote_update` input arrives, its value will be added to the balance. + * + * The `query` input is used to retrieve the current balance. The balance will be sent to the + * `balance` output in response. If a `query` and an update arrive simultaneously, the reply will + * include the cumulative effect of all the updates. + * + * Instances of these `Database` reactors can be arranged in ring or in a broadcast configuration, + * where each replica sends updates to all other replicas. + * + * There is a deadline on the `local_update` inputs. If this deadline is violated, then the update + * is rejected and has no effect on the balance. * - * The `query` input is used to retrieve the current balance. The - * balance will be sent to the `balance` output in response. - * If a `query` and an update arrive simultaneously, the reply - * will include the cumulative effect of all the updates. - * - * Instances of these `Database` reactors can be arranged in ring - * or in a broadcast configuration, where each replica sends updates to - * all other replicas. - * - * There is a deadline on the `local_update` inputs. If this deadline is - * violated, then the update is rejected and has no effect on the balance. - * * @param update_deadline A deadline imposed on the reaction to `local_update` inputs. * @param name A name for the database instance (used in reporting). * @param num_remote_inputs The number of inputs from other database replicas. - * + * * @input local_update An update (deposit or withdrawal) to add to the record. * @input remote_update A multiport input for receiving updates from other replicas. * @input query A trigger to read the current value of the record. @@ -153,16 +138,13 @@ reactor Server( * @output publish This is just the update passed through. * @output balance The time value of the record. */ -reactor Database( - name:char*("unnamed database"), - num_remote_inputs:int(1) -) { - input local_update:int; - input[num_remote_inputs] remote_update:int; - input query:bool; - output balance:int; - state record:int(0); - +reactor Database(name: char* = "unnamed database", num_remote_inputs: int = 1) { + input local_update: int + input[num_remote_inputs] remote_update: int + input query: bool + output balance: int + state record: int = 0 + reaction(local_update, remote_update) {= if (local_update->is_present) { self->record += local_update->value; @@ -179,79 +161,70 @@ reactor Database( self->record, lf_time_physical_elapsed() ); - =} STP (4 msec) {= -#ifdef FEDERATED_DECENTRALIZED - for (int i = 0; i < remote_update_width; i++) { - if (remote_update[i]->is_present) { - lf_print_warning("At tag (%lld, %u), database \"%s\" " - "received remote update (%d) with intended tag (%lld, %u).\n" - " Balance previously reported may have been incorrect.\n" - " Elapsed physical time is %lld.", - current_tag.time - lf_time_start(), - current_tag.microstep, - self->name, - remote_update[i]->value, - remote_update[i]->intended_tag.time - lf_time_start(), - remote_update[i]->intended_tag.microstep, - lf_time_physical_elapsed() - ); - self->record += remote_update[i]->value; - } - } -#else - // The tardy handler should not be invoked - lf_print_error_and_exit("FATAL: Update is tardy and coordination is not decentralized."); -#endif - =} - - reaction(query) -> balance {= - lf_set(balance, self->record); + =} STP(4 msec) {= + #ifdef FEDERATED_DECENTRALIZED + for (int i = 0; i < remote_update_width; i++) { + if (remote_update[i]->is_present) { + lf_print_warning("At tag (%lld, %u), database \"%s\" " + "received remote update (%d) with intended tag (%lld, %u).\n" + " Balance previously reported may have been incorrect.\n" + " Elapsed physical time is %lld.", + current_tag.time - lf_time_start(), + current_tag.microstep, + self->name, + remote_update[i]->value, + remote_update[i]->intended_tag.time - lf_time_start(), + remote_update[i]->intended_tag.microstep, + lf_time_physical_elapsed() + ); + self->record += remote_update[i]->value; + } + } + #else + // The tardy handler should not be invoked + lf_print_error_and_exit("FATAL: Update is tardy and coordination is not decentralized."); + #endif =} + + reaction(query) -> balance {= lf_set(balance, self->record); =} } /** - * A mockup of a platform (e.g. a pod, a virtual machine, a physical - * machine, etc.) that hosts a replicated database and a web server. - * The platform performs local updates and accepts remote updates. - * Instances of this platform can be arranged in a ring or in a - * broadcast configuration, where each platform broadcasts updates - * to all other platforms. - * + * A mockup of a platform (e.g. a pod, a virtual machine, a physical machine, etc.) that hosts a + * replicated database and a web server. The platform performs local updates and accepts remote + * updates. Instances of this platform can be arranged in a ring or in a broadcast configuration, + * where each platform broadcasts updates to all other platforms. + * * @param query_period The period at which the balance is queried. * @param update_period The period at which local updates are generated. * @param update_amount The amount of each local update. * @param num_remote_inputs The number of remote inputs from other replicas. * @param STP The safe-to-process offset (used only in decentralized coordination). * @param name The name assigned to this platform (for reporting). - * + * * @input update An update notification from another replica. - * + * * @output publish A copy of any local updates. */ reactor Platform( - query_period:time(150 msec), - update_period:time(100 msec), - update_amount:int(0), - num_remote_inputs:int(1), - name:char*("unnamed platform") // Used for more visible logging -) { - input[num_remote_inputs] update:int; - output publish:int; + query_period: time = 150 msec, + update_period: time = 100 msec, + update_amount: int = 0, + num_remote_inputs: int = 1, + // Used for more visible logging + name: char* = "unnamed platform") { + input[num_remote_inputs] update: int + output publish: int server = new Server( - name = name, - query_period = query_period, - update_period = update_period, - update_amount = update_amount, - server_name = name - ); - database = new Database( - name = name, - num_remote_inputs = num_remote_inputs - ); - server.query -> database.query; - server.update -> database.local_update; - database.balance -> server.reply; - server.update -> publish; - update -> database.remote_update; + name=name, + query_period=query_period, + update_period=update_period, + update_amount=update_amount, + server_name=name) + database = new Database(name=name, num_remote_inputs=num_remote_inputs) + server.query -> database.query + server.update -> database.local_update + database.balance -> server.reply + server.update -> publish + update -> database.remote_update } - diff --git a/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf b/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf index 74e7746f..8056f319 100644 --- a/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf +++ b/C/src/DistributedDatabase/ReplicatedDatabaseThree.lf @@ -1,6 +1,5 @@ /** - * A version of ReplicatedDatabase.lf with three replicas arranged in - * a broadcast pattern. + * A version of ReplicatedDatabase.lf with three replicas arranged in a broadcast pattern. * @author Edward A. Lee * @author Soroush Bateni */ @@ -10,29 +9,26 @@ target C { import Platform from "ReplicatedDatabase.lf" -main reactor ( - query_period:time(1 sec), - num_remote_inputs:int(2) -) { +main reactor(query_period: time = 1 sec, num_remote_inputs: int = 2) { a = new Platform( - query_period = query_period, + query_period=query_period, update_period = 5 sec, - update_amount = 100, + update_amount=100, name = "San Francisco", - num_remote_inputs = num_remote_inputs); + num_remote_inputs=num_remote_inputs) b = new Platform( - query_period = query_period, + query_period=query_period, update_period = 1 sec, - update_amount = -20, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); + update_amount=-20, + name="Berkeley", + num_remote_inputs=num_remote_inputs) c = new Platform( - query_period = query_period, + query_period=query_period, update_period = 3 sec, - update_amount = 10, - name = "Berkeley", - num_remote_inputs = num_remote_inputs); - b.publish, c.publish -> a.update; - a.publish, c.publish -> b.update; - a.publish, b.publish -> c.update; + update_amount=10, + name="Berkeley", + num_remote_inputs=num_remote_inputs) + b.publish, c.publish -> a.update + a.publish, c.publish -> b.update + a.publish, b.publish -> c.update } diff --git a/C/src/DistributedHelloWorld/HelloWorld.lf b/C/src/DistributedHelloWorld/HelloWorld.lf index 46940a34..91c0ff6f 100644 --- a/C/src/DistributedHelloWorld/HelloWorld.lf +++ b/C/src/DistributedHelloWorld/HelloWorld.lf @@ -1,46 +1,43 @@ /** - * Distributed LF program where a MessageGenerator creates a string - * message that is sent via the RTI (runtime infrastructure) to a - * receiver that prints the message. - * + * Distributed LF program where a MessageGenerator creates a string message that is sent via the RTI + * (runtime infrastructure) to a receiver that prints the message. + * * The code generator generates three programs: - * - * * bin/DistributedHelloWorld: A script that launches the other three - * programs on localhost. Run this program. - * - * * bin/DistrubtedHelloWorld_source: The program that produces the sequence - * of messages. - * - * * bin/DistrubtedHelloWorld_print: The program that produces the sequence - * of messages. - * - * To run this manually, you can start the RTI and then each of the last - * two programs. They will synchronize the start time and run for 10 seconds. - * - * Note: The RTI is a separate program that has to be installed separately. - * See https://github.com/lf-lang/reactor-c/tree/main/core/federated/RTI - * + * + * * bin/DistributedHelloWorld: A script that launches the other three programs on localhost. Run + * this program. + * + * * bin/DistrubtedHelloWorld_source: The program that produces the sequence of messages. + * + * * bin/DistrubtedHelloWorld_print: The program that produces the sequence of messages. + * + * To run this manually, you can start the RTI and then each of the last two programs. They will + * synchronize the start time and run for 10 seconds. + * + * Note: The RTI is a separate program that has to be installed separately. See + * https://github.com/lf-lang/reactor-c/tree/main/core/federated/RTI + * * @author Edward A. Lee */ target C { timeout: 10 secs -}; +} /** - * Reactor that generates a sequence of messages, one per second. - * The message will be a string consisting of a prefix string followed - * by a count. + * Reactor that generates a sequence of messages, one per second. The message will be a string + * consisting of a prefix string followed by a count. * @param prefix The prefix string. * @output message The message. */ -reactor MessageGenerator(prefix:string("")) { +reactor MessageGenerator(prefix: string = "") { // Output type char* instead of string is used for dynamically // allocated character arrays (as opposed to static constant strings). - output message:char*; - state count:int(1); + output message: char* + state count: int = 1 // Send first message after 1 sec so that the startup reactions // do not factor into the transport time measurement on the first message. - timer t(1 sec, 1 sec); + timer t(1 sec, 1 sec) + reaction(t) -> message {= // With NULL, 0 arguments, snprintf tells us how many bytes are needed. // Add one for the null terminator. @@ -64,7 +61,8 @@ reactor MessageGenerator(prefix:string("")) { * @input message The message. */ reactor PrintMessage { - input message:char*; + input message: char* + reaction(message) {= tag_t tag = lf_tag(); lf_print("At (elapsed) logical tag (%lld, %u), print receives: %s", @@ -75,7 +73,7 @@ reactor PrintMessage { } federated reactor HelloWorld { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; + source = new MessageGenerator(prefix = "Hello World") + print = new PrintMessage() + source.message -> print.message } diff --git a/C/src/DistributedHelloWorld/HelloWorldAfter.lf b/C/src/DistributedHelloWorld/HelloWorldAfter.lf index 64172656..13a8f29a 100644 --- a/C/src/DistributedHelloWorld/HelloWorldAfter.lf +++ b/C/src/DistributedHelloWorld/HelloWorldAfter.lf @@ -1,18 +1,18 @@ /** - * Version of HelloWorld with an `after` on the connection. - * The after value on the connection gives a logical time offset - * between the sender and the receiver. - * + * Version of HelloWorld with an `after` on the connection. The after value on the connection gives + * a logical time offset between the sender and the receiver. + * * @author Edward A. Lee */ target C { timeout: 10 secs -}; +} + import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" federated reactor HelloWorldAfter { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message after 10 msec; + source = new MessageGenerator(prefix = "Hello World") + print = new PrintMessage() + source.message -> print.message after 10 msec } diff --git a/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf b/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf index d0862b9c..93270547 100644 --- a/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf +++ b/C/src/DistributedHelloWorld/HelloWorldDecentralized.lf @@ -1,41 +1,41 @@ /** - * Version of HelloWorld that uses decentralized coordination. - * The `offset` parameter at the top level specifies an `after` delay to - * use on the connection. If this delay is too small, then the `print` - * receiving federate will report tardy messages, which are messages where - * the intended timestamp cannot be assigned because the message arrived - * too late. - * - * If the sender and receiver are running in the same machine - * then there is no clock synchronization error and the communication latency - * should be well less than 10msec, so an `offset` of 10 msec should be plenty - * adequate to avoid any tardy messages. If you change the offset to - * 10 usec, then tardy messages will likely occur, unless, on your machine, - * communication between two processes can reliably occur within 10 microseconds. - * + * Version of HelloWorld that uses decentralized coordination. The `offset` parameter at the top + * level specifies an `after` delay to use on the connection. If this delay is too small, then the + * `print` receiving federate will report tardy messages, which are messages where the intended + * timestamp cannot be assigned because the message arrived too late. + * + * If the sender and receiver are running in the same machine then there is no clock synchronization + * error and the communication latency should be well less than 10msec, so an `offset` of 10 msec + * should be plenty adequate to avoid any tardy messages. If you change the offset to 10 usec, then + * tardy messages will likely occur, unless, on your machine, communication between two processes + * can reliably occur within 10 microseconds. + * * @author Edward A. Lee */ target C { timeout: 10 secs, coordination: decentralized -}; +} + import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" -reactor PrintMessageWithDetector(offset:time(10 msec)) extends PrintMessage { +reactor PrintMessageWithDetector(offset: time = 10 msec) extends PrintMessage { // The timer here creates a worst-case scenario where the receiving // federate has an event to process whose timestamp matches that of an // incoming message. Without this timer, you will not see any tardy // messages because the `print` reactor has no reason to advance its // logical time, and hence any incoming intended tag can be handled. - timer local(offset, 1 sec); - reaction (message) {= + timer local(offset, 1 sec) + + reaction(message) {= // Empty. The base class will react and report the incoming message. =} STP(0) {= lf_print_warning("Message is tardy. Intended tag is (%lld, %u).", message->intended_tag.time - start_time, message->intended_tag.microstep ); =} + reaction(local) {= tag_t tag = lf_tag(); lf_print("Timer triggered at logical tag (%lld, %u).", @@ -44,10 +44,8 @@ reactor PrintMessageWithDetector(offset:time(10 msec)) extends PrintMessage { =} } -federated reactor HelloWorldDecentralized(offset:time(10 msec)) { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessageWithDetector( - offset = offset - ); - source.message -> print.message after offset; +federated reactor HelloWorldDecentralized(offset: time = 10 msec) { + source = new MessageGenerator(prefix = "Hello World") + print = new PrintMessageWithDetector(offset=offset) + source.message -> print.message after offset } diff --git a/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf b/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf index 50729a0b..6c922d08 100644 --- a/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf +++ b/C/src/DistributedHelloWorld/HelloWorldDecentralizedSTP.lf @@ -1,47 +1,38 @@ /** - * Version of HelloWorldDecentralized that uses decentralized - * coordination with an STP (safe to process) offset instead of an `after` - * delay. In this version, the timestamps at the receiving federate `print` - * are the same as the timestamps at the sender `source`. - * - * The `STP_offset` parameter on the `print` federate tells the runtime - * system to wait the amount of time specified before processing events. - * That is, before processing an event with timestamp *t*, the federate - * waits until physical time *T* exceeds *t* + `STP`. - * - * If the `STP_offset` is too small, then the `print` - * receiving federate will report tardy messages, which are messages where - * the intended timestamp cannot be assigned because the message arrived + * Version of HelloWorldDecentralized that uses decentralized coordination with an STP (safe to + * process) offset instead of an `after` delay. In this version, the timestamps at the receiving + * federate `print` are the same as the timestamps at the sender `source`. + * + * The `STP_offset` parameter on the `print` federate tells the runtime system to wait the amount of + * time specified before processing events. That is, before processing an event with timestamp *t*, + * the federate waits until physical time *T* exceeds *t* + `STP`. + * + * If the `STP_offset` is too small, then the `print` receiving federate will report tardy messages, + * which are messages where the intended timestamp cannot be assigned because the message arrived * too late. - * - * If the sender and receiver are running in the same machine - * then there is no clock synchronization error and the communication latency - * should be well less than 10msec, so an `offset` of 10 msec should be plenty - * adequate to avoid any tardy messages. If you change the offset to - * 10 usec, then tardy messages will likely occur, unless, on your machine, - * communication between two processes can reliably occur within 10 microseconds. + * + * If the sender and receiver are running in the same machine then there is no clock synchronization + * error and the communication latency should be well less than 10msec, so an `offset` of 10 msec + * should be plenty adequate to avoid any tardy messages. If you change the offset to 10 usec, then + * tardy messages will likely occur, unless, on your machine, communication between two processes + * can reliably occur within 10 microseconds. * * @author Edward A. Lee */ target C { timeout: 10 secs, coordination: decentralized -}; +} + import MessageGenerator from "HelloWorld.lf" import PrintMessageWithDetector from "HelloWorldDecentralized.lf" -/** - * Subclass that simply adds an `STP` parameter, nothing more. - */ -reactor PrintMessageWithSTP(STP_offset:time(10 msec)) extends PrintMessageWithDetector { - +/** Subclass that simply adds an `STP` parameter, nothing more. */ +reactor PrintMessageWithSTP(STP_offset: time = 10 msec) extends PrintMessageWithDetector { } federated reactor { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessageWithSTP( - offset = 0, - STP_offset = 10 msec - ); - source.message -> print.message; + source = new MessageGenerator(prefix = "Hello World") + print = new PrintMessageWithSTP(offset=0, STP_offset = 10 msec) + source.message -> print.message } diff --git a/C/src/DistributedHelloWorld/HelloWorldPhysical.lf b/C/src/DistributedHelloWorld/HelloWorldPhysical.lf index fc70ce80..2611039c 100644 --- a/C/src/DistributedHelloWorld/HelloWorldPhysical.lf +++ b/C/src/DistributedHelloWorld/HelloWorldPhysical.lf @@ -1,27 +1,25 @@ /** - * Version of HelloWorld that uses a physical connection, - * indicated by ~>. - * - * The timestamp assigned at the receiving end depends on the value - * of the physical clock at the receiving end. The extend to which this - * disagrees with the timestamp at the sending end is a measure of the - * communication time between the processes. - * - * Note that with physical connections, messages go directly from one - * federate to another, not through the RTI as they normally would with - * logical connections and centralized control (the default). - * + * Version of HelloWorld that uses a physical connection, indicated by ~>. + * + * The timestamp assigned at the receiving end depends on the value of the physical clock at the + * receiving end. The extend to which this disagrees with the timestamp at the sending end is a + * measure of the communication time between the processes. + * + * Note that with physical connections, messages go directly from one federate to another, not + * through the RTI as they normally would with logical connections and centralized control (the + * default). + * * @author Edward A. Lee */ target C { timeout: 10 secs -}; +} import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" federated reactor HelloWorldPhysical { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message ~> print.message; + source = new MessageGenerator(prefix = "Hello World") + print = new PrintMessage() + source.message ~> print.message } diff --git a/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf b/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf index 049c3117..6f01ab82 100644 --- a/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf +++ b/C/src/DistributedHelloWorld/HelloWorldPhysicalAfter.lf @@ -1,23 +1,23 @@ /** - * Version of HelloWorldPhysical that uses a physical connection, - * indicated by ~>, with an `after` on the connection. - * - * The after value on the connection gives a logical time offset - * between the sender and the receiver. If the connection were not a physical one, - * indicated with ~>, then this logical time offset would simply be the logical - * time offset obtained. However, since it is a physical one, the offset between - * the timestamps is the the communication latency plus 10 msec. - * + * Version of HelloWorldPhysical that uses a physical connection, indicated by ~>, with an `after` + * on the connection. + * + * The after value on the connection gives a logical time offset between the sender and the + * receiver. If the connection were not a physical one, indicated with ~>, then this logical time + * offset would simply be the logical time offset obtained. However, since it is a physical one, the + * offset between the timestamps is the the communication latency plus 10 msec. + * * @author Edward A. Lee */ target C { timeout: 10 secs -}; +} + import MessageGenerator from "HelloWorld.lf" import PrintMessage from "HelloWorld.lf" federated reactor HelloWorldPhysicalAfter { - source = new MessageGenerator(prefix = "Hello World"); + source = new MessageGenerator(prefix = "Hello World") print = new PrintMessage() at localhost; - source.message ~> print.message after 10 msec; + source.message ~> print.message after 10 msec } diff --git a/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf b/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf index 52e934e5..5067f2af 100644 --- a/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf +++ b/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf @@ -1,22 +1,21 @@ /** - * Containerized distributed LF program where a MessageGenerator creates a string - * message that is sent via the RTI (runtime infrastructure) to a - * receiver that prints the message. - * + * Containerized distributed LF program where a MessageGenerator creates a string message that is + * sent via the RTI (runtime infrastructure) to a receiver that prints the message. + * * For run instructions, see README. - * + * * @author Edward A. Lee */ target C { timeout: 10 secs, docker: true -}; +} import MessageGenerator from "../HelloWorld.lf" import PrintMessage from "../HelloWorld.lf" federated reactor HelloWorldContainerized at rti { - source = new MessageGenerator(prefix = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; + source = new MessageGenerator(prefix = "Hello World") + print = new PrintMessage() + source.message -> print.message } diff --git a/C/src/DistributedResourceManagement/FederatedResourceManagement.lf b/C/src/DistributedResourceManagement/FederatedResourceManagement.lf index 90716394..9ae2dfba 100644 --- a/C/src/DistributedResourceManagement/FederatedResourceManagement.lf +++ b/C/src/DistributedResourceManagement/FederatedResourceManagement.lf @@ -1,34 +1,29 @@ /** - * Demo program illustrating the architecture of a distributed resource management - * problem like that described in: - * - * Lamport, L. (1984). "Using Time Instead of Timeout for Fault-Tolerant Distributed Systems." - * ACM Transactions on Programming Languages and Systems 6(2): 254-280. + * Demo program illustrating the architecture of a distributed resource management problem like that + * described in: + * + * Lamport, L. (1984). "Using Time Instead of Timeout for Fault-Tolerant Distributed Systems." ACM + * Transactions on Programming Languages and Systems 6(2): 254-280. + * + * The goal is distributed first-come, first-served exclusive access to a shared resource. + * + * Each instance of Client is initially idle for a time given by its `idle_time` parameter. It then + * requests exclusive access to the resource by sending a `request` message to its local instance of + * ResourceManager. If the resource is available, then the ResourceManager immediately replies with + * a `grant` message. The Client will then hold the resource for an amount of time given by its + * `use_time` parameter, after which it will send a `release` message to the ResourceManager. If the + * resource is not available when the Client requests it, then the ResourceManager queues the + * request and sends the `grant` message when the resource becomes available. + * + * Each instance of ResourceManager maintains a copy of the queue of pending requests to the + * resource. The entries in the queue are ordered by tag (logical time and microstep) so that they + * will be granted using a first-come, first-served policy. If two requests are made simultaneously + * (with the same tag), then they will be granted access in order of their IDs. + * + * At all logical tags, every instance of ResourceManager should have exactly the same queue + * contents. Hence, at every tag, all ResourceManagers agree on which manager has access to the + * resource. It is the manager at the head of their queue. * - * The goal is distributed first-come, first-served exclusive access to a shared - * resource. - * - * Each instance of Client is initially idle for a time given by its - * `idle_time` parameter. It then requests exclusive access to the resource by - * sending a `request` message to its local instance of ResourceManager. - * If the resource is available, then the ResourceManager immediately replies - * with a `grant` message. The Client will then hold the resource for an - * amount of time given by its `use_time` parameter, after which it will send - * a `release` message to the ResourceManager. If the resource is not available - * when the Client requests it, then the ResourceManager queues the request - * and sends the `grant` message when the resource becomes available. - * - * Each instance of ResourceManager maintains a copy of the queue of pending - * requests to the resource. The entries in the queue are ordered by tag - * (logical time and microstep) so that they will be granted using a first-come, - * first-served policy. If two requests are made simultaneously (with the same - * tag), then they will be granted access in order of their IDs. - * - * At all logical tags, every instance of ResourceManager should have exactly - * the same queue contents. Hence, at every tag, all ResourceManagers agree on - * which manager has access to the resource. It is the manager at the head of - * their queue. - * * @author Edward A. Lee */ target C { @@ -37,24 +32,22 @@ target C { import Platform from "ResourceManagement.lf" -federated reactor FederatedResourceManagement( - num_other_resource_managers:int(1) -) { +federated reactor FederatedResourceManagement(num_other_resource_managers: int = 1) { // Each platform needs a unique id that is between 0 and QUEUE_SIZE-1. a = new Platform( idle_time = 50 msec, use_time = 60 msec, name = "San Francisco", - id = 0, - num_other_resource_managers = num_other_resource_managers); + id=0, + num_other_resource_managers=num_other_resource_managers) b = new Platform( idle_time = 200 msec, use_time = 40 msec, - name = "Berkeley", - id = 1, - num_other_resource_managers = num_other_resource_managers); - b.request -> a.remote_request; - b.release -> a.remote_release; - a.request -> b.remote_request; - a.release -> b.remote_release; + name="Berkeley", + id=1, + num_other_resource_managers=num_other_resource_managers) + b.request -> a.remote_request + b.release -> a.remote_release + a.request -> b.remote_request + a.release -> b.remote_release } diff --git a/C/src/DistributedResourceManagement/ResourceManagement.lf b/C/src/DistributedResourceManagement/ResourceManagement.lf index 47a3335f..6d3acd17 100644 --- a/C/src/DistributedResourceManagement/ResourceManagement.lf +++ b/C/src/DistributedResourceManagement/ResourceManagement.lf @@ -1,34 +1,28 @@ /** - * Demo program illustrating the architecture of a distributed resource - * management problem like that described in: + * Demo program illustrating the architecture of a distributed resource management problem like that + * described in: * - * Lamport, L. (1984). "Using Time Instead of Timeout for Fault-Tolerant - * Distributed Systems." ACM Transactions on Programming Languages and Systems - * 6(2): 254-280. + * Lamport, L. (1984). "Using Time Instead of Timeout for Fault-Tolerant Distributed Systems." ACM + * Transactions on Programming Languages and Systems 6(2): 254-280. * - * The goal is distributed first-come, first-served exclusive access to a shared - * resource. + * The goal is distributed first-come, first-served exclusive access to a shared resource. * - * Each instance of Client is initially idle for a time given by its `idle_time` - * parameter. It then requests exclusive access to the resource by sending a - * `request` message to its local instance of ResourceManager. If the resource - * is available, then the ResourceManager immediately replies with a `grant` - * message. The Client will then hold the resource for an amount of time given - * by its `use_time` parameter, after which it will send a `release` message to - * the ResourceManager. If the resource is not available when the Client - * requests it, then the ResourceManager queues the request and sends the - * `grant` message when the resource becomes available. + * Each instance of Client is initially idle for a time given by its `idle_time` parameter. It then + * requests exclusive access to the resource by sending a `request` message to its local instance of + * ResourceManager. If the resource is available, then the ResourceManager immediately replies with + * a `grant` message. The Client will then hold the resource for an amount of time given by its + * `use_time` parameter, after which it will send a `release` message to the ResourceManager. If the + * resource is not available when the Client requests it, then the ResourceManager queues the + * request and sends the `grant` message when the resource becomes available. * - * Each instance of ResourceManager maintains a copy of the queue of pending - * requests to the resource. The entries in the queue are ordered by tag - * (logical time and microstep) so that they will be granted using a first-come, - * first-served policy. If two requests are made simultaneously (with the same - * tag), then they will be granted access in order of their IDs. + * Each instance of ResourceManager maintains a copy of the queue of pending requests to the + * resource. The entries in the queue are ordered by tag (logical time and microstep) so that they + * will be granted using a first-come, first-served policy. If two requests are made simultaneously + * (with the same tag), then they will be granted access in order of their IDs. * - * At all logical tags, every instance of ResourceManager should have exactly - * the same queue contents. Hence, at every tag, all ResourceManagers agree on - * which manager has access to the resource. It is the manager at the head of - * their queue. + * At all logical tags, every instance of ResourceManager should have exactly the same queue + * contents. Hence, at every tag, all ResourceManagers agree on which manager has access to the + * resource. It is the manager at the head of their queue. * * @author Edward A. Lee */ @@ -50,9 +44,9 @@ preamble {= =} /** - * Mockup for a client that requests for access to the resource. This client - * issues a `request` after `idle_time`. When it receives a `grant`, it waits an - * amount of time `use_time` and then issues a `release` output. + * Mockup for a client that requests for access to the resource. This client issues a `request` + * after `idle_time`. When it receives a `grant`, it waits an amount of time `use_time` and then + * issues a `release` output. * * @param idle_time The time until the next `request`. * @param use_time The time after a `grant` input before a `release` output. @@ -63,11 +57,11 @@ preamble {= * @output update Release the resource. */ reactor Client( - idle_time: time = 150 msec, - use_time: time = 30 msec, - id: int = 0, - name: char* = "unnamed Client" // Used for more visible logging -) { + idle_time: time = 150 msec, + use_time: time = 30 msec, + id: int = 0, + // Used for more visible logging + name: char* = "unnamed Client") { logical action request_trigger(idle_time) logical action release_trigger(use_time) @@ -123,23 +117,20 @@ reactor Client( } /** - * A distributed resource manager that grants exclusive access to a shared - * resource to a local client. A local client requests and releases access to - * the resource through the `local_request` and `local_release` input ports. The - * `request` and `release` output ports must be connected to every other - * instance of this ResourceManager. The `remote_request` and `remote_release` - * input ports come from all other instances of this ResourceManager. + * A distributed resource manager that grants exclusive access to a shared resource to a local + * client. A local client requests and releases access to the resource through the `local_request` + * and `local_release` input ports. The `request` and `release` output ports must be connected to + * every other instance of this ResourceManager. The `remote_request` and `remote_release` input + * ports come from all other instances of this ResourceManager. * * The total number of other resource managers is required to match the - * `num_other_resource_managers` parameter, and each resource manager is - * required to have a unique `id` between 0 and `num_other_resource_managers`. A - * resource manager may also optionally have a `name`, which is used when - * reporting errors. + * `num_other_resource_managers` parameter, and each resource manager is required to have a unique + * `id` between 0 and `num_other_resource_managers`. A resource manager may also optionally have a + * `name`, which is used when reporting errors. * * @param name A name for the database instance (used in reporting). * @param id A unique id between 0 and `num_other_resource_managers`. - * @param num_other_resource_managers The total number of other resource - * managers. + * @param num_other_resource_managers The total number of other resource managers. * * @input local_request A local request for access to the resource. * @input local_release Release the resource held locally. @@ -151,10 +142,9 @@ reactor Client( * @output release Broadcast release to other resource managers. */ reactor ResourceManager( - name: char* = "unnamed ResourceManager", - id: int = 0, - num_other_resource_managers: int = 1 -) { + name: char* = "unnamed ResourceManager", + id: int = 0, + num_other_resource_managers: int = 1) { input local_request: bool input local_release: bool input[num_other_resource_managers] remote_request: int @@ -370,51 +360,40 @@ reactor ResourceManager( } /** - * A mockup of a platform (e.g. a pod, a virtual machine, a physical machine, - * etc.) that hosts a client that needs exclusive access to some resource - * managed by a ResourceManager. + * A mockup of a platform (e.g. a pod, a virtual machine, a physical machine, etc.) that hosts a + * client that needs exclusive access to some resource managed by a ResourceManager. * - * @param idle_time The amount of time the client is idle before requesting the - * resource. + * @param idle_time The amount of time the client is idle before requesting the resource. * @param use_time The amount of time the client holds on to the resource. - * @param num_other_resource_managers The number of other resource managers - * requiring access. + * @param num_other_resource_managers The number of other resource managers requiring access. * @param id A unique ID between 0 and num_other_resource_managers. * @param name A name (for reporting). * - * @input remote_request A multiport input accepting remote requests for the - * resource. - * @input remote_release A multiport input accepting remote releases of the - * resource. + * @input remote_request A multiport input accepting remote requests for the resource. + * @input remote_release A multiport input accepting remote releases of the resource. * * @output request An output to broadcast a local request for the resource. * @output release An output to broadcast a local release of the resource. */ reactor Platform( - idle_time: time = 150 msec, - use_time: time = 30 msec, - num_other_resource_managers: int = 1, - id: int = 0, - name: char* = "unnamed Platform" // Used for more visible logging -) { + idle_time: time = 150 msec, + use_time: time = 30 msec, + num_other_resource_managers: int = 1, + id: int = 0, + // Used for more visible logging + name: char* = "unnamed Platform") { input[num_other_resource_managers] remote_request: int input[num_other_resource_managers] remote_release: int output request: int output release: int - client = new Client( - name = name, - id = id, - idle_time = idle_time, - use_time = use_time - ) + client = new Client(name=name, id=id, idle_time=idle_time, use_time=use_time) manager = new ResourceManager( - name = name, - id = id, - num_other_resource_managers = num_other_resource_managers - ) + name=name, + id=id, + num_other_resource_managers=num_other_resource_managers) client.request -> manager.local_request client.release -> manager.local_release @@ -428,19 +407,17 @@ reactor Platform( main reactor ResourceManagement(num_other_resource_managers: int = 1) { // Each platform needs a unique id that is between 0 and QUEUE_SIZE-1. a = new Platform( - idle_time = 50 msec, - use_time = 60 msec, - name = "San Francisco", - id = 0, - num_other_resource_managers = num_other_resource_managers - ) + idle_time = 50 msec, + use_time = 60 msec, + name = "San Francisco", + id=0, + num_other_resource_managers=num_other_resource_managers) b = new Platform( - idle_time = 200 msec, - use_time = 40 msec, - name = "Berkeley", - id = 1, - num_other_resource_managers = num_other_resource_managers - ) + idle_time = 200 msec, + use_time = 40 msec, + name="Berkeley", + id=1, + num_other_resource_managers=num_other_resource_managers) b.request -> a.remote_request b.release -> a.remote_release a.request -> b.remote_request diff --git a/C/src/Parallelism/ForkJoin.lf b/C/src/Parallelism/ForkJoin.lf index d99d9a82..03c5f296 100644 --- a/C/src/Parallelism/ForkJoin.lf +++ b/C/src/Parallelism/ForkJoin.lf @@ -1,50 +1,54 @@ /** - * Each instance of TakeTime takes 200 ms wall clock time to - * transport the input to the output. Four of them are - * instantiated. Note that without parallel execution, there is - * no way this program can keep up with real time since in every - * 200 msec cycle it has 800 msec of work to do. Given 4 workers, - * however, this program can complete 800 msec of work in about - * 225 msec. + * Each instance of TakeTime takes 200 ms wall clock time to transport the input to the output. Four + * of them are instantiated. Note that without parallel execution, there is no way this program can + * keep up with real time since in every 200 msec cycle it has 800 msec of work to do. Given 4 + * workers, however, this program can complete 800 msec of work in about 225 msec. */ target C { timeout: 2 sec, - workers: 1, // Change to 4 to see speed up. -}; -main reactor(width:int(4)) { - a = new Source(); - t = new[width] TakeTime(); - (a.out)+ -> t.in; - b = new Destination(width = width); - t.out -> b.in; + workers: 1 // Change to 4 to see speed up. } + +main reactor(width: int = 4) { + a = new Source() + t = new[width] TakeTime() + (a.out)+ -> t.in + b = new Destination(width=width) + t.out -> b.in +} + reactor Source { - timer t(0, 200 msec); - output out:int; - state s:int(0); - reaction(t) -> out {= - lf_set(out, self->s); - self->s++; - =} + timer t(0, 200 msec) + output out: int + state s: int = 0 + + reaction(t) -> out {= + lf_set(out, self->s); + self->s++; + =} } + reactor TakeTime { - input in:int; - output out:int; - reaction(in) -> out {= - struct timespec sleep_time = {(time_t) 0, (long)200000000}; - struct timespec remaining_time; - nanosleep(&sleep_time, &remaining_time); - int offset = 0; - for (int i = 0; i < 100000000; i++) { - offset++; - } - lf_set(out, in->value + offset); - =} + input in: int + output out: int + + reaction(in) -> out {= + struct timespec sleep_time = {(time_t) 0, (long)200000000}; + struct timespec remaining_time; + nanosleep(&sleep_time, &remaining_time); + int offset = 0; + for (int i = 0; i < 100000000; i++) { + offset++; + } + lf_set(out, in->value + offset); + =} } -reactor Destination(width:int(4)) { - state s:int(400000000); - input[width] in:int; - reaction(in) {= + +reactor Destination(width: int = 4) { + state s: int = 400000000 + input[width] in: int + + reaction(in) {= int sum = 0; for (int i = 0; i < in_width; i++) { sum += in[i]->value; @@ -55,5 +59,5 @@ reactor Destination(width:int(4)) { exit(1); } self->s += in_width; - =} + =} } diff --git a/C/src/Parallelism/Pipeline.lf b/C/src/Parallelism/Pipeline.lf index 57334d1d..98fd7025 100644 --- a/C/src/Parallelism/Pipeline.lf +++ b/C/src/Parallelism/Pipeline.lf @@ -1,40 +1,33 @@ /** - * Basic pipeline pattern where a periodic source feeds - * a chain of reactors that can all execute in parallel - * at each logical time step. - * - * The workers argument specifies the number of worker - * workers, which enables the reactors in the chain to - * execute on multiple cores simultaneously. - * - * This uses the TakeTime reactor to perform computation. - * If you reduce the number of worker workers to 1, the - * execution time will be approximately four times as long. - * + * Basic pipeline pattern where a periodic source feeds a chain of reactors that can all execute in + * parallel at each logical time step. + * + * The workers argument specifies the number of worker workers, which enables the reactors in the + * chain to execute on multiple cores simultaneously. + * + * This uses the TakeTime reactor to perform computation. If you reduce the number of worker workers + * to 1, the execution time will be approximately four times as long. + * * @author Edward A. Lee * @author Marten Lohstroh */ - target C { - workers: 4, +target C { + workers: 4 } - + /** * Send counting sequence periodically. - * + * * @param offset The starting time. * @param period The period. * @param init The first output. * @param increment The increment between outputs */ -reactor SendCount( - offset:time(0), - period:time(1 sec), - init:int(0), - increment:int(1) -) { - state count:int(init); - output out:int; - timer t(offset, period); +reactor SendCount(offset: time = 0, period: time = 1 sec, init: int = 0, increment: int = 1) { + state count: int = init + output out: int + timer t(offset, period) + reaction(t) -> out {= lf_set(out, self->count); self->count += self->increment; @@ -42,12 +35,12 @@ reactor SendCount( } /** - * Receive an input and report the elapsed logical tag - * and the value of the input. Request stop when the 10th - * value has been received. + * Receive an input and report the elapsed logical tag and the value of the input. Request stop when + * the 10th value has been received. */ reactor Receive { - input in:int; + input in: int + reaction(in) {= lf_print("At elapsed tag (%lld, %d), received %d.", lf_time_logical_elapsed(), lf_tag().microstep, @@ -60,21 +53,19 @@ reactor Receive { } /** - * When triggered, take the specified amount of physical time - * before outputting the value of the trigger. - * - * @param approximate_time The approximate amount of physical - * time to take for each input. - * + * When triggered, take the specified amount of physical time before outputting the value of the + * trigger. + * + * @param approximate_time The approximate amount of physical time to take for each input. + * * @input in A triggering input. - * - * @output out The triggering input. + * + * @output out The triggering input. */ -reactor TakeTime( - approximate_time:time(100 msec) -) { - input in:int; - output out:int; +reactor TakeTime(approximate_time: time = 100 msec) { + input in: int + output out: int + reaction(in) -> out {= instant_t start_time = lf_time_physical(); while (lf_time_physical() < start_time + self->approximate_time) { @@ -85,9 +76,10 @@ reactor TakeTime( } main reactor { - r0 = new SendCount(period = 100 msec); - rp = new[4] TakeTime(approximate_time = 100 msec); - r5 = new Receive(); + r0 = new SendCount(period = 100 msec) + rp = new[4] TakeTime(approximate_time = 100 msec) + r5 = new Receive() // Uncomment the "after" clause to expose parallelism. - r0.out, rp.out -> rp.in, r5.in // after 100 msec; + // after 100 msec; + r0.out, rp.out -> rp.in, r5.in } diff --git a/C/src/ProtocolBuffers/HelloProtocolBuffers.lf b/C/src/ProtocolBuffers/HelloProtocolBuffers.lf index 7ab2b424..66115eaf 100644 --- a/C/src/ProtocolBuffers/HelloProtocolBuffers.lf +++ b/C/src/ProtocolBuffers/HelloProtocolBuffers.lf @@ -1,12 +1,10 @@ /** - * This example demonstrates a very simple use of protocol buffers - * within a reactor. It encodes and decodes a very simple protocol - * buffer definition in hello_string.proto. This reactor is heavily + * This example demonstrates a very simple use of protocol buffers within a reactor. It encodes and + * decodes a very simple protocol buffer definition in hello_string.proto. This reactor is heavily * based on the examples at https: * github.com/protobuf-c/protobuf-c/wiki/Examples. * - * To run this example first install the protocol buffers compiler - * from https: * github.com/protocolbuffers/protobuf. For Mac, it is - * available from homebrew via + * To run this example first install the protocol buffers compiler from https: * + * github.com/protocolbuffers/protobuf. For Mac, it is available from homebrew via * ``` * brew install protobuf * ``` @@ -15,18 +13,17 @@ * brew install protobuf-c * brew install coreutils * ``` - * Building protobuf from source is very slow, so avoid doing that - * if possible. Next install the C plugin for protocol buffers from - * [https://github.com/protobuf-c/protobuf-c]. + * Building protobuf from source is very slow, so avoid doing that if possible. Next install the C + * plugin for protocol buffers from [https://github.com/protobuf-c/protobuf-c]. * - * Navigate to the directory containing the protocol buffer definition - * hello_string.proto. To compile it, run the command: + * Navigate to the directory containing the protocol buffer definition hello_string.proto. To + * compile it, run the command: * ``` * protoc-c --c_out=. hello_string.proto * ``` - * This should generate the files "hello_string.pb-c.c" and "hello_string.pb-c.h". - * Move both files to the src-gen directory where the C code for this - * reactor is generated by Lingua Franca. Compile there with: + * This should generate the files "hello_string.pb-c.c" and "hello_string.pb-c.h". Move both files + * to the src-gen directory where the C code for this reactor is generated by Lingua Franca. Compile + * there with: * ``` * cc HelloProtocolBuffers.c hello_string.pb-c.c -l protobuf-c * ``` @@ -34,37 +31,37 @@ * ``` * ./a.out * ``` - * + + * * @author Matt Weber * @author Edward A. Lee */ - target C { protobufs: hello_string.proto -}; +} main reactor HelloProtocolBuffers { - - timer t; + timer t + reaction(t) {= HelloString pack_msg = HELLO_STRING__INIT; // Macro to create the protocol buffer void *buf; // Buffer to store the serialized data unsigned len; // Length of the packed message - + char* hello_msg = "Hello Protocol Buffers!"; - pack_msg.message = hello_msg; - + pack_msg.message = hello_msg; + //Pack the message into buf. len = hello_string__get_packed_size(&pack_msg); buf = malloc(len); hello_string__pack(&pack_msg,buf); - + //Now unpack the message from buf. HelloString *unpacked_msg; unpacked_msg = hello_string__unpack(NULL, len, buf); - + //Extract and print the unpacked message. printf("Read: %s\n", unpacked_msg->message); - free(buf); // Free the allocated serialized buffer + free(buf); // Free the allocated serialized buffer =} } diff --git a/C/src/ROS/BasicROS.lf b/C/src/ROS/BasicROS.lf index 3a6e406b..15580bf3 100644 --- a/C/src/ROS/BasicROS.lf +++ b/C/src/ROS/BasicROS.lf @@ -1,169 +1,163 @@ -/** - * This is an example of exchanging messages between reactors using ROS2. - * - * There is a MessageGenerator reactor that publishes String messages on 'topic' - * and a MessageReceiver reactor that subscribes to 'topic'. - * - * 1- To get this example working, install full ROS 2 desktop - * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). - * - * Please note that 'colcon' should also be installed. - * See 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. - * - * 2- Follow the instruction in - * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ - * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. - * - * 3- Follow section 2.2 and 2.3 to modify the CMakeLists.txt and package.xml. - * - * 4- Replace the default C++14 standard in CMakeLists.txt (i.e., set(CMAKE_CXX_STANDARD 14)) - * with: - * - * # Default to C++20 - * if(NOT CMAKE_CXX_STANDARD) - * set(CMAKE_CXX_STANDARD 20) - * endif() - * - * 5- Add the following lines to CMakeLists.txt: - * - * add_compile_definitions(NUMBER_OF_WORKERS=4) - * - * add_library(platform_support src/core/platform/lf_linux_support.cpp) - * - * add_executable(talker src/BasicROS.cpp) - * - * target_link_libraries(talker platform_support) - * - * install(TARGETS - * platform_support - * DESTINATION lib - * ) - * - * 6- Use lfc (in bin/) to compile the provided .lf file - * - * lfc BasicROS.lf - * - * 7- Run the provided build-ROS-node.sh: - * - * ./build-ROS-node.sh BasicROS cpp_pubsub - * - * This will create a 'talker' node in the package cpp_pubsub (these names can be changed in - * CMakeLists.txt and in the argument to build-ROS-node.sh). - * - * 7- Source the appropriate setup.bash and run the node: - * - * source cpp_pubsub/install/setup.bash - * ros2 run cpp_pubsub talker - * - */ -target C { - keepalive: true, - logging: DEBUG, - no-compile: true -}; - -preamble {= - #include - #include - #include - #include - - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/string.hpp" -=} - -reactor MessageGenerator { - preamble {= - class MinimalPublisher : public rclcpp::Node { - public: - MinimalPublisher() - : Node("minimal_publisher") - { - publisher_ = this->create_publisher("topic", 10); - } - - rclcpp::Publisher::SharedPtr publisher_; - }; - =} - state minimal_publisher:{=std::shared_ptr=}; - state i:int(0); - timer t(0, 500 msec); - reaction(startup) {= - std::cout << "Executing startup." << std::endl; - char *argv[] = {(char*)"BasicROSPub", NULL}; - rclcpp::init(1, argv); - self->minimal_publisher = std::make_shared(); - =} - reaction(t) {= - auto message = std_msgs::msg::String(); - std::cout << "Executing timer reaction." << std::endl; - message.data = "Hello, world! " + std::to_string(self->i++); - RCLCPP_INFO(self->minimal_publisher->get_logger(), - "Sender publishing: '%s'", message.data.c_str()); - self->minimal_publisher->publisher_->publish(message); - rclcpp::spin_some(self->minimal_publisher); - std::cout << "Done executing timer reaction." << std::endl; - =} - - reaction(shutdown) {= - std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} -} - -reactor MessageReceiver { - preamble {= - class MinimalSubscriber : public rclcpp::Node { - public: - MinimalSubscriber(void* physical_action) - : Node("minimal_subscriber"), physical_action_(physical_action) { - subscription_ = this->create_subscription( - "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); - } - - private: - void topic_callback(const std_msgs::msg::String::SharedPtr msg) const { - char* writable_string = (char*)malloc(msg->data.length() + 1); - strcpy(writable_string, msg->data.c_str()); - // writable_string[msg->data.length()] = '\0'; // Terminate with 0 - RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str()); - std::cout << "At tag (" << lf_time_logical_elapsed() << "," - << lf_tag().microstep << ") calling schedule_value with value " - << writable_string << " and length " << msg->data.length() - << "." << std::endl; - lf_schedule_copy(physical_action_, 0, &(writable_string), msg->data.length() + 1); - // std::cout << "Done calling schedule_value." << std::endl; - } - rclcpp::Subscription::SharedPtr subscription_; - void* physical_action_; - }; - =} - physical action ros_message_a:string; - state minimal_subscriber:{=std::shared_ptr=}; - reaction(startup) -> ros_message_a {= - // std::cout << "Executing startup." << std::endl; - self->minimal_subscriber = std::make_shared(ros_message_a); - =} - - reaction(ros_message_a){= - std::cout << "Physical action triggered." << std::endl; - printf("Received: %s.\n", ros_message_a->value); - =} - - - timer t(0, 500 msec); - reaction(t) {= - rclcpp::spin_some(self->minimal_subscriber); - // std::cout << "Timer triggered." << std::endl; - =} - - reaction(shutdown) {= - // std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} -} - -main reactor { - sender = new MessageGenerator(); - receiver = new MessageReceiver(); -} +/** + * This is an example of exchanging messages between reactors using ROS2. + * + * There is a MessageGenerator reactor that publishes String messages on 'topic' and a + * MessageReceiver reactor that subscribes to 'topic'. + * + * 1- To get this example working, install full ROS 2 desktop + * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). + * + * Please note that 'colcon' should also be installed. See + * 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. + * + * 2- Follow the instruction in + * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ + * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. + * + * 3- Follow section 2.2 and 2.3 to modify the CMakeLists.txt and package.xml. + * + * 4- Replace the default C++14 standard in CMakeLists.txt (i.e., set(CMAKE_CXX_STANDARD 14)) with: + * + * # Default to C++20 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 20) endif() + * + * 5- Add the following lines to CMakeLists.txt: + * + * add_compile_definitions(NUMBER_OF_WORKERS=4) + * + * add_library(platform_support src/core/platform/lf_linux_support.cpp) + * + * add_executable(talker src/BasicROS.cpp) + * + * target_link_libraries(talker platform_support) + * + * install(TARGETS platform_support DESTINATION lib ) + * + * 6- Use lfc (in bin/) to compile the provided .lf file + * + * lfc BasicROS.lf + * + * 7- Run the provided build-ROS-node.sh: + * + * ./build-ROS-node.sh BasicROS cpp_pubsub + * + * This will create a 'talker' node in the package cpp_pubsub (these names can be changed in + * CMakeLists.txt and in the argument to build-ROS-node.sh). + * + * 7- Source the appropriate setup.bash and run the node: + * + * source cpp_pubsub/install/setup.bash ros2 run cpp_pubsub talker + */ +target C { + keepalive: true, + logging: DEBUG, + no-compile: true +} + +preamble {= + #include + #include + #include + #include + + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/string.hpp" +=} + +reactor MessageGenerator { + preamble {= + class MinimalPublisher : public rclcpp::Node { + public: + MinimalPublisher() + : Node("minimal_publisher") + { + publisher_ = this->create_publisher("topic", 10); + } + + rclcpp::Publisher::SharedPtr publisher_; + }; + =} + state minimal_publisher: {= std::shared_ptr =} + state i: int = 0 + timer t(0, 500 msec) + + reaction(startup) {= + std::cout << "Executing startup." << std::endl; + char *argv[] = {(char*)"BasicROSPub", NULL}; + rclcpp::init(1, argv); + self->minimal_publisher = std::make_shared(); + =} + + reaction(t) {= + auto message = std_msgs::msg::String(); + std::cout << "Executing timer reaction." << std::endl; + message.data = "Hello, world! " + std::to_string(self->i++); + RCLCPP_INFO(self->minimal_publisher->get_logger(), + "Sender publishing: '%s'", message.data.c_str()); + self->minimal_publisher->publisher_->publish(message); + rclcpp::spin_some(self->minimal_publisher); + std::cout << "Done executing timer reaction." << std::endl; + =} + + reaction(shutdown) {= + std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} +} + +reactor MessageReceiver { + preamble {= + class MinimalSubscriber : public rclcpp::Node { + public: + MinimalSubscriber(void* physical_action) + : Node("minimal_subscriber"), physical_action_(physical_action) { + subscription_ = this->create_subscription( + "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); + } + + private: + void topic_callback(const std_msgs::msg::String::SharedPtr msg) const { + char* writable_string = (char*)malloc(msg->data.length() + 1); + strcpy(writable_string, msg->data.c_str()); + // writable_string[msg->data.length()] = '\0'; // Terminate with 0 + RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str()); + std::cout << "At tag (" << lf_time_logical_elapsed() << "," + << lf_tag().microstep << ") calling schedule_value with value " + << writable_string << " and length " << msg->data.length() + << "." << std::endl; + lf_schedule_copy(physical_action_, 0, &(writable_string), msg->data.length() + 1); + // std::cout << "Done calling schedule_value." << std::endl; + } + rclcpp::Subscription::SharedPtr subscription_; + void* physical_action_; + }; + =} + physical action ros_message_a: string + state minimal_subscriber: {= std::shared_ptr =} + + timer t(0, 500 msec) + + reaction(startup) -> ros_message_a {= + // std::cout << "Executing startup." << std::endl; + self->minimal_subscriber = std::make_shared(ros_message_a); + =} + + reaction(ros_message_a) {= + std::cout << "Physical action triggered." << std::endl; + printf("Received: %s.\n", ros_message_a->value); + =} + + reaction(t) {= + rclcpp::spin_some(self->minimal_subscriber); + // std::cout << "Timer triggered." << std::endl; + =} + + reaction(shutdown) {= + // std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} +} + +main reactor { + sender = new MessageGenerator() + receiver = new MessageReceiver() +} diff --git a/C/src/ROS/PTIDES-ROS.lf b/C/src/ROS/PTIDES-ROS.lf index 1ff6426d..b8f4011e 100644 --- a/C/src/ROS/PTIDES-ROS.lf +++ b/C/src/ROS/PTIDES-ROS.lf @@ -1,175 +1,169 @@ -/** - * This is an example of exchanging messages between reactors using ROS2. - * This version uses a 25 msec delay to demonstrate the usage of PTIDES - * to conserve message timestamp sent over the ROS2 network. - * - * There is a MessageGenerator reactor that publishes String messages on 'topic' - * and a MessageReceiver reactor that subscribes to 'topic'. - * - * 1- To get this example working, install full ROS 2 desktop - * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). - * - * Please note that 'colcon' should also be installed. - * See 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. - * - * 2- Follow the instruction in - * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ - * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. - * - * 3- Follow section 2.2 and 2.3 to modify the CMakeLists.txt and package.xml. - * - * 4- Replace the default C++14 standard in CMakeLists.txt (i.e., set(CMAKE_CXX_STANDARD 14)) - * with: - * - * # Default to C++20 - * if(NOT CMAKE_CXX_STANDARD) - * set(CMAKE_CXX_STANDARD 20) - * endif() - * - * 5- Add the following lines to CMakeLists.txt: - * - * add_compile_definitions(NUMBER_OF_WORKERS=4) - * - * add_library(platform_support src/core/platform/lf_linux_support.cpp) - * - * add_executable(talker src/PTIDES-ROS.cpp) - * - * target_link_libraries(talker platform_support) - * - * install(TARGETS - * platform_support - * DESTINATION lib - * ) - * - * 6- Use lfc (in bin/) to compile the provided .lf file - * - * lfc PTIDES-ROS.lf - * - * 7- Run the provided build-ROS-node.sh: - * - * ./build-ROS-node.sh PTIDES-ROS cpp_pubsub - * - * This will create a 'talker' node in the package cpp_pubsub (these names can be changed in - * CMakeLists.txt and in the argument to build-ROS-node.sh). - * - * 7- Source the appropriate setup.bash and run the node: - * - * source cpp_pubsub/install/setup.bash - * ros2 run cpp_pubsub talker - * - */ -target C { - keepalive: true, - no-compile: true -}; - -preamble {= - #include - #include - #include - #include - - #include "rclcpp/rclcpp.hpp" - #include "std_msgs/msg/int64.hpp" -=} - -reactor MessageGenerator { - preamble {= - class MinimalPublisher : public rclcpp::Node { - public: - MinimalPublisher() - : Node("minimal_publisher") - { - publisher_ = this->create_publisher("topic", 10); - } - - rclcpp::Publisher::SharedPtr publisher_; - }; - =} - state minimal_publisher:{=std::shared_ptr=}; - state i:int(0); - timer t(0, 500 msec); - reaction(startup) {= - // std::cout << "Executing startup." << std::endl; - char *argv[] = {(char*)"BasicROSPub", NULL}; - rclcpp::init(1, argv); - self->minimal_publisher = std::make_shared(); - =} - reaction(t) {= - auto message = std_msgs::msg::Int64(); - // std::cout << "Executing timer reaction." << std::endl; - message.data = lf_time_logical() + MSEC(25); // Add a 25 msec delay - // RCLCPP_INFO(self->minimal_publisher->get_logger(), - // "Sender publishing: '%lld'", message.data); - self->minimal_publisher->publisher_->publish(message); - rclcpp::spin_some(self->minimal_publisher); - // std::cout << "Done executing timer reaction." << std::endl; - =} - - reaction(shutdown) {= - std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} -} - -reactor MessageReceiver { - preamble {= - class MinimalSubscriber : public rclcpp::Node { - public: - MinimalSubscriber(void* physical_action) - : Node("minimal_subscriber"), physical_action_(physical_action) { - subscription_ = this->create_subscription( - "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); - } - - private: - void topic_callback(const std_msgs::msg::Int64::SharedPtr msg) const { - // writable_string[msg->data.length()] = '\0'; // Terminate with 0 - // RCLCPP_INFO(this->get_logger(), "I heard: '%lld'", msg->data); - // std::cout << "At tag (" << lf_time_logical_elapsed() << "," - // << lf_tag().microstep << ") calling schedule_copy with value " - // << msg->data << "." << std::endl; - lf_schedule_copy(physical_action_, 0, &(msg->data), sizeof(instant_t)); - // std::cout << "Done calling schedule_value." << std::endl; - } - rclcpp::Subscription::SharedPtr subscription_; - void* physical_action_; - }; - =} - physical action ros_message_a:instant_t; - logical action ros_message_l; - state minimal_subscriber:{=std::shared_ptr=}; - reaction(startup) -> ros_message_a {= - // std::cout << "Executing startup." << std::endl; - self->minimal_subscriber = std::make_shared(ros_message_a); - =} - - reaction(ros_message_a) -> ros_message_l {= - // std::cout << "Physical action triggered." << std::endl; - // printf("Received: %lld.\n", ros_message_a->value); - lf_schedule(ros_message_l, ros_message_a->value - lf_time_logical()); - =} - - reaction(ros_message_l) {= - printf("PTIDES reaction called at tag (%lld, %u).\n", - lf_time_logical_elapsed(), - lf_tag().microstep); - =} - - - timer t(0, 5 msec); - reaction(t) {= - rclcpp::spin_some(self->minimal_subscriber); - // std::cout << "Timer triggered." << std::endl; - =} - - reaction(shutdown) {= - // std::cout << "Executing shutdown reaction." << std::endl; - rclcpp::shutdown(); - =} -} - -main reactor { - sender = new MessageGenerator(); - receiver = new MessageReceiver(); -} +/** + * This is an example of exchanging messages between reactors using ROS2. This version uses a 25 + * msec delay to demonstrate the usage of PTIDES to conserve message timestamp sent over the ROS2 + * network. + * + * There is a MessageGenerator reactor that publishes String messages on 'topic' and a + * MessageReceiver reactor that subscribes to 'topic'. + * + * 1- To get this example working, install full ROS 2 desktop + * ('https://index.ros.org/doc/ros2/Installation/Foxy/'). + * + * Please note that 'colcon' should also be installed. See + * 'https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/' for more details. + * + * 2- Follow the instruction in + * https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/ + * **section 1** to create a 'cpp_pubsub' package in the current (example/ROS) folder. + * + * 3- Follow section 2.2 and 2.3 to modify the CMakeLists.txt and package.xml. + * + * 4- Replace the default C++14 standard in CMakeLists.txt (i.e., set(CMAKE_CXX_STANDARD 14)) with: + * + * # Default to C++20 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 20) endif() + * + * 5- Add the following lines to CMakeLists.txt: + * + * add_compile_definitions(NUMBER_OF_WORKERS=4) + * + * add_library(platform_support src/core/platform/lf_linux_support.cpp) + * + * add_executable(talker src/PTIDES-ROS.cpp) + * + * target_link_libraries(talker platform_support) + * + * install(TARGETS platform_support DESTINATION lib ) + * + * 6- Use lfc (in bin/) to compile the provided .lf file + * + * lfc PTIDES-ROS.lf + * + * 7- Run the provided build-ROS-node.sh: + * + * ./build-ROS-node.sh PTIDES-ROS cpp_pubsub + * + * This will create a 'talker' node in the package cpp_pubsub (these names can be changed in + * CMakeLists.txt and in the argument to build-ROS-node.sh). + * + * 7- Source the appropriate setup.bash and run the node: + * + * source cpp_pubsub/install/setup.bash ros2 run cpp_pubsub talker + */ +target C { + keepalive: true, + no-compile: true +} + +preamble {= + #include + #include + #include + #include + + #include "rclcpp/rclcpp.hpp" + #include "std_msgs/msg/int64.hpp" +=} + +reactor MessageGenerator { + preamble {= + class MinimalPublisher : public rclcpp::Node { + public: + MinimalPublisher() + : Node("minimal_publisher") + { + publisher_ = this->create_publisher("topic", 10); + } + + rclcpp::Publisher::SharedPtr publisher_; + }; + =} + state minimal_publisher: {= std::shared_ptr =} + state i: int = 0 + timer t(0, 500 msec) + + reaction(startup) {= + // std::cout << "Executing startup." << std::endl; + char *argv[] = {(char*)"BasicROSPub", NULL}; + rclcpp::init(1, argv); + self->minimal_publisher = std::make_shared(); + =} + + reaction(t) {= + auto message = std_msgs::msg::Int64(); + // std::cout << "Executing timer reaction." << std::endl; + message.data = lf_time_logical() + MSEC(25); // Add a 25 msec delay + // RCLCPP_INFO(self->minimal_publisher->get_logger(), + // "Sender publishing: '%lld'", message.data); + self->minimal_publisher->publisher_->publish(message); + rclcpp::spin_some(self->minimal_publisher); + // std::cout << "Done executing timer reaction." << std::endl; + =} + + reaction(shutdown) {= + std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} +} + +reactor MessageReceiver { + preamble {= + class MinimalSubscriber : public rclcpp::Node { + public: + MinimalSubscriber(void* physical_action) + : Node("minimal_subscriber"), physical_action_(physical_action) { + subscription_ = this->create_subscription( + "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, std::placeholders::_1)); + } + + private: + void topic_callback(const std_msgs::msg::Int64::SharedPtr msg) const { + // writable_string[msg->data.length()] = '\0'; // Terminate with 0 + // RCLCPP_INFO(this->get_logger(), "I heard: '%lld'", msg->data); + // std::cout << "At tag (" << lf_time_logical_elapsed() << "," + // << lf_tag().microstep << ") calling schedule_copy with value " + // << msg->data << "." << std::endl; + lf_schedule_copy(physical_action_, 0, &(msg->data), sizeof(instant_t)); + // std::cout << "Done calling schedule_value." << std::endl; + } + rclcpp::Subscription::SharedPtr subscription_; + void* physical_action_; + }; + =} + physical action ros_message_a: instant_t + logical action ros_message_l + state minimal_subscriber: {= std::shared_ptr =} + + timer t(0, 5 msec) + + reaction(startup) -> ros_message_a {= + // std::cout << "Executing startup." << std::endl; + self->minimal_subscriber = std::make_shared(ros_message_a); + =} + + reaction(ros_message_a) -> ros_message_l {= + // std::cout << "Physical action triggered." << std::endl; + // printf("Received: %lld.\n", ros_message_a->value); + lf_schedule(ros_message_l, ros_message_a->value - lf_time_logical()); + =} + + reaction(ros_message_l) {= + printf("PTIDES reaction called at tag (%lld, %u).\n", + lf_time_logical_elapsed(), + lf_tag().microstep); + =} + + reaction(t) {= + rclcpp::spin_some(self->minimal_subscriber); + // std::cout << "Timer triggered." << std::endl; + =} + + reaction(shutdown) {= + // std::cout << "Executing shutdown reaction." << std::endl; + rclcpp::shutdown(); + =} +} + +main reactor { + sender = new MessageGenerator() + receiver = new MessageReceiver() +} diff --git a/C/src/ReflexGame/ReflexGame.lf b/C/src/ReflexGame/ReflexGame.lf index 258cf510..2746f114 100644 --- a/C/src/ReflexGame/ReflexGame.lf +++ b/C/src/ReflexGame/ReflexGame.lf @@ -1,12 +1,10 @@ /** - * This example illustrates the use of logical and physical actions, - * asynchronous external inputs, the use of startup and shutdown reactions, and - * the use of actions with values. + * This example illustrates the use of logical and physical actions, asynchronous external inputs, + * the use of startup and shutdown reactions, and the use of actions with values. * - * The example is fashioned after an Esterel implementation given by Berry and - * Gonthier in "The ESTEREL synchronous programming language: design, semantics, - * implementation," Science of Computer Programming, 19(2) pp. 87-152, Nov. - * 1992, DOI: 10.1016/0167-6423(92)90005-V. + * The example is fashioned after an Esterel implementation given by Berry and Gonthier in "The + * ESTEREL synchronous programming language: design, semantics, implementation," Science of Computer + * Programming, 19(2) pp. 87-152, Nov. 1992, DOI: 10.1016/0167-6423(92)90005-V. * * @author Edward A. Lee * @author Marten Lohstroh @@ -20,8 +18,8 @@ preamble {= =} /** - * Produce a counting sequence at random times with a minimum and maximum time - * between outputs specified as parameters. + * Produce a counting sequence at random times with a minimum and maximum time between outputs + * specified as parameters. * * @param min_time The minimum time between outputs. * @param max_time The maximum time between outputs. @@ -67,9 +65,9 @@ reactor RandomSource(min_time: time = 2 sec, max_time: time = 8 sec) { } /** - * Upon receiving a prompt, record the time of the prompt, then listen for user - * input. When the user hits return, then schedule a physical action that - * records the time of this event and then report the response time. + * Upon receiving a prompt, record the time of the prompt, then listen for user input. When the user + * hits return, then schedule a physical action that records the time of this event and then report + * the response time. */ reactor GetUserInput { preamble {= diff --git a/C/src/ReflexGame/ReflexGameTest.lf b/C/src/ReflexGame/ReflexGameTest.lf index 4f596b31..8621be15 100644 --- a/C/src/ReflexGame/ReflexGameTest.lf +++ b/C/src/ReflexGame/ReflexGameTest.lf @@ -1,12 +1,11 @@ /** - * The Lingua Franca (LF) program above is designed to read characters from user - * input and display them along with the elapsed time since the last input. The - * program starts a separate thread to read characters from the keyboard, and it - * sends the input characters as valued physical actions to the main reactor. - * Upon receiving a character, the program calculates the time elapsed since the - * last input and prints the character along with the elapsed time in - * nanoseconds. If the received character is an end-of-file (EOF) signal (e.g., - * Control-D or Control-Z), the program stops execution. + * The Lingua Franca (LF) program above is designed to read characters from user input and display + * them along with the elapsed time since the last input. The program starts a separate thread to + * read characters from the keyboard, and it sends the input characters as valued physical actions + * to the main reactor. Upon receiving a character, the program calculates the time elapsed since + * the last input and prints the character along with the elapsed time in nanoseconds. If the + * received character is an end-of-file (EOF) signal (e.g., Control-D or Control-Z), the program + * stops execution. */ target C { keepalive: true, diff --git a/C/src/RockPaperScissors.lf b/C/src/RockPaperScissors.lf index afb91fd6..fce8375b 100644 --- a/C/src/RockPaperScissors.lf +++ b/C/src/RockPaperScissors.lf @@ -1,9 +1,8 @@ /** * Demonstration of the classic rock-paper-scissors game. See - * [https://en.wikipedia.org/wiki/Rock_paper_scissors]. This illustrates - * interactive feedback in Lingua Franca and the importance of reaction order. - * If you reorder the second and third reactions in the Player reactor, a - * causality loop emerges. + * [https://en.wikipedia.org/wiki/Rock_paper_scissors]. This illustrates interactive feedback in + * Lingua Franca and the importance of reaction order. If you reorder the second and third reactions + * in the Player reactor, a causality loop emerges. * * @author Marten Lohstroh */ @@ -12,8 +11,8 @@ target C preamble {= typedef enum {paper=0, rock=1, scissors=2} symbol_t; =} main reactor RockPaperScissors { - player1 = new Player(id = 1) - player2 = new Player(id = 2) + player1 = new Player(id=1) + player2 = new Player(id=2) player1.reveal -> player2.observe player2.reveal -> player1.observe diff --git a/C/src/SleepingBarber.lf b/C/src/SleepingBarber.lf index a3012928..b1d211b4 100644 --- a/C/src/SleepingBarber.lf +++ b/C/src/SleepingBarber.lf @@ -1,42 +1,35 @@ /** - * This example illustrates the classic problem in concurrent - * computing called the "sleeping barber problem," often attributed - * to Edsger Dijkstra (see https://en.wikipedia.org/wiki/Sleeping_barber_problem). - * Using the logical times of Lingua Franca, many of the potential - * problems that can arise do not arise. - * - * Upon startup, the barber goes to sleep and is woken up by - * the arrival of a customer. After serving each customer, - * the barber checks the waiting room for the next customer. - * If there is no customer waiting, the barber goes back - * to sleep. Customers arrive independently from each other - * at random times. If the barber is sleeping, the customer - * gets served right away. Otherwise, if there is room in the - * waiting room, the customer waits. If the waiting room is - * full, the customer goes away and returns a random amount - * of time later. Execution ends when all customers have been + * This example illustrates the classic problem in concurrent computing called the "sleeping barber + * problem," often attributed to Edsger Dijkstra (see + * https://en.wikipedia.org/wiki/Sleeping_barber_problem). Using the logical times of Lingua Franca, + * many of the potential problems that can arise do not arise. + * + * Upon startup, the barber goes to sleep and is woken up by the arrival of a customer. After + * serving each customer, the barber checks the waiting room for the next customer. If there is no + * customer waiting, the barber goes back to sleep. Customers arrive independently from each other + * at random times. If the barber is sleeping, the customer gets served right away. Otherwise, if + * there is room in the waiting room, the customer waits. If the waiting room is full, the customer + * goes away and returns a random amount of time later. Execution ends when all customers have been * served. - * - * This example is inspired by Savina benchmark by the same name, - * which has a C target implementation here: + * + * This example is inspired by Savina benchmark by the same name, which has a C target + * implementation here: * [https://github.com/lf-lang/lingua-franca/blob/master/benchmark/C/Savina/src/concurrency/SleepingBarber.lf]. - * Unlike the benchmark, this example is a direct implementation - * of the problem at hand using the features of Lingua Franca. - * It resembles the Savina benchmark only in overall structure. - * + * Unlike the benchmark, this example is a direct implementation of the problem at hand using the + * features of Lingua Franca. It resembles the Savina benchmark only in overall structure. + * * @author Christian Menard * @author Hannes Klein * @author Matthew Chorlian * @author Edward A. Lee * @author Thee Ho */ - target C { fast: true, threading: false, cmake-include: "/lib/c/reactor-c/util/deque.cmake", files: ["/lib/c/reactor-c/util/deque.h", "/lib/c/reactor-c/util/deque.c"] -}; +} preamble {= #include "deque.h" @@ -50,25 +43,19 @@ preamble {= =} /** - * Upon startup or the triggering of the `next` action, - * produce the send_customer output, then schedule another occurrence of the - * action `next` a random amount of time later. - * The first customer will be produced at logical time 0. - * The time between new customers is uniformly between 0 and - * `max_time_between_customers`. - * - * When a `customer_returned` input arrives, schedule a - * resending of the same customer a random amount of time later, - * where the random number is again uniformly distributed between + * Upon startup or the triggering of the `next` action, produce the send_customer output, then + * schedule another occurrence of the action `next` a random amount of time later. The first + * customer will be produced at logical time 0. The time between new customers is uniformly between * 0 and `max_time_between_customers`. - * - * This reactor uses the customer_done input to count customers and request - * halting the program when all customers have been served. + * + * When a `customer_returned` input arrives, schedule a resending of the same customer a random + * amount of time later, where the random number is again uniformly distributed between 0 and + * `max_time_between_customers`. + * + * This reactor uses the customer_done input to count customers and request halting the program when + * all customers have been served. */ -reactor CustomerFactory( - num_customers:size_t(20), - max_time_between_customers:time(10 minutes) -) { +reactor CustomerFactory(num_customers: size_t = 20, max_time_between_customers: time = 10 minutes) { preamble {= /* Note that this function is shared with Barber reactor. */ interval_t random_time(interval_t max) { @@ -76,22 +63,22 @@ reactor CustomerFactory( return result; } =} - output send_customer: size_t; - input[num_customers] customer_done: bool; - input[num_customers] customer_returned: bool; + output send_customer: size_t + input[num_customers] customer_done: bool + input[num_customers] customer_returned: bool - logical action next; // Next new customer. - logical action again:int; // Returning customer. Payload is customer ID. + logical action next // Next new customer. + logical action again: int // Returning customer. Payload is customer ID. - state done_customers: size_t(0); // Count of finished customers. - state attempts: size_t(0); // Count of customer attempts. - state next_customer_id: size_t(0); // ID of next new customer. + state done_customers: size_t = 0 // Count of finished customers. + state attempts: size_t = 0 // Count of customer attempts. + state next_customer_id: size_t = 0 // ID of next new customer. reaction(startup, next) -> send_customer, next {= // send the new customer to the waiting room self->attempts++; lf_set(send_customer, self->next_customer_id++); - + if (self->next_customer_id < self->num_customers) { // Schedule again. interval_t delay = random_time(self->max_time_between_customers); @@ -99,13 +86,13 @@ reactor CustomerFactory( } =} - reaction (again) -> send_customer {= + reaction(again) -> send_customer {= size_t customer_id = again->value; self->attempts++; lf_set(send_customer, customer_id); =} - reaction (customer_returned) -> again {= + reaction(customer_returned) -> again {= for (int i = 0; i < self->num_customers; i++) { if (customer_returned[i]->is_present) { // The customer returned because the waiting room is full. @@ -116,7 +103,7 @@ reactor CustomerFactory( } =} - reaction (customer_done) {= + reaction(customer_done) {= // Only one customer can be done at any logical tag, so we // only need to count invocations of this reaction. self->done_customers++; @@ -125,7 +112,7 @@ reactor CustomerFactory( } =} - reaction (shutdown) {= + reaction(shutdown) {= char buffer[LF_TIME_BUFFER_LENGTH]; lf_readable_time(buffer, lf_time_logical_elapsed()); lf_print("Finished: %zu customers got haircuts in %zu attempts over %s.", @@ -135,31 +122,28 @@ reactor CustomerFactory( } /** - * When a customer_enters input arrives, if the barber is asleep, - * then wake the barber up and send the customer ID to the - * `barber_leaves_with_customer` output. If the barber is - * awake and there is room in the queue, - * then put the customer ID on the queue and produce a wait output - * on the channel corresponding to the customer ID. If the queue - * is full, then produce a full output on the channel corresponding - * to the customer ID. + * When a customer_enters input arrives, if the barber is asleep, then wake the barber up and send + * the customer ID to the `barber_leaves_with_customer` output. If the barber is awake and there is + * room in the queue, then put the customer ID on the queue and produce a wait output on the channel + * corresponding to the customer ID. If the queue is full, then produce a full output on the channel + * corresponding to the customer ID. */ -reactor WaitingRoom(capacity:size_t(1000), num_customers:size_t(2000)) { +reactor WaitingRoom(capacity: size_t = 1000, num_customers: size_t = 2000) { preamble {= #include "deque.h" =} - input customer_enters: size_t; + input customer_enters: size_t - output[num_customers] full: bool; - output[num_customers] wait: bool; + output[num_customers] full: bool + output[num_customers] wait: bool - input barber_arrives: bool; - output barber_leaves_with_customer: int; + input barber_arrives: bool + output barber_leaves_with_customer: int - state queue: deque_t; - state barber_asleep: bool(true); + state queue: deque_t + state barber_asleep: bool = true - reaction (customer_enters) -> full, wait, barber_leaves_with_customer {= + reaction(customer_enters) -> full, wait, barber_leaves_with_customer {= size_t customer_id = customer_enters->value; if (deque_size(&self->queue) == self->capacity) { @@ -180,7 +164,7 @@ reactor WaitingRoom(capacity:size_t(1000), num_customers:size_t(2000)) { } =} - reaction (barber_arrives) -> barber_leaves_with_customer {= + reaction(barber_arrives) -> barber_leaves_with_customer {= if (deque_is_empty(&self->queue)) { self->barber_asleep = true; } else { @@ -190,21 +174,20 @@ reactor WaitingRoom(capacity:size_t(1000), num_customers:size_t(2000)) { } /** - * Reactor representing a customer. - * This reactor reports on what is happening from the perspective of - * a customer. It also relays a `room_full` input to its `returned` - * output and its `done_cutting` input to its `done` output. + * Reactor representing a customer. This reactor reports on what is happening from the perspective + * of a customer. It also relays a `room_full` input to its `returned` output and its `done_cutting` + * input to its `done` output. */ -reactor Customer(bank_index:size_t(0)) { - input room_full: bool; - input wait: bool; - input start_cutting: bool; - input done_cutting: bool; +reactor Customer(bank_index: size_t = 0) { + input room_full: bool + input wait: bool + input start_cutting: bool + input done_cutting: bool - output returned: bool; - output done: bool; + output returned: bool + output done: bool - reaction (room_full) -> returned {= + reaction(room_full) -> returned {= char buffer[LF_TIME_BUFFER_LENGTH]; lf_readable_time(buffer, lf_time_logical_elapsed()); lf_print("Customer %zu: Turned away at %s. Will try later.", @@ -213,7 +196,7 @@ reactor Customer(bank_index:size_t(0)) { lf_set(returned, true); =} - reaction (wait) {= + reaction(wait) {= char buffer[LF_TIME_BUFFER_LENGTH]; lf_readable_time(buffer, lf_time_logical_elapsed()); lf_print("Customer %zu: Entered waiting room at %s. Waiting.", @@ -221,15 +204,15 @@ reactor Customer(bank_index:size_t(0)) { ); =} - reaction (start_cutting) {= + reaction(start_cutting) {= char buffer[LF_TIME_BUFFER_LENGTH]; lf_readable_time(buffer, lf_time_logical_elapsed()); lf_print("Customer %zu: Started a haircut at %s.", self->bank_index, buffer ); =} - - reaction (done_cutting) -> done {= + + reaction(done_cutting) -> done {= char buffer[LF_TIME_BUFFER_LENGTH]; lf_readable_time(buffer, lf_time_logical_elapsed()); lf_print("Customer %zu: Finished a haircut at %s.", @@ -240,39 +223,36 @@ reactor Customer(bank_index:size_t(0)) { } /** - * Upon receiving an `enter` input (whose value is a customer ID), - * send a `start_cutting` output to corresponding customer, then - * then schedule the logical action `done` to trigger a random - * amount of time later. Upon triggering the `done` action, send a - * `done_cutting` output to the corresponding customer and a - * `next` output to ask for the next customer. - * - * The random time the haircut takes is uniformly distributed between - * `min_cut_time` and `max_cut_time`. + * Upon receiving an `enter` input (whose value is a customer ID), send a `start_cutting` output to + * corresponding customer, then then schedule the logical action `done` to trigger a random amount + * of time later. Upon triggering the `done` action, send a `done_cutting` output to the + * corresponding customer and a `next` output to ask for the next customer. + * + * The random time the haircut takes is uniformly distributed between `min_cut_time` and + * `max_cut_time`. */ reactor Barber( - min_cut_time:time(5 minutes), - max_cut_time:time(15 minutes), - num_customers:size_t(20) -) { - input enter: int; - - output[num_customers] start_cutting: bool; - output[num_customers] done_cutting: bool; - output next: bool; - - logical action done: int; - - reaction (done) -> done_cutting, next {= + min_cut_time: time = 5 minutes, + max_cut_time: time = 15 minutes, + num_customers: size_t = 20) { + input enter: int + + output[num_customers] start_cutting: bool + output[num_customers] done_cutting: bool + output next: bool + + logical action done: int + + reaction(done) -> done_cutting, next {= int customer_id = done->value; lf_set(done_cutting[customer_id], true); lf_set(next, true); =} - reaction (enter) -> start_cutting, done {= + reaction(enter) -> start_cutting, done {= int customer_id = enter->value; lf_set(start_cutting[customer_id], true); - + // Calculate a random delay. interval_t delay = self->min_cut_time + random_time(self->max_cut_time - self->min_cut_time); @@ -282,39 +262,31 @@ reactor Barber( =} } - -main reactor ( - waiting_room_capacity:size_t(7), - max_time_between_customers:time(10 minutes), - min_cut_time:time(5 minutes), - max_cut_time:time(15 minutes), - num_customers:size_t(20) -) { - +main reactor( + waiting_room_capacity: size_t = 7, + max_time_between_customers: time = 10 minutes, + min_cut_time: time = 5 minutes, + max_cut_time: time = 15 minutes, + num_customers: size_t = 20) { factory = new CustomerFactory( - num_customers=num_customers, - max_time_between_customers=max_time_between_customers - ); - room = new WaitingRoom( - capacity=waiting_room_capacity, - num_customers=num_customers - ); + num_customers=num_customers, + max_time_between_customers=max_time_between_customers) + room = new WaitingRoom(capacity=waiting_room_capacity, num_customers=num_customers) barber = new Barber( - min_cut_time=min_cut_time, - max_cut_time=max_cut_time, - num_customers=num_customers - ) - customers = new[num_customers] Customer(); - - factory.send_customer -> room.customer_enters; - room.full -> customers.room_full; - room.wait -> customers.wait; - room.barber_leaves_with_customer -> barber.enter; - barber.next -> room.barber_arrives; - barber.start_cutting -> customers.start_cutting; - barber.done_cutting -> customers.done_cutting; - customers.done -> factory.customer_done; - - customers.returned -> factory.customer_returned; + min_cut_time=min_cut_time, + max_cut_time=max_cut_time, + num_customers=num_customers) + customers = new[num_customers] Customer() + + factory.send_customer -> room.customer_enters + room.full -> customers.room_full + room.wait -> customers.wait + room.barber_leaves_with_customer -> barber.enter + barber.next -> room.barber_arrives + barber.start_cutting -> customers.start_cutting + barber.done_cutting -> customers.done_cutting + customers.done -> factory.customer_done + + customers.returned -> factory.customer_returned } diff --git a/C/src/Smokers.lf b/C/src/Smokers.lf index 3f951e56..d7f7b10e 100644 --- a/C/src/Smokers.lf +++ b/C/src/Smokers.lf @@ -1,23 +1,19 @@ /** - * A classic challenge program in concurrent programming is called the - * "cigarette smoker's problem" was introduced by Suhas Patil in 1971 - * (Patil, 1971) and is discussed in Downey's *Little Book of Semaphores* - * (Downey, 2016). Patil's original formulation goes like this: - * - * "Three smokers are sitting at a table. One of them has tobacco, - * another has cigarette papers, and the third one has matches---each - * one has a different ingredient required to make and smoke a cigarette - * but he may not give any ingredient to another. On the table in front - * of them, two of the three ingredients will be placed, and the smoker - * who has the necessary third ingredient should pick up the ingredients - * from the table, make the cigarette and smoke it. Since a new set of - * ingredients will not be placed on the table until this action is - * completed, the other smokers who cannot make and smoke a cigarette - * with the ingredients on the table must not interfere with the fellow - * who can." - * - * A naive solution realizes each smoker as follows (in pseudo code, - * shown for the smoker that holds tobacco): + * A classic challenge program in concurrent programming is called the "cigarette smoker's problem" + * was introduced by Suhas Patil in 1971 (Patil, 1971) and is discussed in Downey's *Little Book of + * Semaphores* (Downey, 2016). Patil's original formulation goes like this: + * + * "Three smokers are sitting at a table. One of them has tobacco, another has cigarette papers, and + * the third one has matches---each one has a different ingredient required to make and smoke a + * cigarette but he may not give any ingredient to another. On the table in front of them, two of + * the three ingredients will be placed, and the smoker who has the necessary third ingredient + * should pick up the ingredients from the table, make the cigarette and smoke it. Since a new set + * of ingredients will not be placed on the table until this action is completed, the other smokers + * who cannot make and smoke a cigarette with the ingredients on the table must not interfere with + * the fellow who can." + * + * A naive solution realizes each smoker as follows (in pseudo code, shown for the smoker that holds + * tobacco): * ``` * while(true) { * acquire_paper(); @@ -26,90 +22,81 @@ * release(); * } * ``` - * The two "acquire" functions block until the specified resource is - * available on the table and then acquire exclusive access to that resource. - * This realization, however, very likely deadlocks because after this smoker - * acquires paper, another smoker may acquire the matches (or the supplier - * process supplies tobacco instead of matches). At that point, no further - * progress is possible and all smokers freeze. + * The two "acquire" functions block until the specified resource is available on the table and + * then acquire exclusive access to that resource. This realization, however, very likely deadlocks + * because after this smoker acquires paper, another smoker may acquire the matches (or the supplier + * process supplies tobacco instead of matches). At that point, no further progress is possible and + * all smokers freeze. + * + * Patil imposed some constraints, that "the process which supplies the ingredients cannot be + * changed," and that "no conditional statements can be used." Patil argued that under these + * constraints, the problem cannot be solved using Dijkstra's semaphores. * - * Patil imposed some constraints, that "the process which supplies the - * ingredients cannot be changed," and that "no conditional statements - * can be used." Patil argued that under these constraints, the problem - * cannot be solved using Dijkstra's semaphores. + * In 1975, Parnas showed that Patil had imposed some additional unstated constraints on the use of + * semaphores and gave a solution that uses vector semaphores, but still avoids conditional + * statements (Parnas, 1975). Downey argued that the constraint to avoid conditional statements is + * rather artificial, but with the less artificial constraint that the supplier not be modified (it + * could, after all, represent an operating system), then the problem is interesting and the + * solutions can get quite convoluted (Downey, 2016). Searching the web for solutions to this + * problem yields a few other attempts to solve it, including one that argues that the problem + * demonstrates the requirement for tests that enrich semaphores such as POSIX operations such as + * `sem_try_wait()` or `pthread_mutex_trylock()`. See for example OpenCSF: + * [https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/html/CigSmokers.html], although, unfortunately, + * the solution given there still exhibits the possibility of a form of deadlock, where one thread + * repeatedly, unfairly acquires a semaphore in a busy wait. * - * In 1975, Parnas showed that Patil had imposed some additional unstated - * constraints on the use of semaphores and gave a solution that uses - * vector semaphores, but still avoids conditional statements - * (Parnas, 1975). Downey argued that the constraint to avoid conditional - * statements is rather artificial, but with the less artificial constraint - * that the supplier not be modified (it could, after all, represent an - * operating system), then the problem is interesting and the solutions - * can get quite convoluted (Downey, 2016). Searching the web for solutions - * to this problem yields a few other attempts to solve it, including one - * that argues that the problem demonstrates the requirement for tests - * that enrich semaphores such as POSIX operations such as `sem_try_wait()` - * or `pthread_mutex_trylock()`. See for example OpenCSF: - * [https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/html/CigSmokers.html], - * although, unfortunately, the solution given there still exhibits the - * possibility of a form of deadlock, where one thread repeatedly, - * unfairly acquires a semaphore in a busy wait. - * - * A more commonly accepted solution, one implemented for example in - * the Savina actor benchmark suite (Imam and Sarkar, 2014), defines - * a centralized coordinator that first determines what the supplier - * has supplied, then decides which smoker should be given permission - * to take the supplies and dispatches a message to that smoker. - * - * Here, we give a Lingua Franca solution to Patil's original problem - * statement that does not use semaphores (except under the hood in the - * Lingua Franca implementation). This does not really solve Patil's - * problem, which was explicitly about how to accomplish this with - * semaphores, but instead demonstrates that some problems that become - * challenging puzzles with semaphores becomes trivially easy and - * uninteresting in Lingua Franca. + * A more commonly accepted solution, one implemented for example in the Savina actor benchmark + * suite (Imam and Sarkar, 2014), defines a centralized coordinator that first determines what the + * supplier has supplied, then decides which smoker should be given permission to take the supplies + * and dispatches a message to that smoker. + * + * Here, we give a Lingua Franca solution to Patil's original problem statement that does not use + * semaphores (except under the hood in the Lingua Franca implementation). This does not really + * solve Patil's problem, which was explicitly about how to accomplish this with semaphores, but + * instead demonstrates that some problems that become challenging puzzles with semaphores becomes + * trivially easy and uninteresting in Lingua Franca. * * References: - * - * Downey, A.B.: The Little Book of Semaphores, vol. Version 2.2.1. - * Green Tea Press, second edition edn. (2016), - * [https://greenteapress.com/semaphores/ LittleBookOfSemaphores.pdf] - * - * Imam,S.,Sarkar,V.: "Savina---An Actor Benchmark Suite Enabling Empirical - * Evaluation of Actor Libraries." In: Workshop on Programming based on - * Actors, Agents, and Decentralized Control (AGERE) (2014). - * [https://doi.org/10.1145/2687357.2687368] - * - * Parnas, D.L.: "On a Solution to the Cigarette Smokers’ Problem - * (Without Conditional Statements)." Communications of the ACM 18(3), - * 181–183 (March 1975). [https://doi.org/10.1145/360680.360709]. - * - * Patil,S.S.: "Limitations and Capabilities of Dijkstra’s Semaphore - * Primitives for Coordination among Processes." Report, Computation - * Structures Group, Project MAC, MIT (February 1971) - * + * + * Downey, A.B.: The Little Book of Semaphores, vol. Version 2.2.1. Green Tea Press, second edition + * edn. (2016), [https://greenteapress.com/semaphores/ LittleBookOfSemaphores.pdf] + * + * Imam,S.,Sarkar,V.: "Savina---An Actor Benchmark Suite Enabling Empirical Evaluation of Actor + * Libraries." In: Workshop on Programming based on Actors, Agents, and Decentralized Control + * (AGERE) (2014). [https://doi.org/10.1145/2687357.2687368] + * + * Parnas, D.L.: "On a Solution to the Cigarette Smokers’ Problem (Without Conditional Statements)." + * Communications of the ACM 18(3), 181–183 (March 1975). [https://doi.org/10.1145/360680.360709]. + * + * Patil,S.S.: "Limitations and Capabilities of Dijkstra’s Semaphore Primitives for Coordination + * among Processes." Report, Computation Structures Group, Project MAC, MIT (February 1971) + * * @author Edward A. Lee */ -target C; +target C + main reactor { - a = new Agent(); - s1 = new Smoker(smoke_time = 1 sec, has = 0); // Has tobacco. - s2 = new Smoker(smoke_time = 2 sec, has = 1); // Has paper. - s3 = new Smoker(smoke_time = 3 sec, has = 2); // Has matches. - (a.tobacco)+ -> s1.tobacco, s2.tobacco, s3.tobacco; - (a.paper)+ -> s1.paper, s2.paper, s3.paper; - (a.matches)+ -> s1.matches, s2.matches, s3.matches; - s1.done, s2.done, s3.done -> a.trigger; + a = new Agent() + s1 = new Smoker(smoke_time = 1 sec, has=0) // Has tobacco. + s2 = new Smoker(smoke_time = 2 sec, has=1) // Has paper. + s3 = new Smoker(smoke_time = 3 sec, has=2) // Has matches. + (a.tobacco)+ -> s1.tobacco, s2.tobacco, s3.tobacco + (a.paper)+ -> s1.paper, s2.paper, s3.paper + (a.matches)+ -> s1.matches, s2.matches, s3.matches + s1.done, s2.done, s3.done -> a.trigger } + reactor Agent { - input[3] trigger:bool; - output tobacco:bool; - output paper:bool; - output matches:bool; + input[3] trigger: bool + output tobacco: bool + output paper: bool + output matches: bool + reaction(startup) {= // At start, seed the random number. srand((unsigned)lf_time_logical()); =} + reaction(startup, trigger) -> tobacco, paper, matches {= int choice = rand() % 3; if (choice == 0) { @@ -127,22 +114,24 @@ reactor Agent { } =} } + reactor Smoker( - smoke_time:time(1 sec), - has:int(0) // 0 for tobacco, 1 for paper, 2 for matches -) { - input tobacco:bool; - input paper:bool; - input matches:bool; - - output done:bool; - - logical action smoke; - + smoke_time: time = 1 sec, + // 0 for tobacco, 1 for paper, 2 for matches + has: int = 0) { + input tobacco: bool + input paper: bool + input matches: bool + + output done: bool + + logical action smoke + reaction(smoke) -> done {= lf_print("Smoker is done smoking."); lf_set(done, true); =} + reaction(tobacco, paper, matches) -> smoke {= if (self->has == 0 && paper->is_present && matches->is_present) { lf_print("Smoker with tobacco starts smoking."); diff --git a/C/src/TrainDoor/TrainDoor.lf b/C/src/TrainDoor/TrainDoor.lf index 9da251bc..09340d89 100644 --- a/C/src/TrainDoor/TrainDoor.lf +++ b/C/src/TrainDoor/TrainDoor.lf @@ -1,11 +1,12 @@ /** - * Program that emulates a train door controller. It has two components: - * one that controls the door and one that senses motion. When the door - * controller receives a request to open the door (a button press), it has - * to first check whether the vehicle was recently in motion. The request - * will be denied if motion has been detected less than two seconds ago. + * Program that emulates a train door controller. It has two components: one that controls the door + * and one that senses motion. When the door controller receives a request to open the door (a + * button press), it has to first check whether the vehicle was recently in motion. The request will + * be denied if motion has been detected less than two seconds ago. */ -target C {keepalive: true}; +target C { + keepalive: true +} preamble {= #include @@ -14,7 +15,7 @@ preamble {= void* open; void* close; } buttons; - + void* read_input(void* arg) { printf("***************************************************************\n"); printf("Press 'o' and hit return or enter to open the door\n"); @@ -38,23 +39,26 @@ preamble {= } request_stop(); return NULL; - } + } =} reactor MotionDetector { - physical action movement; - state timestamp:time(0); - input check:bool; - output ok:bool; + physical action movement + state timestamp: time = 0 + input check: bool + output ok: bool + reaction(startup) -> movement {= buttons.move = movement; lf_thread_t thread_id; lf_thread_create(&thread_id, &read_input, NULL); =} + reaction(movement) {= printf("Motion detected!\n"); self->timestamp = lf_time_logical(); =} + reaction(check) -> ok {= if (self->timestamp == 0L || (lf_time_logical() - self->timestamp) > SECS(2)) { lf_set(ok, true); @@ -65,19 +69,19 @@ reactor MotionDetector { } reactor DoorController { - - physical action open; - physical action close; - - output check:bool; - input ok:bool; - state opened:bool(false); - state requested:bool(false); + physical action open + physical action close + + output check: bool + input ok: bool + state opened: bool = false + state requested: bool = false + reaction(startup) -> open, close {= buttons.open = open; buttons.close = close; =} - + reaction(open) -> check {= if (self->opened) { printf("The door is already open\n"); @@ -87,16 +91,16 @@ reactor DoorController { self->requested = true; } =} - + reaction(close) {= printf("Closing the door\n"); self->opened = false; =} - + reaction(ok) {= if (self->requested && ok->value) { self->opened = true; - printf("Opening the door.\n"); + printf("Opening the door.\n"); } else { printf("Cannot open the door; recent motion detected.\n"); } @@ -105,8 +109,8 @@ reactor DoorController { } main reactor TrainDoor { - motion = new MotionDetector(); - door = new DoorController(); - door.check -> motion.check; - motion.ok -> door.ok; + motion = new MotionDetector() + door = new DoorController() + door.check -> motion.check + motion.ok -> door.ok } diff --git a/C/src/TrainDoor/TrainDoorAsymmetric.lf b/C/src/TrainDoor/TrainDoorAsymmetric.lf index 1e97554f..7cc42c9a 100644 --- a/C/src/TrainDoor/TrainDoorAsymmetric.lf +++ b/C/src/TrainDoor/TrainDoorAsymmetric.lf @@ -1,49 +1,60 @@ // This is a variant of the example is considered in this paper: -// https://www.mdpi.com/2227-7390/8/7/1068 +// https://www.mdpi.com/2227-7390/8/7/1068 // where it is studied for its verifiability. -target C; +target C + reactor Controller { - output lock:bool; output unlock:bool; - output move:bool; output stop:bool; - physical action external:bool; + output lock: bool + output unlock: bool + output move: bool + output stop: bool + physical action external: bool + reaction(startup) {= - // ... Set up external sensing. + // ... Set up external sensing. =} - reaction(external)->lock, unlock, move, stop {= - if (external->value) { - lf_set(lock, true); lf_set(move, true); - } else { - lf_set(unlock, true); lf_set(stop, true); - } + + reaction(external) -> lock, unlock, move, stop {= + if (external->value) { + lf_set(lock, true); lf_set(move, true); + } else { + lf_set(unlock, true); lf_set(stop, true); + } =} } + reactor Train { - input move:bool; input stop:bool; - state moving:bool(false); - reaction(move) {= - self->moving = true; - =} - reaction(stop) {= - self->moving = false; - =} + input move: bool + input stop: bool + state moving: bool = false + + reaction(move) {= self->moving = true; =} + + reaction(stop) {= self->moving = false; =} } + reactor Door { - input lock:bool; input unlock:bool; - state locked:bool(false); + input lock: bool + input unlock: bool + state locked: bool = false + reaction(lock) {= - // ... Actuate to lock door. - self->locked = true; + // ... Actuate to lock door. + self->locked = true; =} + reaction(unlock) {= - // ... Actuate to unlock door. - self->locked = false; + // ... Actuate to unlock door. + self->locked = false; =} } + main reactor { - c = new Controller(); d = new Door(); - t = new Train(); - c.lock -> d.lock; - c.unlock -> d.unlock after 100 msec; // |\label{line:unlockafter}| - c.move -> t.move after 100 msec; // |\label{line:moveafter}| - c.stop -> t.stop; + c = new Controller() + d = new Door() + t = new Train() + c.lock -> d.lock + c.unlock -> d.unlock after 100 msec // |label{line:unlockafter}| + c.move -> t.move after 100 msec // |label{line:moveafter}| + c.stop -> t.stop } diff --git a/C/src/TrainDoor/TrainDoorSimplest.lf b/C/src/TrainDoor/TrainDoorSimplest.lf index 747635ad..2e98c42e 100644 --- a/C/src/TrainDoor/TrainDoorSimplest.lf +++ b/C/src/TrainDoor/TrainDoorSimplest.lf @@ -1,39 +1,47 @@ // This simple example is considered in this paper: -// https://www.mdpi.com/2227-7390/8/7/1068 +// https://www.mdpi.com/2227-7390/8/7/1068 // where it is studied for its verifiability. -target C; +target C + reactor Controller { - output lock:bool; - output move:bool; - physical action external_move:bool; + output lock: bool + output move: bool + physical action external_move: bool + reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} - reaction(external_move)->lock, move {= - lf_set(lock, external_move->value); - lf_set(move, external_move->value); + + reaction(external_move) -> lock, move {= + lf_set(lock, external_move->value); + lf_set(move, external_move->value); =} } + reactor Train { - input move:bool; - state moving:bool(false); + input move: bool + state moving: bool = false + reaction(move) {= - // ... actuate to move or stop - self->moving = move->value; + // ... actuate to move or stop + self->moving = move->value; =} } + reactor Door { - input lock:bool; - state locked:bool(false); + input lock: bool + state locked: bool = false + reaction(lock) {= - // ... Actuate to lock or unlock door. - self->locked = lock->value; + // ... Actuate to lock or unlock door. + self->locked = lock->value; =} } + main reactor { - controller = new Controller(); - door = new Door(); - train = new Train(); - controller.lock -> door.lock; - controller.move -> train.move; + controller = new Controller() + door = new Door() + train = new Train() + controller.lock -> door.lock + controller.move -> train.move } diff --git a/C/src/TrainDoor/TrainDoorWithDeadlines.lf b/C/src/TrainDoor/TrainDoorWithDeadlines.lf index 0afdabfa..92a7d35b 100644 --- a/C/src/TrainDoor/TrainDoorWithDeadlines.lf +++ b/C/src/TrainDoor/TrainDoorWithDeadlines.lf @@ -1,66 +1,72 @@ // This is a variant of the example is considered in this paper: -// https://www.mdpi.com/2227-7390/8/7/1068 +// https://www.mdpi.com/2227-7390/8/7/1068 // where it is studied for its verifiability. -target C; +target C + reactor Controller { - output lock:bool; - output unlock:bool; - output move:bool; - output stop:bool; - physical action external_move(100 msec):bool; + output lock: bool + output unlock: bool + output move: bool + output stop: bool + physical action external_move(100 msec): bool + reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} - reaction(external_move)->lock, move, unlock, stop {= - lf_set(lock, external_move->value); - lf_set(move, external_move->value); + + reaction(external_move) -> lock, move, unlock, stop {= + lf_set(lock, external_move->value); + lf_set(move, external_move->value); =} } + realtime reactor Train { - input move:bool; - input stop:bool; - state moving:bool(false); - reaction(stop) {= - self->moving = false; - =} deadline (50 msec) {= =} - reaction(move) {= - self->moving = false; - =} deadline(48 msec) {= =} + input move: bool + input stop: bool + state moving: bool = false + + reaction(stop) {= self->moving = false; =} deadline(50 msec) {= =} + + reaction(move) {= self->moving = false; =} deadline(48 msec) {= =} } + realtime reactor Door { - input lock:bool; - input unlock:bool; - physical action external_open; - state locked:bool(false); - state open:bool(false); + input lock: bool + input unlock: bool + physical action external_open + state locked: bool = false + state open: bool = false + reaction(startup) {= - // ... Set up sensing. - =} deadline(48 msec) {= =} + // ... Set up sensing. + =} deadline(48 msec) {= =} + reaction(lock) {= - if (lock && self->open) { - // ... Actuate to close door. - self->open = false; - } - self->locked = lock->value; + if (lock && self->open) { + // ... Actuate to close door. + self->open = false; + } + self->locked = lock->value; =} deadline(50 msec) {= // ... handle the deadline violation... =} - reaction(unlock) {= - self->locked = true; - =} + + reaction(unlock) {= self->locked = true; =} + reaction(external_open) {= - if (!self->locked) { - // ... Actuate to open door. - self->open = true; - } + if (!self->locked) { + // ... Actuate to open door. + self->open = true; + } =} } + main reactor { - controller = new Controller(); - door = new Door(); - train = new Train(); - controller.lock -> door.lock; - controller.move -> train.move after 51 msec; - controller.unlock -> door.unlock after 51 msec; - controller.stop -> train.stop; + controller = new Controller() + door = new Door() + train = new Train() + controller.lock -> door.lock + controller.move -> train.move after 51 msec + controller.unlock -> door.unlock after 51 msec + controller.stop -> train.stop } diff --git a/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf b/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf index 429e10cb..68b22af0 100644 --- a/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf +++ b/C/src/TrainDoor/TrainDoorWithDoorOpenState.lf @@ -1,56 +1,66 @@ // This is a variant of the example is considered in this paper: -// https://www.mdpi.com/2227-7390/8/7/1068 +// https://www.mdpi.com/2227-7390/8/7/1068 // where it is studied for its verifiability. -target C; +target C + reactor Controller { - output lock:bool; - output move:bool; - physical action external_move:bool; + output lock: bool + output move: bool + physical action external_move: bool + reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} - reaction(external_move)->lock, move {= - lf_set(lock, external_move->value); - lf_set(move, external_move->value); + + reaction(external_move) -> lock, move {= + lf_set(lock, external_move->value); + lf_set(move, external_move->value); =} } + reactor Train { - input move:bool; - state moving:bool(false); + input move: bool + state moving: bool = false + reaction(move) {= - if (move) { - self->moving = true; - } else { - self->moving = false; - } + if (move) { + self->moving = true; + } else { + self->moving = false; + } =} } + reactor Door { - input lock:bool; - physical action external_open; - state locked:bool(false); - state open:bool(false); + input lock: bool + physical action external_open + state locked: bool = false + state open: bool = false + reaction(startup) {= - // ... Set up sensing. + // ... Set up sensing. =} + reaction(lock) {= - if (lock && self->open) { - // ... Actuate to close door. - self->open = false; - } - self->locked = lock->value; + if (lock && self->open) { + // ... Actuate to close door. + self->open = false; + } + self->locked = lock->value; =} + reaction(external_open) {= - if (!self->locked) { - // ... Actuate to open door. - self->open = true; - } + if (!self->locked) { + // ... Actuate to open door. + self->open = true; + } =} } + main reactor { - controller = new Controller(); - door = new Door(); - train = new Train(); - controller.lock -> door.lock; - controller.move -> train.move; + controller = new Controller() + door = new Door() + train = new Train() + controller.lock -> door.lock + controller.move -> train.move } diff --git a/C/src/TrainDoor/TrainDoorWithOpen.lf b/C/src/TrainDoor/TrainDoorWithOpen.lf index f2f8b562..18925896 100644 --- a/C/src/TrainDoor/TrainDoorWithOpen.lf +++ b/C/src/TrainDoor/TrainDoorWithOpen.lf @@ -1,52 +1,65 @@ // This is a variant of the example is considered in this paper: -// https://www.mdpi.com/2227-7390/8/7/1068 +// https://www.mdpi.com/2227-7390/8/7/1068 // where it is studied for its verifiability. -target C; +target C + reactor Controller { - output close:bool; output lock:bool; - output unlock:bool; output open:bool; - physical action external:bool; + output close: bool + output lock: bool + output unlock: bool + output open: bool + physical action external: bool + reaction(startup) {= - // ... Set up external sensing. + // ... Set up external sensing. =} - reaction(external)->close, lock, open, unlock {= - if (external->value) { - lf_set(close, true); lf_set(lock, true); - } else { - lf_set(open, true); lf_set(unlock, true); - } + + reaction(external) -> close, lock, open, unlock {= + if (external->value) { + lf_set(close, true); lf_set(lock, true); + } else { + lf_set(open, true); lf_set(unlock, true); + } =} } + reactor Door { - input close:bool; input lock:bool; - input unlock:bool; input open:bool; - physical action ext_open:bool; - state locked:bool(false); - state is_open:bool(false); + input close: bool + input lock: bool + input unlock: bool + input open: bool + physical action ext_open: bool + state locked: bool = false + state is_open: bool = false + reaction(close) {= - // ... Actuate to close door. - self->is_open = false; + // ... Actuate to close door. + self->is_open = false; =} + reaction(lock) {= - // ... Actuate to lock door. - if(!self->is_open) - self->locked = true; + // ... Actuate to lock door. + if(!self->is_open) + self->locked = true; =} + reaction(unlock) {= - // ... Actuate to unlock door. - self->locked = false; + // ... Actuate to unlock door. + self->locked = false; =} + reaction(open, ext_open) {= - // ... Actuate to open door. - if(!self->locked) - self->is_open = true; + // ... Actuate to open door. + if(!self->locked) + self->is_open = true; =} } + main reactor { - c = new Controller(); - d = new Door(); - c.lock -> d.lock after 5 msec; // |\label{line:unlockafter}| - c.unlock -> d.unlock after 4 msec; // |\label{line:unlockafter}| - c.open -> d.open after 7 msec; // |\label{line:unlockafter}| - c.close -> d.close after 3 msec; // |\label{line:unlockafter}| + c = new Controller() + d = new Door() + c.lock -> d.lock after 5 msec // |label{line:unlockafter}| + c.unlock -> d.unlock after 4 msec // |label{line:unlockafter}| + c.open -> d.open after 7 msec // |label{line:unlockafter}| + c.close -> d.close after 3 msec // |label{line:unlockafter}| } diff --git a/C/src/browser-ui/BrowserUI.lf b/C/src/browser-ui/BrowserUI.lf index 657e9a6f..30eb6eef 100644 --- a/C/src/browser-ui/BrowserUI.lf +++ b/C/src/browser-ui/BrowserUI.lf @@ -1,20 +1,18 @@ /** - * A basic user interface realized in the browser. - * This creates a basic web server that listens on a port (default 8080) - * for requests. When it receives a request with no path - * (e.g. "http://localhost:8080/"), it responds with the contents - * of a file specified by the `initial_file` parameter, which is expected - * to be an HTML file and outputs a true on its initialized output. - * - * When it receives a request with a path (e.g. "http://localhost:8080/test"), - * it responds with text. Normally, this would be JSON-formatted, but in this - * simple illustration, it is just plain text that reports the count of the - * number of requests that have been received. - * - * The default `initial_file` provides HTML containing a button that the user - * can push to issue a request with a path. This results in a display in the - * web page of the number of times the button has been pushed. - * + * A basic user interface realized in the browser. This creates a basic web server that listens on a + * port (default 8080) for requests. When it receives a request with no path (e.g. + * "http://localhost:8080/"), it responds with the contents of a file specified by the + * `initial_file` parameter, which is expected to be an HTML file and outputs a true on its + * initialized output. + * + * When it receives a request with a path (e.g. "http://localhost:8080/test"), it responds with + * text. Normally, this would be JSON-formatted, but in this simple illustration, it is just plain + * text that reports the count of the number of requests that have been received. + * + * The default `initial_file` provides HTML containing a button that the user can push to issue a + * request with a path. This results in a display in the web page of the number of times the button + * has been pushed. + * * @author Edward A. Lee */ target C { @@ -44,44 +42,107 @@ preamble {= =} /** - * Start an HTTP server, listen for requests, and provide responses. - * When an HTTP request comes in, this reactor outputs on the request - * output the part of the path starting with the first slash. - * For example, if the URL is http://localhost:8080/foo, then the - * request output will produce "/foo". - * If there is nothing after the first slash or no first slash, - * then this reactor will instead return the contents of the initial_file - * parameter, output true on the initialized output, and output nothing - * on the request output. + * Start an HTTP server, listen for requests, and provide responses. When an HTTP request comes in, + * this reactor outputs on the request output the part of the path starting with the first slash. + * For example, if the URL is http://localhost:8080/foo, then the request output will produce + * "/foo". If there is nothing after the first slash or no first slash, then this reactor will + * instead return the contents of the initial_file parameter, output true on the initialized output, + * and output nothing on the request output. * @param initial_file Path to the initial HTML file to serve, relative to the source directory. - * Defaults to "page.html". + * Defaults to "page.html". * @param hostport The host port number, which defults to 8080. */ -reactor ServerUI( - initial_file:string = "page.html", - hostport:uint16_t = 8080 -) { - output initialized:bool - output request:char* - input response:char* - - physical action req_action:char* - - state browser_ui:browser_ui_t - +reactor ServerUI(initial_file: string = "page.html", hostport: uint16_t = 8080) { + output initialized: bool + output request: char* + input response: char* + + physical action req_action: char* + + state browser_ui: browser_ui_t + + preamble {= + const char html_header[] = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n"; + + void* listener_thread(void* args) { + browser_ui_t* browser_ui = (browser_ui_t*)args; + + int server_socket = socket(AF_INET, SOCK_STREAM, 0); + if (server_socket < 0) { + lf_print_error_and_exit("Error creating socket."); + } + + int one = 1; + // Allow reusing of local addresses. + setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); + + struct sockaddr_in server_address; + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = INADDR_ANY; + server_address.sin_port = htons(browser_ui->hostport); + + if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { + close(server_socket); + lf_print_error_and_exit("Error binding socket."); + } + // Pending queue length of 5. + listen(server_socket, 5); + + lf_print("****** Point your browser to http://localhost:%d", browser_ui->hostport); + + while(browser_ui->running) { + struct sockaddr_in client_address; + int client_address_length = sizeof(client_address); + + browser_ui->client_socket = accept(server_socket, (struct sockaddr *)&client_address, (socklen_t*)&client_address_length); + + if (browser_ui->client_socket < 0) { + lf_print_error_and_exit("Error accepting connection."); + } + + char buffer[1024] = {0}; + read(browser_ui->client_socket, buffer, 1024); + + // The response depends on the path part of the request. + const char *start_of_path = strchr(buffer, ' ') + 1; + if (start_of_path != NULL && strncmp("/ ", start_of_path, 2) != 0) { + const char *end_of_path = strchr(start_of_path, ' ') + 1; + size_t length = end_of_path - start_of_path; + char* path = (char*)malloc(length + 1); + strncpy(path, start_of_path, length); + path[length] = '\0'; + lf_schedule_value(browser_ui->req_action, 0, path, length + 1); + } else { + // Default is to write initial page. + write(browser_ui->client_socket, html_header, strlen(html_header)); + write( + browser_ui->client_socket, + browser_ui->initial_page, + strlen(browser_ui->initial_page) + ); + close(browser_ui->client_socket); + browser_ui->client_socket = -1; + lf_schedule_copy(browser_ui->req_action, 0, "", 1); + } + } + return NULL; + } + =} + reaction(startup) -> req_action {= // Read the default file to serve. self->browser_ui.initial_page = read_file(self->initial_file); - + self->browser_ui.running = true; self->browser_ui.client_socket = -1; // No client socket awaiting response. self->browser_ui.req_action = req_action; self->browser_ui.hostport = self->hostport; - + lf_thread_t listener; lf_thread_create(&listener, &listener_thread, &self->browser_ui); =} - + reaction(req_action) -> request, initialized {= if (strlen(req_action->value) == 0) { lf_set(initialized, true); @@ -89,7 +150,7 @@ reactor ServerUI( lf_set_token(request, req_action->token); } =} - + reaction(response) {= if (self->browser_ui.client_socket < 0) { lf_print_error("No pending request at the server!"); @@ -100,20 +161,18 @@ reactor ServerUI( close(self->browser_ui.client_socket); self->browser_ui.client_socket = -1; =} - + reaction(shutdown) {= self->browser_ui.running = false; free(self->browser_ui.initial_page); =} /** - * Read a file at path relative to the source .lf file - * and return dynamically-allocated memory with its contents. - * The caller is responsible for freeing the pointer returned. + * Read a file at path relative to the source .lf file and return dynamically-allocated memory + * with its contents. The caller is responsible for freeing the pointer returned. * @param path File path relative to the source .lf file. */ - method read_file(path:{=const char*=}): char* {= - + method read_file(path: {= const char* =}): char* {= // Construct the path to the file to be read char* file_path = (char *) malloc(strlen(LF_SOURCE_DIRECTORY) + strlen(path) + 2); if (file_path == NULL) lf_print_error_and_exit("Out of memory."); @@ -141,87 +200,17 @@ reactor ServerUI( fclose(file); return buffer; =} - - preamble {= - const char html_header[] = "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=UTF-8\r\n\r\n"; - - void* listener_thread(void* args) { - browser_ui_t* browser_ui = (browser_ui_t*)args; - - int server_socket = socket(AF_INET, SOCK_STREAM, 0); - if (server_socket < 0) { - lf_print_error_and_exit("Error creating socket."); - } - - int one = 1; - // Allow reusing of local addresses. - setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); - - struct sockaddr_in server_address; - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = INADDR_ANY; - server_address.sin_port = htons(browser_ui->hostport); - - if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { - close(server_socket); - lf_print_error_and_exit("Error binding socket."); - } - // Pending queue length of 5. - listen(server_socket, 5); - - lf_print("****** Point your browser to http://localhost:%d", browser_ui->hostport); - - while(browser_ui->running) { - struct sockaddr_in client_address; - int client_address_length = sizeof(client_address); - - browser_ui->client_socket = accept(server_socket, (struct sockaddr *)&client_address, (socklen_t*)&client_address_length); - - if (browser_ui->client_socket < 0) { - lf_print_error_and_exit("Error accepting connection."); - } - - char buffer[1024] = {0}; - read(browser_ui->client_socket, buffer, 1024); - - // The response depends on the path part of the request. - const char *start_of_path = strchr(buffer, ' ') + 1; - if (start_of_path != NULL && strncmp("/ ", start_of_path, 2) != 0) { - const char *end_of_path = strchr(start_of_path, ' ') + 1; - size_t length = end_of_path - start_of_path; - char* path = (char*)malloc(length + 1); - strncpy(path, start_of_path, length); - path[length] = '\0'; - lf_schedule_value(browser_ui->req_action, 0, path, length + 1); - } else { - // Default is to write initial page. - write(browser_ui->client_socket, html_header, strlen(html_header)); - write( - browser_ui->client_socket, - browser_ui->initial_page, - strlen(browser_ui->initial_page) - ); - close(browser_ui->client_socket); - browser_ui->client_socket = -1; - lf_schedule_copy(browser_ui->req_action, 0, "", 1); - } - } - return NULL; - } - =} } main reactor { - state count:int = 0 + state count: int = 0 s = new ServerUI() - - reaction(s.initialized) {= - self->count = 0; - =} + + reaction(s.initialized) {= self->count = 0; =} + reaction(s.request) -> s.response {= char* response; - asprintf(&response, "You have pushed %d times. Path of GET request: %s", + asprintf(&response, "You have pushed %d times. Path of GET request: %s", ++self->count, s.request->value ); diff --git a/C/src/browser-ui/WebSocket.lf b/C/src/browser-ui/WebSocket.lf index 97a20641..6d2a87e2 100644 --- a/C/src/browser-ui/WebSocket.lf +++ b/C/src/browser-ui/WebSocket.lf @@ -1,20 +1,17 @@ /** - * Demo of a use of WebSocketServer enabling a user interface realized in the - * browser. Compile and run this program, then open WebSocket.html in your - * favorite browser. This example program sends to the web page a counting - * sequence. It also accepts text messages from the web page and prints them on - * standard output. + * Demo of a use of WebSocketServer enabling a user interface realized in the browser. Compile and + * run this program, then open WebSocket.html in your favorite browser. This example program sends + * to the web page a counting sequence. It also accepts text messages from the web page and prints + * them on standard output. * - * This example also shows how to limit the number of connections to just two. - * If you try to open the WebSocket.html web page more than twice, only the - * first two attempts will succeed in connecting. By default, WebSocketServer - * imposes no such limit. + * This example also shows how to limit the number of connections to just two. If you try to open + * the WebSocket.html web page more than twice, only the first two attempts will succeed in + * connecting. By default, WebSocketServer imposes no such limit. * - * This example also shows one way to keep track of multiple connections. It - * uses a hashset containing pointers to the wsi (web socket interface) fields, - * which are unique to each connection. In this example, if you connect two - * clients, one will receive even numbered counts, and the other will receive - * odd numbered counts. + * This example also shows one way to keep track of multiple connections. It uses a hashset + * containing pointers to the wsi (web socket interface) fields, which are unique to each + * connection. In this example, if you connect two clients, one will receive even numbered counts, + * and the other will receive odd numbered counts. * * @author Edward A. Lee */ @@ -35,7 +32,7 @@ main reactor { state connected_instances: hashset_t = {= NULL =} - s = new WebSocketServer(max_clients = 2) // Limit number of clients to 2. + s = new WebSocketServer(max_clients=2) // Limit number of clients to 2. reaction(startup) -> s.send {= lf_set_destructor(s.send, web_socket_message_destructor); diff --git a/C/src/browser-ui/WebSocketServer.lf b/C/src/browser-ui/WebSocketServer.lf index ad9ff1ed..637443ea 100644 --- a/C/src/browser-ui/WebSocketServer.lf +++ b/C/src/browser-ui/WebSocketServer.lf @@ -1,40 +1,35 @@ /** - * A web socket server enabling a user interface realized in the browser. This - * creates a web server that listens on a port (default 8000) for web socket - * connections. + * A web socket server enabling a user interface realized in the browser. This creates a web server + * that listens on a port (default 8000) for web socket connections. * - * When a connection is established with a client, an output is produced on the - * connected port that is a struct with a unique wsi (web socket interface) for - * the client and a boolean indicating whether the connection is being opened or - * closed. The wsi can be used to provide input at the send port that will - * target this specific client. + * When a connection is established with a client, an output is produced on the connected port that + * is a struct with a unique wsi (web socket interface) for the client and a boolean indicating + * whether the connection is being opened or closed. The wsi can be used to provide input at the + * send port that will target this specific client. * - * To send messages to a client, construct a dynamically allocated struct of - * type web_socket_message_t, set its wsi field to the value provided by the - * connected output, and set its message and length. Only strings can be sent. + * To send messages to a client, construct a dynamically allocated struct of type + * web_socket_message_t, set its wsi field to the value provided by the connected output, and set + * its message and length. Only strings can be sent. * - * When a message is received from a client, a struct of type - * web_socket_message_t will be produced on the received output port. You can - * use the wsi field to determine which client sent the message. + * When a message is received from a client, a struct of type web_socket_message_t will be produced + * on the received output port. You can use the wsi field to determine which client sent the + * message. * - * You can limit the number of clients by setting the max_clients parameter. It - * defaults to 0, which means there is no limit. A common case for an embedded - * application might be 1 to ensure that only one client connects to your - * application. + * You can limit the number of clients by setting the max_clients parameter. It defaults to 0, which + * means there is no limit. A common case for an embedded application might be 1 to ensure that only + * one client connects to your application. * * This uses the libwebsockets (see API - * documentation and API documentation and installation * instructions). To install on MacOS, we recommending using brew: *
 brew install libwebsockets
- * 
This puts the compiled libraries in {@code /usr/local/lib}, and these - * libraries can be linked to providing the {@code -lwebsockets} compile option. + * This puts the compiled libraries in {@code /usr/local/lib}, and these libraries can be + * linked to providing the {@code -lwebsockets} compile option. * * There are a number of limitations: *
    - *
  1. **FIXME:** This should use the secure sockets API in libwebsockets to - * get SSL. + *
  2. **FIXME:** This should use the secure sockets API in libwebsockets to get SSL. *
  3. **FIXME:** This currently only supports sending and receiving text. *
* @@ -275,9 +270,7 @@ reactor WebSocketServer(hostport: int = 8000, max_clients: int = 0) { self->status.running = true; =} - reaction(received_action) -> received {= - lf_set_token(received, received_action->token); - =} + reaction(received_action) -> received {= lf_set_token(received, received_action->token); =} reaction(send) {= // NOTE: This send must be before the reaction to connected_action @@ -297,9 +290,7 @@ reactor WebSocketServer(hostport: int = 8000, max_clients: int = 0) { } =} - reaction(connected_action) -> connected {= - lf_set(connected, connected_action->value); - =} + reaction(connected_action) -> connected {= lf_set(connected, connected_action->value); =} reaction(shutdown) {= self->status.running = false; =} } diff --git a/C/src/car-brake/CarBrake.lf b/C/src/car-brake/CarBrake.lf index 6da1d443..3f1d4c98 100644 --- a/C/src/car-brake/CarBrake.lf +++ b/C/src/car-brake/CarBrake.lf @@ -1,36 +1,36 @@ /** - * Sketch of an ADAS system illustrating tradeoffs between consistency and availability. - * This version experiences deadline violations in the Brake component because of - * processing times in the BrakingAssistant component. - * This version is federated for fair comparison with the other two versions. - * + * Sketch of an ADAS system illustrating tradeoffs between consistency and availability. This + * version experiences deadline violations in the Brake component because of processing times in the + * BrakingAssistant component. This version is federated for fair comparison with the other two + * versions. + * * @author Christian Menard * @author Edward A. Lee */ target C { - timeout: 10s, + timeout: 10 s, keepalive: true -}; +} reactor Camera { - timer t(20ms, 33ms) + timer t(20 ms, 33 ms) output frame: bool - reaction (t) -> frame {= + reaction(t) -> frame {= lf_set(frame, true); // send a "frame" =} } reactor BrakingAssistant { - input frame: bool; - output trigger_brake: bool; - - state counter: int(0); + input frame: bool + output trigger_brake: bool + + state counter: int = 0 reaction(frame) -> trigger_brake {= // processing takes some time (10 ms) lf_sleep(MSEC(10)); - + if (self->counter % 10 == 0) { lf_print("[automatic] Send the brake signal at physical time " PRINTF_TIME, lf_time_physical_elapsed()); lf_set(trigger_brake, true); @@ -54,15 +54,13 @@ reactor Braking { return NULL; } =} - input brake_assistant: bool; + input brake_assistant: bool - physical action pedal; + physical action pedal - state thread: lf_thread_t; + state thread: lf_thread_t - reaction(startup) -> pedal {= - lf_thread_create(&self->thread, &press_pedal, pedal); - =} + reaction(startup) -> pedal {= lf_thread_create(&self->thread, &press_pedal, pedal); =} reaction(shutdown) {= stop_thread = true; @@ -74,17 +72,17 @@ reactor Braking { lf_print("[system] Brake manually triggered at physical time " PRINTF_TIME, lf_time_physical_elapsed() ); - =} deadline (3msecs) {= + =} deadline(3 msecs) {= lf_print("\033[1;31m[ERROR]\033[0m Deadline on manual braking violated at physical time " PRINTF_TIME, lf_time_physical_elapsed() ); =} - + reaction(brake_assistant) {= lf_print("[system] Brake automatically triggered at physical time " PRINTF_TIME, lf_time_physical_elapsed() ); - =} deadline (25msecs) {= + =} deadline(25 msecs) {= lf_print("\033[1;31m[error]\033[0m Deadline on automatic braking violated " "at physical time " PRINTF_TIME, lf_time_physical_elapsed() ); @@ -92,17 +90,17 @@ reactor Braking { } reactor Vision { - output trigger_brake: bool; - camera = new Camera(); - assistant = new BrakingAssistant(); + output trigger_brake: bool + camera = new Camera() + assistant = new BrakingAssistant() - camera.frame -> assistant.frame; - assistant.trigger_brake -> trigger_brake; + camera.frame -> assistant.frame + assistant.trigger_brake -> trigger_brake } federated reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking() + vision = new Vision() - vision.trigger_brake -> braking.brake_assistant; + vision.trigger_brake -> braking.brake_assistant } diff --git a/C/src/car-brake/CarBrake2.lf b/C/src/car-brake/CarBrake2.lf index 4afd73e2..29805602 100644 --- a/C/src/car-brake/CarBrake2.lf +++ b/C/src/car-brake/CarBrake2.lf @@ -1,20 +1,19 @@ /** - * This version of the CarBreak example decouples the Vision analysis - * using a physical connection, which gives up consistency (and determinacy). - * The execution is federated, so the Vision component has no effect - * on the ability to meet deadlines in the response to brake pedal actions. - * This version is far less likely to experience deadline violations. + * This version of the CarBreak example decouples the Vision analysis using a physical connection, + * which gives up consistency (and determinacy). The execution is federated, so the Vision component + * has no effect on the ability to meet deadlines in the response to brake pedal actions. This + * version is far less likely to experience deadline violations. */ target C { keepalive: true, - timeout: 10s -}; + timeout: 10 s +} import Braking, Vision from "CarBrake.lf" federated reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking() + vision = new Vision() - vision.trigger_brake ~> braking.brake_assistant; + vision.trigger_brake ~> braking.brake_assistant } diff --git a/C/src/car-brake/CarBrake3.lf b/C/src/car-brake/CarBrake3.lf index 752b8efa..4201b5aa 100644 --- a/C/src/car-brake/CarBrake3.lf +++ b/C/src/car-brake/CarBrake3.lf @@ -1,20 +1,19 @@ /** - * This version of the CarBreak example decouples the Vision analysis - * using a physical connection, which gives up consistency (and determinacy). - * The execution is federated, so the Vision component has no effect - * on the ability to meet deadlines in the response to brake pedal actions. - * This version is far less likely to experience deadline violations. + * This version of the CarBreak example decouples the Vision analysis using a physical connection, + * which gives up consistency (and determinacy). The execution is federated, so the Vision component + * has no effect on the ability to meet deadlines in the response to brake pedal actions. This + * version is far less likely to experience deadline violations. */ target C { keepalive: true, - timeout: 10s -}; + timeout: 10 s +} import Braking, Vision from "CarBrake.lf" federated reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking() + vision = new Vision() - vision.trigger_brake -> braking.brake_assistant after 20ms; + vision.trigger_brake -> braking.brake_assistant after 20 ms } diff --git a/C/src/deadlines/AnytimePrime.lf b/C/src/deadlines/AnytimePrime.lf index 095155b8..c2a6c4a1 100644 --- a/C/src/deadlines/AnytimePrime.lf +++ b/C/src/deadlines/AnytimePrime.lf @@ -1,12 +1,10 @@ /** - * This program is a concept demonstration showing how the lf_check_deadline() - * function works. This program performs "anytime computation" - * (https://en.wikipedia.org/wiki/Anytime_algorithm) to find the largest prime - * within the given deadline. The lf_check_deadline() function is called after - * checking whether each number is prime. If lf_check_deadline() is called after - * the deadline has passed, the function calls the deadline handler and returns - * true. Then the reaction outputs the largest prime found by that time and - * exits. + * This program is a concept demonstration showing how the lf_check_deadline() function works. This + * program performs "anytime computation" (https://en.wikipedia.org/wiki/Anytime_algorithm) to find + * the largest prime within the given deadline. The lf_check_deadline() function is called after + * checking whether each number is prime. If lf_check_deadline() is called after the deadline has + * passed, the function calls the deadline handler and returns true. Then the reaction outputs the + * largest prime found by that time and exits. * * For more discussion on lf_check_deadline(), see: * https://github.com/lf-lang/lingua-franca/issues/403 @@ -17,10 +15,7 @@ */ target C { fast: true, - files: [ - "/lib/c/reactor-c/include/core/utils/vector.h", - "/lib/c/reactor-c/core/utils/vector.c" - ], + files: ["/lib/c/reactor-c/include/core/utils/vector.h", "/lib/c/reactor-c/core/utils/vector.c"], cmake-include: ["/lib/c/reactor-c/core/utils/vector.cmake"] } @@ -60,9 +55,7 @@ reactor Prime { reactor Print { input in: {= long long =} - reaction(in) {= - printf("Largest prime found within the deadline: %lld\n", in->value); - =} + reaction(in) {= printf("Largest prime found within the deadline: %lld\n", in->value); =} } main reactor AnytimePrime { diff --git a/C/src/deadlines/Deadline.lf b/C/src/deadlines/Deadline.lf index 4058e70a..6ec8d499 100644 --- a/C/src/deadlines/Deadline.lf +++ b/C/src/deadlines/Deadline.lf @@ -1,9 +1,8 @@ /** - * Example that demonstrates the detection of deadline misses. This program - * models a sensor, some work done on the sensor data, and an actuator with a - * deadline. The sensor is your keyboard Return or Enter button. The work that - * is done is simulated every second time to take more than 500 microseconds (by - * sleeping for 500 usecs). Hence, you should see that deadline is missed every + * Example that demonstrates the detection of deadline misses. This program models a sensor, some + * work done on the sensor data, and an actuator with a deadline. The sensor is your keyboard Return + * or Enter button. The work that is done is simulated every second time to take more than 500 + * microseconds (by sleeping for 500 usecs). Hence, you should see that deadline is missed every * second time you hit Return. * * @author Edward A. Lee diff --git a/C/src/deadlines/PeriodicDeadline.lf b/C/src/deadlines/PeriodicDeadline.lf index 59777a3c..132ed447 100644 --- a/C/src/deadlines/PeriodicDeadline.lf +++ b/C/src/deadlines/PeriodicDeadline.lf @@ -1,50 +1,46 @@ /** - * In a periodic sense-compute-actuate system, we can put a deadline on the actuator - * to specify a real-time requirement. However, a common situation is that there is - * an initialization phase that takes considerable physical time to complete. - * Without some precautions, this can lead to the deadline being violated during - * the first few cycles. - * - * This example illustrates the problem and two solutions. There are three parallel - * subsystems: - * - * 1. This naive system is the only one that should miss deadlines. - * It starts its periodic sensing at the starting logical time, ignoring the - * lengthy time taken by the startup reaction. - * - * 2. This better system introduces an offset in the timer that an estimate - * of the maximum time that the startup reaction is likely to take. - * Only after that offset does it start periodic sensing. - * - * 3. This even better system does not assume any knowledge of how long the - * startup reaction will take. Instead, at the conclusion of the startup - * reaction, it schedules an action that starts the periodic sensing at - * the current physical time. + * In a periodic sense-compute-actuate system, we can put a deadline on the actuator to specify a + * real-time requirement. However, a common situation is that there is an initialization phase that + * takes considerable physical time to complete. Without some precautions, this can lead to the + * deadline being violated during the first few cycles. + * + * This example illustrates the problem and two solutions. There are three parallel subsystems: + * + * 1. This naive system is the only one that should miss deadlines. It starts its periodic sensing + * at the starting logical time, ignoring the lengthy time taken by the startup reaction. + * + * 2. This better system introduces an offset in the timer that an estimate of the maximum time that + * the startup reaction is likely to take. Only after that offset does it start periodic sensing. + * + * 3. This even better system does not assume any knowledge of how long the startup reaction will + * take. Instead, at the conclusion of the startup reaction, it schedules an action that starts the + * periodic sensing at the current physical time. * * @author Edward A. Lee */ target C { - timeout: 5s + timeout: 5 s } reactor Sensor { - output y:int - state count:int = 0 + output y: int + state count: int = 0 } -reactor OffsetSensor(offset:time = 0) extends Sensor { - timer t(offset, 500ms) +reactor OffsetSensor(offset: time = 0) extends Sensor { + timer t(offset, 500 ms) + reaction(startup) {= // Take a long time. lf_sleep(SEC(2)); =} - reaction(t) -> y {= - lf_set(y, self->count++); - =} + + reaction(t) -> y {= lf_set(y, self->count++); =} } reactor StartupSensor extends Sensor { logical action a + reaction(startup) -> a {= // Take a long time. lf_sleep(SEC(2)); @@ -53,6 +49,7 @@ reactor StartupSensor extends Sensor { instant_t offset = lf_time_physical_elapsed(); lf_schedule(a, offset); =} + reaction(a) -> y, a {= lf_set(y, self->count++); lf_schedule(a, MSEC(500)); @@ -69,14 +66,15 @@ reactor Compute { =} } -reactor Actuator(id:int = 1) { +reactor Actuator(id: int = 1) { input x: int + reaction(x) {= lf_print("Actuator %d: Actuating with lag " PRINTF_TIME, self->id, lf_time_physical_elapsed() - lf_time_logical_elapsed() ); - =} deadline(30ms) {= + =} deadline(30 ms) {= lf_print("Actuator %d: **** Deadline missed with lag " PRINTF_TIME, self->id, lf_time_physical_elapsed() - lf_time_logical_elapsed() @@ -85,21 +83,21 @@ reactor Actuator(id:int = 1) { } main reactor { - s1 = new OffsetSensor(offset = 0) + s1 = new OffsetSensor(offset=0) c1 = new Compute() - a1 = new Actuator(id = 1) + a1 = new Actuator(id=1) s1.y -> c1.x c1.y -> a1.x - - s2 = new OffsetSensor(offset = 2s) + + s2 = new OffsetSensor(offset = 2 s) c2 = new Compute() - a2 = new Actuator(id = 2) + a2 = new Actuator(id=2) s2.y -> c2.x c2.y -> a2.x s3 = new StartupSensor() c3 = new Compute() - a3 = new Actuator(id = 3) + a3 = new Actuator(id=3) s3.y -> c3.x c3.y -> a3.x } diff --git a/C/src/keyboard/Keyboard.lf b/C/src/keyboard/Keyboard.lf index cf50a4bc..25e199ea 100644 --- a/C/src/keyboard/Keyboard.lf +++ b/C/src/keyboard/Keyboard.lf @@ -29,74 +29,74 @@ reactor KeyboardInput(exit_key: int = 0) { physical action keypress: int preamble {= - #include "platform.h" - #include - #include - // Thread to read input characters until an EOF is received. - // Each time a character is received, schedule a keypress action. - void* read_input(void* keyboard_input) { - int c; - while((c = getch()) != EOF) { - if (c == ((keyboard_input_t*)keyboard_input)->exit_key) { - lf_request_stop(); - break; + #include "platform.h" + #include + #include + // Thread to read input characters until an EOF is received. + // Each time a character is received, schedule a keypress action. + void* read_input(void* keyboard_input) { + int c; + while((c = getch()) != EOF) { + if (c == ((keyboard_input_t*)keyboard_input)->exit_key) { + lf_request_stop(); + break; + } + lf_schedule_copy(((keyboard_input_t*)keyboard_input)->keypress, 0, &c, 1); } - lf_schedule_copy(((keyboard_input_t*)keyboard_input)->keypress, 0, &c, 1); + return NULL; + } + // Function to direct printed messages to the curses-managed terminal. + void print_to_terminal(const char* format, va_list args) { + static int line_count = 1; + move(line_count++, 0); + vwprintw(stdscr, format, args); + refresh(); + if(line_count >= getmaxy(stdscr)) line_count = 1; + } + // Function for orderly shutdown upon control-c. + void sig_handler(int sig) { + lf_request_stop(); } - return NULL; - } - // Function to direct printed messages to the curses-managed terminal. - void print_to_terminal(const char* format, va_list args) { - static int line_count = 1; - move(line_count++, 0); - vwprintw(stdscr, format, args); - refresh(); - if(line_count >= getmaxy(stdscr)) line_count = 1; - } - // Function for orderly shutdown upon control-c. - void sig_handler(int sig) { - lf_request_stop(); - } =} reaction(startup) -> keypress {= - initscr(); // Initialize the curses library - cbreak(); // Disable line buffering - noecho(); // Disable automatic echoing of typed characters - keypad(stdscr, TRUE); // Enable special keys + initscr(); // Initialize the curses library + cbreak(); // Disable line buffering + noecho(); // Disable automatic echoing of typed characters + keypad(stdscr, TRUE); // Enable special keys - move(0, 0); - if(self->exit_key != 0) { - printw("Type %c to exit.\n", self->exit_key); - } - refresh(); + move(0, 0); + if(self->exit_key != 0) { + printw("Type %c to exit.\n", self->exit_key); + } + refresh(); - // Register a print function handler so lf_print works. - lf_register_print_function(print_to_terminal, LOG_LEVEL_ALL); + // Register a print function handler so lf_print works. + lf_register_print_function(print_to_terminal, LOG_LEVEL_ALL); - if (signal(SIGINT, sig_handler) == SIG_ERR) { - lf_print_warning("Failed to register signal handler. After exit, may have to reset terminal using 'reset'."); - } + if (signal(SIGINT, sig_handler) == SIG_ERR) { + lf_print_warning("Failed to register signal handler. After exit, may have to reset terminal using 'reset'."); + } - static keyboard_input_t keyboard_input; - keyboard_input.keypress = keypress; - keyboard_input.exit_key = self->exit_key; + static keyboard_input_t keyboard_input; + keyboard_input.keypress = keypress; + keyboard_input.exit_key = self->exit_key; - // Start the thread that listens for key presses. - lf_thread_t thread_id; - lf_thread_create(&thread_id, &read_input, &keyboard_input); + // Start the thread that listens for key presses. + lf_thread_t thread_id; + lf_thread_create(&thread_id, &read_input, &keyboard_input); =} reaction(keypress) -> key {= lf_set(key, keypress->value); =} reaction(shutdown) {= - endwin(); - lf_register_print_function(NULL, -1); + endwin(); + lf_register_print_function(NULL, -1); =} } main reactor { - k = new KeyboardInput(exit_key = 120) + k = new KeyboardInput(exit_key=120) reaction(k.key) {= lf_print("You typed %c (keycode %d).", k.key->value, k.key->value); =} } diff --git a/C/src/leader-election/Election.lf b/C/src/leader-election/Election.lf index 06abf4c1..9f2e90ca 100644 --- a/C/src/leader-election/Election.lf +++ b/C/src/leader-election/Election.lf @@ -1,36 +1,31 @@ /** - * The Lingua Franca (LF) program above represents a distributed leader election - * system with three nodes. Each node has an associated unique ID, and their goal - * is to elect the node with the highest ID as the leader. The program consists of - * a federated reactor that connects three Node reactors in a ring topology. Each - * node initially broadcasts its ID, and upon receiving an incoming ID, it compares - * it with its own. If the received ID is greater than its own, the node forwards - * the received ID after a 10 ms delay. If the received ID is equal to the node's - * own ID, it declares itself as the elected leader and transitions to the Elected - * mode. The leader election process continues until the node with the highest ID - * is elected. - * + * The Lingua Franca (LF) program above represents a distributed leader election system with three + * nodes. Each node has an associated unique ID, and their goal is to elect the node with the + * highest ID as the leader. The program consists of a federated reactor that connects three Node + * reactors in a ring topology. Each node initially broadcasts its ID, and upon receiving an + * incoming ID, it compares it with its own. If the received ID is greater than its own, the node + * forwards the received ID after a 10 ms delay. If the received ID is equal to the node's own ID, + * it declares itself as the elected leader and transitions to the Elected mode. The leader election + * process continues until the node with the highest ID is elected. + * * @author Edward A. Lee * @author Julien Brunel * @author David Chemouil */ target C -reactor Node( - id:int(0) -) { - input in:int - output out:int - - logical action a:int - + +reactor Node(id: int = 0) { + input in: int + output out: int + + logical action a: int + initial mode NotElected { - reaction(startup) -> out {= - lf_set(out, self->id); - =} - reaction(a) -> out {= - lf_set(out, a->value); - =} - reaction(in) -> a, Elected {= + reaction(startup) -> out {= lf_set(out, self->id); =} + + reaction(a) -> out {= lf_set(out, a->value); =} + + reaction(in) -> a, reset(Elected) {= if (in->value > self->id) { lf_schedule_int(a, MSEC(10), in->value); } else if (in->value == self->id) { @@ -39,14 +34,15 @@ reactor Node( } =} } + mode Elected { - } } + federated reactor { i0 = new Node() - i1 = new Node(id = 1) - i2 = new Node(id = 2) + i1 = new Node(id=1) + i2 = new Node(id=2) i0.out -> i1.in i1.out -> i2.in i2.out -> i0.in diff --git a/C/src/lib/PoissonClock.lf b/C/src/lib/PoissonClock.lf index 8e5286d9..540215dd 100644 --- a/C/src/lib/PoissonClock.lf +++ b/C/src/lib/PoissonClock.lf @@ -1,31 +1,34 @@ /** - * This reactor produces output events according to a Poisson process. - * The time between events is given by independent and identically - * distributed exponential random variables. Each output is a boolean - * true. The average number of events per second is given by the lambda - * parameter (1/lambda is the mean time between events). - * - * If the seed is set to anything other than 0, then the output sequence - * will be the same each time the program is run. This will be true even - * if there are multiple instances of this reactor. If any two have the - * same seed, then they will produce identical sequences. - * + * This reactor produces output events according to a Poisson process. The time between events is + * given by independent and identically distributed exponential random variables. Each output is a + * boolean true. The average number of events per second is given by the lambda parameter (1/lambda + * is the mean time between events). + * + * If the seed is set to anything other than 0, then the output sequence will be the same each time + * the program is run. This will be true even if there are multiple instances of this reactor. If + * any two have the same seed, then they will produce identical sequences. + * * @author Edward A. Lee */ target C + import Random from "Random.lf" + preamble {= #include =} -reactor PoissonClock(lambda:double(1.0)) extends Random { - output event:bool + +reactor PoissonClock(lambda: double = 1.0) extends Random { + output event: bool logical action a + reaction(startup) -> a {= double delta = exponential(self->lambda); // Convert seconds to nanoseconds. interval_t interval = (interval_t)(delta * SEC(1)); lf_schedule(a, interval); =} + reaction(a) -> event, a {= lf_set(event, true); double delta = exponential(self->lambda); diff --git a/C/src/lib/PrintToFile.lf b/C/src/lib/PrintToFile.lf index a5182325..5a0a80a4 100644 --- a/C/src/lib/PrintToFile.lf +++ b/C/src/lib/PrintToFile.lf @@ -1,21 +1,21 @@ -/** - * Reactor that prints time-value pairs to a file. - */ +/** Reactor that prints time-value pairs to a file. */ target C -reactor PrintToFile(filename:string("output.data")) { - input y:double; - state file:FILE*({=NULL=}); + +reactor PrintToFile(filename: string = "output.data") { + input y: double + state file: FILE* = {= NULL =} + reaction(startup) {= self->file = fopen(self->filename, "w"); if(self->file == NULL) { lf_print_error_and_exit("Failed to open file: %s", self->filename); } =} + reaction(y) {= double t = lf_time_logical_elapsed() / 1.0e9; fprintf(self->file, "%f %f\n", t, y->value); =} - reaction(shutdown) {= - fclose(self->file); - =} + + reaction(shutdown) {= fclose(self->file); =} } diff --git a/C/src/lib/Random.lf b/C/src/lib/Random.lf index aef15f20..b7bb897a 100644 --- a/C/src/lib/Random.lf +++ b/C/src/lib/Random.lf @@ -1,40 +1,35 @@ /** - * This reactor is a base class for reactors that generate - * or use random numbers. The method random() returns an - * integer between 0 and RAND_MAX. The method - * exponential(lambda) returns an exponential random - * variable with rate lambda (expected value 1/lambda). - * - * This reactor ensures that if a seed is set, then - * the reactor returns a repeatable random number sequence - * regardless of how many other reactors are generating - * random numbers. - * - * The seed defaults to 0, which indicates to use the - * starting logical time (or more precisely, the low-order - * 32 bits of the starting logical time) as a seed. - * If any seed other than 0 is given, then the sequence - * of random numbers will be repeatable in the sense that - * every execution of the program will produce the same - * sequence. - * + * This reactor is a base class for reactors that generate or use random numbers. The method + * random() returns an integer between 0 and RAND_MAX. The method exponential(lambda) returns an + * exponential random variable with rate lambda (expected value 1/lambda). + * + * This reactor ensures that if a seed is set, then the reactor returns a repeatable random number + * sequence regardless of how many other reactors are generating random numbers. + * + * The seed defaults to 0, which indicates to use the starting logical time (or more precisely, the + * low-order 32 bits of the starting logical time) as a seed. If any seed other than 0 is given, + * then the sequence of random numbers will be repeatable in the sense that every execution of the + * program will produce the same sequence. + * * @author Edward A. Lee */ target C + preamble {= #include #include =} -reactor Random(seed:{=unsigned int=}(0)) { + +reactor Random(seed: {= unsigned int =} = 0) { reaction(startup) {= if (self->seed == 0) { self->seed = (unsigned int)lf_time_logical(); } =} - method random():int {= - return rand_r(&self->seed); - =} - method exponential(lambda:double):double {= + + method random(): int {= return rand_r(&self->seed); =} + + method exponential(lambda: double): double {= double u = random() / (RAND_MAX + 1.0); return -log(1.0 - u) / lambda; =} diff --git a/C/src/lib/RandomDelay.lf b/C/src/lib/RandomDelay.lf index e5dddeb5..59d9c5d4 100644 --- a/C/src/lib/RandomDelay.lf +++ b/C/src/lib/RandomDelay.lf @@ -1,21 +1,21 @@ /** - * Delay an input value by a random amount given - * by an exponential random variable with average value - * given by average. To make measuring the - * delay convenient, the type of the input is a time + * Delay an input value by a random amount given by an exponential random variable with average + * value given by average. To make measuring the delay convenient, the type of the input is a time * and the same time will be sent to the output. - * + * * @author Edward A. Lee */ target C + import Random from "Random.lf" -reactor RandomDelay(average:time(1 sec)) extends Random { - input in:time - output out:time - logical action a:time - reaction(a) -> out {= - lf_set(out, a->value); - =} + +reactor RandomDelay(average: time = 1 sec) extends Random { + input in: time + output out: time + logical action a: time + + reaction(a) -> out {= lf_set(out, a->value); =} + reaction(in) -> a {= double lambda = SEC(1) / ((double)self->average); double exp = exponential(lambda); diff --git a/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf b/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf index ed812939..1af7c3e3 100644 --- a/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf +++ b/C/src/modal_models/FurutaPendulum/FurutaPendulum.lf @@ -1,19 +1,15 @@ /** - * A simulation of a Furuta pendulum with a modal controller - * based on the Ptolemy II model constructed by Johan Eker - * and described in this paper: - * - * J. Liu, J. Eker, J. W. Janneck, and E. A. Lee, - * “Realistic simulations of embedded control systems,” - * IFAC Proceedings Volumes, vol. 35, no. 1, pp. 391–396, 2002. - * - * This program specifies a build script that only code generates - * and compiles the program, as usual, but also executes the - * program and processes its output to generate and open a - * plot. - * You have to have installed gnuplot and have it in your - * PATH for this script to work as expected (and also cmake). - * + * A simulation of a Furuta pendulum with a modal controller based on the Ptolemy II model + * constructed by Johan Eker and described in this paper: + * + * J. Liu, J. Eker, J. W. Janneck, and E. A. Lee, “Realistic simulations of embedded control + * systems,” IFAC Proceedings Volumes, vol. 35, no. 1, pp. 391–396, 2002. + * + * This program specifies a build script that only code generates and compiles the program, as + * usual, but also executes the program and processes its output to generate and open a plot. You + * have to have installed gnuplot and have it in your PATH for this script to work as expected (and + * also cmake). + * * @author Edward A. Lee * @author Alexander Schulz-Rosengarten */ @@ -23,22 +19,23 @@ target C { flags: "-lm", build: "./build_run_plot.sh FurutaPendulum" } -import PendulumController from "PendulumController.lf"; -import PendulumSimulation from "PendulumSimulation.lf"; -import Print from "Print.lf"; + +import PendulumController from "PendulumController.lf" +import PendulumSimulation from "PendulumSimulation.lf" +import Print from "Print.lf" main reactor { - s = new PendulumSimulation(); - c = new PendulumController(); - p = new Print(); + s = new PendulumSimulation() + c = new PendulumController() + p = new Print() - s.phi, s.d_phi -> c.phi, c.d_phi; - s.theta, s.d_theta -> c.theta, c.d_theta; - c.control -> s.u; + s.phi, s.d_phi -> c.phi, c.d_phi + s.theta, s.d_theta -> c.theta, c.d_theta + c.control -> s.u - c.control -> p.control; - c.modeID -> p.modeID; - c.energy -> p.energy; - s.theta -> p.theta; - s.phi -> p.phi; + c.control -> p.control + c.modeID -> p.modeID + c.energy -> p.energy + s.theta -> p.theta + s.phi -> p.phi } diff --git a/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf b/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf index 389d33a8..48c24c6b 100644 --- a/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf +++ b/C/src/modal_models/FurutaPendulum/FurutaPendulumDisturbance.lf @@ -1,7 +1,7 @@ /** - * This variant of the Furuta pendulum example introduces - * a disturbance to bring the pendulum out of balance. - * + * This variant of the Furuta pendulum example introduces a disturbance to bring the pendulum out of + * balance. + * * @author Edward A. Lee * @author Alexander Schulz-Rosengarten */ @@ -11,28 +11,27 @@ target C { flags: "-lm", build: "./build_run_plot.sh FurutaPendulumDisturbance" } -import PendulumController from "PendulumController.lf"; -import PendulumSimulation from "PendulumSimulation.lf"; -import Print from "Print.lf"; + +import PendulumController from "PendulumController.lf" +import PendulumSimulation from "PendulumSimulation.lf" +import Print from "Print.lf" main reactor { - s = new PendulumSimulation(); - c = new PendulumController(); - p = new Print(); + s = new PendulumSimulation() + c = new PendulumController() + p = new Print() + + timer disturb(3 sec) - timer disturb(3 sec); - - reaction(disturb) -> s.d {= - lf_set(s.d, 0.5); - =} + s.phi, s.d_phi -> c.phi, c.d_phi + s.theta, s.d_theta -> c.theta, c.d_theta + c.control -> s.u - s.phi, s.d_phi -> c.phi, c.d_phi; - s.theta, s.d_theta -> c.theta, c.d_theta; - c.control -> s.u; + c.control -> p.control + c.modeID -> p.modeID + c.energy -> p.energy + s.theta -> p.theta + s.phi -> p.phi - c.control -> p.control; - c.modeID -> p.modeID; - c.energy -> p.energy; - s.theta -> p.theta; - s.phi -> p.phi; + reaction(disturb) -> s.d {= lf_set(s.d, 0.5); =} } diff --git a/C/src/modal_models/FurutaPendulum/PendulumController.lf b/C/src/modal_models/FurutaPendulum/PendulumController.lf index 5acef877..a6ad66b7 100644 --- a/C/src/modal_models/FurutaPendulum/PendulumController.lf +++ b/C/src/modal_models/FurutaPendulum/PendulumController.lf @@ -1,15 +1,14 @@ /** - * A modal controller for a Furuta pendulum, - * based on the Ptolemy II model constructed by Johan Eker + * A modal controller for a Furuta pendulum, based on the Ptolemy II model constructed by Johan Eker * and described in this paper: * - * J. Liu, J. Eker, J. W. Janneck, and E. A. Lee, - * “Realistic simulations of embedded control systems,” - * IFAC Proceedings Volumes, vol. 35, no. 1, pp. 391–396, 2002. - * + * J. Liu, J. Eker, J. W. Janneck, and E. A. Lee, “Realistic simulations of embedded control + * systems,” IFAC Proceedings Volumes, vol. 35, no. 1, pp. 391–396, 2002. + * * @author Edward A. Lee */ -target C; +target C + preamble {= #include #define PI 3.14159265 @@ -21,41 +20,41 @@ preamble {= return((fmod(fabs(theta) + PI, 2 * PI) - PI) * sign(theta)); } =} + reactor PendulumController( - h:double(0.005), // Sample interval - w0:double(6.3), - k:double(0.5), // Energy multiplier to swing up. - n:double(0.5), // Bound on swing up control magnitude. - region1:double(0.1), // Region to exit SwingUp. - region2:double(0.2), // Region to exit Stabilize. - max_speed:double(0.05), // Speed to exit Catch. - ci1:double(-1.04945717118225), - ci2:double(-0.20432286791216), - ci3:double(-0.00735846749875), - ci4:double(-0.00735846749875), - si1:double(-1.70871686211144), - si2:double(-0.30395427746831), - si3:double(-0.03254225945714), - si4:double(-0.05808270221773), - phi2:double(-7.0124562) -) { - input theta:double; - input d_theta:double; - input phi:double; - input d_phi:double; - - output control:double; - output modeID:double; - output energy:double; - - state phi0:double(0.0); - + h: double = 0.005, // Sample interval + w0: double = 6.3, + k: double = 0.5, // Energy multiplier to swing up. + n: double = 0.5, // Bound on swing up control magnitude. + region1: double = 0.1, // Region to exit SwingUp. + region2: double = 0.2, // Region to exit Stabilize. + max_speed: double = 0.05, // Speed to exit Catch. + ci1: double = -1.04945717118225, + ci2: double = -0.20432286791216, + ci3: double = -0.00735846749875, + ci4: double = -0.00735846749875, + si1: double = -1.70871686211144, + si2: double = -0.30395427746831, + si3: double = -0.03254225945714, + si4: double = -0.05808270221773, + phi2: double = -7.0124562) { + input theta: double + input d_theta: double + input phi: double + input d_phi: double + + output control: double + output modeID: double + output energy: double + + state phi0: double = 0.0 + initial mode SwingUp { reaction(theta, d_theta) d_phi -> control, modeID, energy {= double th = restrictAngle(theta->value); - double E = 0.5 - * d_theta->value - * d_theta->value + double E = 0.5 + * d_theta->value + * d_theta->value / (self->w0 * self->w0) + cos(th) - 1.0; double c = sign(d_theta->value * cos(th)); @@ -64,13 +63,14 @@ reactor PendulumController( lf_set(energy, E); lf_set(modeID, -1); =} - reaction(theta) -> Catch {= + + reaction(theta) -> reset(Catch) {= if (fabs(theta->value) < self->region1) { lf_set_mode(Catch); } =} } - + mode Catch { reaction(theta, d_theta, phi, d_phi) -> control, modeID, energy {= double th = restrictAngle(theta->value); @@ -81,14 +81,15 @@ reactor PendulumController( + d_phi->value * self->ci4 )); lf_set(modeID, 0); - double E = 0.5 + double E = 0.5 * d_theta->value * d_theta->value / (self->w0 * self->w0) + cos(th) - 1.0; lf_set(energy, E); =} - reaction(phi, d_phi) -> Stabilize {= + + reaction(phi, d_phi) -> reset(Stabilize) {= if (fabs(d_phi->value) < self->max_speed) { lf_set_mode(Stabilize); self->phi0 = phi->value; @@ -105,15 +106,16 @@ reactor PendulumController( + (phi->value - self->phi0) * self->si3 + d_phi->value * self->si4 )); - double E = 0.5 - * d_theta->value - * d_theta->value + double E = 0.5 + * d_theta->value + * d_theta->value / (self->w0 * self->w0) + cos(th) - 1.0; lf_set(energy, E); lf_set(modeID, 1); =} - reaction(theta) -> SwingUp {= + + reaction(theta) -> reset(SwingUp) {= double th = restrictAngle(theta->value); if (fabs(th) > self->region2) { lf_set_mode(SwingUp); diff --git a/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf b/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf index 0b597ca2..c8b1e7e0 100644 --- a/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf +++ b/C/src/modal_models/FurutaPendulum/PendulumSimulation.lf @@ -1,66 +1,56 @@ -target C; +target C + /** - * A simple forward-Euler simulation of a Furuta pendulum, - * based on the Ptolemy II model constructed by Johan Eker - * and described in this paper: + * A simple forward-Euler simulation of a Furuta pendulum, based on the Ptolemy II model constructed + * by Johan Eker and described in this paper: * - * J. Liu, J. Eker, J. W. Janneck, and E. A. Lee, - * “Realistic simulations of embedded control systems,” - * IFAC Proceedings Volumes, vol. 35, no. 1, pp. 391–396, 2002. - * - * The Ptolemy II model is more accurate because it uses an - * RK-45 solver, but this is adequate for many purposes. - * - * This outputs its state every `sample_period`. - * It updates the state before outputting it - * using the most recently received control input. - * - * The `theta` output is the angle of the pendulum, - * which is 0 when the pendulum is pointing straight up, - * and `d_theta` is its initial angular velocity. - * The `phi` output is the angle of the horizontal - * arm and `d_phi` is its angular velocity. - * - * The `u` input is the control input, which applies - * torque to the arm. The `d` input is an impulsive - * disturbance applied to the pendulum, as if you were - * to tap it with a hard object. When an input is - * received on `d`, its value provides an instantaneous - * increment or decrement to `d_theta`. - * Notice that no output is produced when an input - * is received on `d` unless that input is simultaneous - * with the sampling period. The effect of the disturbance - * will not be seen on the outputs until the next sample, - * as would usually be the case for digital controller. + * J. Liu, J. Eker, J. W. Janneck, and E. A. Lee, “Realistic simulations of embedded control + * systems,” IFAC Proceedings Volumes, vol. 35, no. 1, pp. 391–396, 2002. + * + * The Ptolemy II model is more accurate because it uses an RK-45 solver, but this is adequate for + * many purposes. + * + * This outputs its state every `sample_period`. It updates the state before outputting it using the + * most recently received control input. + * + * The `theta` output is the angle of the pendulum, which is 0 when the pendulum is pointing + * straight up, and `d_theta` is its initial angular velocity. The `phi` output is the angle of the + * horizontal arm and `d_phi` is its angular velocity. + * + * The `u` input is the control input, which applies torque to the arm. The `d` input is an + * impulsive disturbance applied to the pendulum, as if you were to tap it with a hard object. When + * an input is received on `d`, its value provides an instantaneous increment or decrement to + * `d_theta`. Notice that no output is produced when an input is received on `d` unless that input + * is simultaneous with the sampling period. The effect of the disturbance will not be seen on the + * outputs until the next sample, as would usually be the case for digital controller. * * @author Edward A. Lee */ reactor PendulumSimulation( - initial_theta:double(-3.14159), // Initial pendulum angle. - sample_period:time(5 msec), // Sample period. - g:double(9.81), // Acceleration of gravity. - alpha:double(0.00260569), - beta:double(0.05165675), - gamma:double(9.7055e-4), - epsilon:double(0.08103060) -){ + initial_theta: double = -3.14159, // Initial pendulum angle. + sample_period: time = 5 msec, // Sample period. + g: double = 9.81, // Acceleration of gravity. + alpha: double = 0.00260569, + beta: double = 0.05165675, + gamma: double = 9.7055e-4, + epsilon: double = 0.08103060) { preamble {= #include =} - input u:double; // Control input. - input d:double; // Impulsive disturbance - - output theta:double; // Pendulum angle. - output d_theta:double; // Pendulum angular velocity. - output phi:double; // Arm angle. - output d_phi:double; // Arm angular velocity. - - state x:double[4](0.0, 0.0, 0.0, 0.0); // [theta, d_theta, phi, d_phi] - state first:bool(true); - state latest_u:double(0.0); - - timer t(0, sample_period); - + input u: double // Control input. + input d: double // Impulsive disturbance + + output theta: double // Pendulum angle. + output d_theta: double // Pendulum angular velocity. + output phi: double // Arm angle. + output d_phi: double // Arm angular velocity. + + state x: double[4] = {0.0, 0.0, 0.0, 0.0} // [theta, d_theta, phi, d_phi] + state first: bool = true + state latest_u: double = 0.0 + + timer t(0, sample_period) + reaction(t) -> theta, d_theta, phi, d_phi {= if (!self->first) { // Update the state. @@ -95,17 +85,17 @@ reactor PendulumSimulation( * cos(self->x[0]) * self->g * self->latest_u - + + + ( - self->alpha - * self->beta + self->alpha + * self->beta + pow(self->alpha * sin(self->x[0]), 2.0) ) * self->epsilon / self->alpha * sin(self->x[0]) ); double x2_dot = self->x[3]; double x3_dot = (1.0 / ( - self->alpha * self->beta + self->alpha * self->beta + pow(self->alpha * sin(self->x[0]), 2.0) - pow(self->gamma * cos(self->x[0]), 2.0) )) * ( @@ -151,6 +141,7 @@ reactor PendulumSimulation( lf_set(phi, self->x[2]); lf_set(d_phi, self->x[3]); =} + reaction(d) {= // NOTE: If the disturbance is coincident with a sample, // then it won't have any effect on theta until the next sample. @@ -164,7 +155,6 @@ reactor PendulumSimulation( // are implemented in C. self->x[1] += d->value; =} - reaction(u) {= - self->latest_u = u->value; - =} + + reaction(u) {= self->latest_u = u->value; =} } diff --git a/C/src/modal_models/FurutaPendulum/Print.lf b/C/src/modal_models/FurutaPendulum/Print.lf index 85895b84..0c36d602 100644 --- a/C/src/modal_models/FurutaPendulum/Print.lf +++ b/C/src/modal_models/FurutaPendulum/Print.lf @@ -1,21 +1,19 @@ -/** - * A utility reactor to print the pendulum state into a csv file. - */ -target C; +/** A utility reactor to print the pendulum state into a csv file. */ +target C preamble {= #include #define PI 3.14159265 =} -reactor Print(filename:string("pendulum.csv")) { - input control:double; - input modeID:double; - input energy:double; - input theta:double; - input phi:double; +reactor Print(filename: string = "pendulum.csv") { + input control: double + input modeID: double + input energy: double + input theta: double + input phi: double - state file:FILE*({=NULL=}); + state file: FILE* = {= NULL =} reaction(startup) {= self->file = fopen(self->filename, "w"); @@ -39,7 +37,5 @@ reactor Print(filename:string("pendulum.csv")) { ); =} - reaction(shutdown) {= - fclose(self->file); - =} + reaction(shutdown) {= fclose(self->file); =} } diff --git a/C/src/mqtt/MQTTDistributed.lf b/C/src/mqtt/MQTTDistributed.lf index 88b017fd..6152b317 100644 --- a/C/src/mqtt/MQTTDistributed.lf +++ b/C/src/mqtt/MQTTDistributed.lf @@ -1,63 +1,52 @@ /** - * This is a federated LF program consisting of two unconnected federates - * that communicate via MQTT. The publisher has `include_timestamp` set - * to `true`, and the subscriber has `use_physical_time` set to `false`. - * Like `MQTTLogical`, there is no other activity - * in this program, so the subscriber's timestamps will deterministically - * match those of the publisher. Unlike `MQTTLogical`, however, the microstep - * will be zero at the subscriber end. Also, the tags will be deterministic - * at the receiving end regardless of the communication latency because the - * receiving federate has no reason to advance its logical time unless it - * receives an MQTT subscription message. You can change the `use_physical_time` - * parameter of the `MQTTSubscriber` to `true` to get a (nondeterministic) - * physical connection, similar to `MQTTPhysical`. - * - * The code generator produces three programs, bin/MQTTDistributed_RTI, - * bin/MQTTDistributed_source, and bin/MQTTDistributed_destination, - * plus a script bin/MQTTDistributed that runs all three. - * - * Since the source and destination are running in the same - * executable, there is no clock synchronization error. - * + * This is a federated LF program consisting of two unconnected federates that communicate via MQTT. + * The publisher has `include_timestamp` set to `true`, and the subscriber has `use_physical_time` + * set to `false`. Like `MQTTLogical`, there is no other activity in this program, so the + * subscriber's timestamps will deterministically match those of the publisher. Unlike + * `MQTTLogical`, however, the microstep will be zero at the subscriber end. Also, the tags will be + * deterministic at the receiving end regardless of the communication latency because the receiving + * federate has no reason to advance its logical time unless it receives an MQTT subscription + * message. You can change the `use_physical_time` parameter of the `MQTTSubscriber` to `true` to + * get a (nondeterministic) physical connection, similar to `MQTTPhysical`. + * + * The code generator produces three programs, bin/MQTTDistributed_RTI, bin/MQTTDistributed_source, + * and bin/MQTTDistributed_destination, plus a script bin/MQTTDistributed that runs all three. + * + * Since the source and destination are running in the same executable, there is no clock + * synchronization error. + * * See README.md for prerequisites and further information. - * + * * @author Ravi Akella * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", - "include/mosquitto-extension.cmake"], + cmake-include: ["include/paho-extension.cmake", "include/mosquitto-extension.cmake"], timeout: 10 secs, - coordination: centralized, -}; + coordination: centralized +} -import MQTTPublisher from "lib/MQTTPublisher.lf"; -import MQTTSubscriber from "lib/MQTTSubscriber.lf"; -import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; +import MQTTPublisher from "lib/MQTTPublisher.lf" +import MQTTSubscriber from "lib/MQTTSubscriber.lf" +import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf" reactor Source { - msg = new MessageGenerator(root = "Hello World"); - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883", - include_timestamp = true - ); - msg.message->pub.in; + msg = new MessageGenerator(root = "Hello World") + pub = new MQTTPublisher(topic="my/test", address="tcp://localhost:1883", include_timestamp=true) + msg.message -> pub.in } reactor Destination { sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = false, - offset = 0 sec - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + address="tcp://localhost:1883", + topic="my/test", + use_physical_time=false, + offset = 0 sec) + dsp = new PrintMessage() + sub.message -> dsp.message } federated reactor { - source = new Source(); - destination = new Destination(); + source = new Source() + destination = new Destination() } diff --git a/C/src/mqtt/MQTTDistributedActivity.lf b/C/src/mqtt/MQTTDistributedActivity.lf index cb17816c..525b3973 100644 --- a/C/src/mqtt/MQTTDistributedActivity.lf +++ b/C/src/mqtt/MQTTDistributedActivity.lf @@ -1,48 +1,41 @@ /** - * This is a federated LF program consisting of two unconnected federates - * that communicate via MQTT, but where the destination reactor has activity - * that interferes with its ability to use the incoming timestamps from the - * publisher. This program will print a warning each time it receives a - * message. To get rid of the warnings, you can set the `use_physical_time` - * parameter of the `MQTTSubscriber` to true, and then it will not use - * the incoming timestamps (except to measure apparent latency). - * + * This is a federated LF program consisting of two unconnected federates that communicate via MQTT, + * but where the destination reactor has activity that interferes with its ability to use the + * incoming timestamps from the publisher. This program will print a warning each time it receives a + * message. To get rid of the warnings, you can set the `use_physical_time` parameter of the + * `MQTTSubscriber` to true, and then it will not use the incoming timestamps (except to measure + * apparent latency). + * * See README.md for prerequisites and further information. - * + * * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", - "include/mosquitto-extension.cmake"], + cmake-include: ["include/paho-extension.cmake", "include/mosquitto-extension.cmake"], timeout: 10 secs, - coordination: centralized, -}; + coordination: centralized +} -import MQTTPublisher from "lib/MQTTPublisher.lf"; -import MQTTSubscriber from "lib/MQTTSubscriber.lf"; -import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; +import MQTTPublisher from "lib/MQTTPublisher.lf" +import MQTTSubscriber from "lib/MQTTSubscriber.lf" +import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf" reactor Source { - msg = new MessageGenerator(root = "Hello World"); - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883", - include_timestamp = true - ); - msg.message->pub.in; + msg = new MessageGenerator(root = "Hello World") + pub = new MQTTPublisher(topic="my/test", address="tcp://localhost:1883", include_timestamp=true) + msg.message -> pub.in } reactor Destination { timer t(1001 ms, 1 s) sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = false, - offset = 0 sec - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + address="tcp://localhost:1883", + topic="my/test", + use_physical_time=false, + offset = 0 sec) + dsp = new PrintMessage() + sub.message -> dsp.message + reaction(t) {= tag_t tag = lf_tag(); lf_print("Destination: Activity at " PRINTF_TAG, @@ -52,6 +45,6 @@ reactor Destination { } federated reactor { - source = new Source(); - destination = new Destination(); + source = new Source() + destination = new Destination() } diff --git a/C/src/mqtt/MQTTLegacy.lf b/C/src/mqtt/MQTTLegacy.lf index 1c910982..209e70eb 100644 --- a/C/src/mqtt/MQTTLegacy.lf +++ b/C/src/mqtt/MQTTLegacy.lf @@ -1,68 +1,55 @@ /** - * This program illustrates how to interface to a legacy MQTT - * service that has no connection with Lingua Franca. - * The "Publisher" reactor publishes messages every 5s - * on topic "legacy" that any other MQTT application can subscribe - * to. For example, you can subscribe to these messages using + * This program illustrates how to interface to a legacy MQTT service that has no connection with + * Lingua Franca. The "Publisher" reactor publishes messages every 5s on topic "legacy" that any + * other MQTT application can subscribe to. For example, you can subscribe to these messages using * the command-line utility (in another window): - * - * mosquitto_sub -t 'legacy' - * - * You can publish your own messages on this topic using any - * MQTT publisher, such as the command line utility: - * - * mosquitto_pub -t 'legacy' -m '******* My own message!' - * - * This is a federated program, the publisher and subscriber run - * in separate programs. This would work pretty much the same - * way, however, as an unfederated program. To run as an - * unfederated program, add to cmake-include the following file: - * - * "include/net_utils.cmake" - * + * + * mosquitto_sub -t 'legacy' + * + * You can publish your own messages on this topic using any MQTT publisher, such as the command + * line utility: + * + * mosquitto_pub -t 'legacy' -m '******* My own message!' + * + * This is a federated program, the publisher and subscriber run in separate programs. This would + * work pretty much the same way, however, as an unfederated program. To run as an unfederated + * program, add to cmake-include the following file: + * + * "include/net_utils.cmake" + * * and change the `federated` keyword to `main`. - * + * * See README.md for prerequisites and further information. * * @author Edward A. Lee */ target C { - cmake-include: [ - "include/paho-extension.cmake", - "include/mosquitto-extension.cmake"], + cmake-include: ["include/paho-extension.cmake", "include/mosquitto-extension.cmake"], timeout: 5 min, - coordination: centralized, -}; + coordination: centralized +} -import MQTTPublisher from "lib/MQTTPublisher.lf"; -import MQTTSubscriber from "lib/MQTTSubscriber.lf"; -import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; +import MQTTPublisher from "lib/MQTTPublisher.lf" +import MQTTSubscriber from "lib/MQTTSubscriber.lf" +import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf" reactor Publisher { - msg = new MessageGenerator( - root = "Legacy Message", - period = 5 sec - ); - pub = new MQTTPublisher( - topic = "legacy", - address = "tcp://localhost:1883", - include_timestamp = false - ); - msg.message->pub.in; + msg = new MessageGenerator(root = "Legacy Message", period = 5 sec) + pub = new MQTTPublisher(topic="legacy", address="tcp://localhost:1883", include_timestamp=false) + msg.message -> pub.in } reactor Subscriber { sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "legacy", - use_physical_time = true, - offset = 0 sec - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + address="tcp://localhost:1883", + topic="legacy", + use_physical_time=true, + offset = 0 sec) + dsp = new PrintMessage() + sub.message -> dsp.message } federated reactor { - source = new Publisher(); - destination = new Subscriber(); + source = new Publisher() + destination = new Subscriber() } diff --git a/C/src/mqtt/MQTTLogical.lf b/C/src/mqtt/MQTTLogical.lf index 55ba4ffc..3a6afb56 100644 --- a/C/src/mqtt/MQTTLogical.lf +++ b/C/src/mqtt/MQTTLogical.lf @@ -1,47 +1,39 @@ /** - * This program periodically publishes on a topic and, in a different - * part of the program, subscribes to the same topic. The publisher has - * `include_timestamp` set to `true`, and the subscriber has `use_physical_time` - * set to `false`. The program has no other activity, so as long as - * the time between publishing of events is larger than the total - * latency through the MQTT broker, the subscriber will see messages - * one microstep later than the publisher. By setting a positive `offset` - * at the subscriber, you can increase the logical time of the received - * message and get a zero microstep. This gives behavior similar to an - * LF connection with an `after` delay. - * + * This program periodically publishes on a topic and, in a different part of the program, + * subscribes to the same topic. The publisher has `include_timestamp` set to `true`, and the + * subscriber has `use_physical_time` set to `false`. The program has no other activity, so as long + * as the time between publishing of events is larger than the total latency through the MQTT + * broker, the subscriber will see messages one microstep later than the publisher. By setting a + * positive `offset` at the subscriber, you can increase the logical time of the received message + * and get a zero microstep. This gives behavior similar to an LF connection with an `after` delay. + * * See README.md for prerequisites and further information. - * + * * @author Ravi Akella * @author Edward A. Lee */ target C { cmake-include: [ - "include/paho-extension.cmake", // For #include "MQTTClient.h" - "include/net_utils.cmake" // For encode_int64() - ], - timeout: 10 secs, -}; - -import MQTTPublisher from "lib/MQTTPublisher.lf"; -import MQTTSubscriber from "lib/MQTTSubscriber.lf"; -import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; + "include/paho-extension.cmake", // For #include "MQTTClient.h" + // For encode_int64() + "include/net_utils.cmake"], + timeout: 10 secs +} + +import MQTTPublisher from "lib/MQTTPublisher.lf" +import MQTTSubscriber from "lib/MQTTSubscriber.lf" +import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf" main reactor { - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883", - include_timestamp = true - ); - msg = new MessageGenerator(root = "Hello World"); - msg.message->pub.in; - + pub = new MQTTPublisher(topic="my/test", address="tcp://localhost:1883", include_timestamp=true) + msg = new MessageGenerator(root = "Hello World") + msg.message -> pub.in + sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = false, - offset = 0 - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + address="tcp://localhost:1883", + topic="my/test", + use_physical_time=false, + offset=0) + dsp = new PrintMessage() + sub.message -> dsp.message } diff --git a/C/src/mqtt/MQTTPhysical.lf b/C/src/mqtt/MQTTPhysical.lf index f430c621..df455ab7 100644 --- a/C/src/mqtt/MQTTPhysical.lf +++ b/C/src/mqtt/MQTTPhysical.lf @@ -1,43 +1,38 @@ /** - * This program periodically publishes on a topic and, in a different - * part of the program, subscribes to the same topic. The timestamp at the - * receiving end will be nondeterministically determined from the local - * physical clock. The difference between the publisher's logical time - * and the subscriber's logical time is a reasonable measure of the - * latency through the MQTT broker. This gives behavior similar to an - * LF [physical connection](https://www.lf-lang.org/docs/handbook/composing-reactors?target=c#physical-connections). - * + * This program periodically publishes on a topic and, in a different part of the program, + * subscribes to the same topic. The timestamp at the receiving end will be nondeterministically + * determined from the local physical clock. The difference between the publisher's logical time and + * the subscriber's logical time is a reasonable measure of the latency through the MQTT broker. + * This gives behavior similar to an LF [physical + * connection](https://www.lf-lang.org/docs/handbook/composing-reactors?target=c#physical-connections). + * * See README.md for prerequisites and further information. - * + * * @author Ravi Akella * @author Edward A. Lee */ target C { cmake-include: [ - "include/paho-extension.cmake", // For #include "MQTTClient.h" - "include/net_utils.cmake" // For encode_int64() - ], - timeout: 10 secs, -}; - -import MQTTPublisher from "lib/MQTTPublisher.lf"; -import MQTTSubscriber from "lib/MQTTSubscriber.lf"; -import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf"; + "include/paho-extension.cmake", // For #include "MQTTClient.h" + // For encode_int64() + "include/net_utils.cmake"], + timeout: 10 secs +} + +import MQTTPublisher from "lib/MQTTPublisher.lf" +import MQTTSubscriber from "lib/MQTTSubscriber.lf" +import MessageGenerator, PrintMessage from "lib/MQTTTestReactors.lf" main reactor { - pub = new MQTTPublisher( - topic = "my/test", - address = "tcp://localhost:1883" - ); - msg = new MessageGenerator(root = "Hello World"); - msg.message->pub.in; - + pub = new MQTTPublisher(topic="my/test", address="tcp://localhost:1883") + msg = new MessageGenerator(root = "Hello World") + msg.message -> pub.in + sub = new MQTTSubscriber( - address = "tcp://localhost:1883", - topic = "my/test", - use_physical_time = true, - offset = 0 - ); - dsp = new PrintMessage(); - sub.message->dsp.message; + address="tcp://localhost:1883", + topic="my/test", + use_physical_time=true, + offset=0) + dsp = new PrintMessage() + sub.message -> dsp.message } diff --git a/C/src/mqtt/lib/MQTTPublisher.lf b/C/src/mqtt/lib/MQTTPublisher.lf index 7f9cdbbf..4a305fae 100644 --- a/C/src/mqtt/lib/MQTTPublisher.lf +++ b/C/src/mqtt/lib/MQTTPublisher.lf @@ -1,30 +1,25 @@ target C + /** - * Reactor that publishes strings (or arbitrary byte arrays cast - * to `char*`) to a specified MQTT topic. - * - * This publisher ensures in-order delivery messages to - * subscribers. If an attempt is made to send a message before - * the delivery of the previous message has completed, then the reaction - * that sends the message (the reaction to an input 'in') will - * block until the previous delivery has completed. - * - * If `include_timestamp` is true (the default is `false`), - * then two things happen: - * - * 1. The publisher ensures that the message is null terminated by - * adding a null terminator if needed. This ensures that the message - * can be treated as a string at the receiving end. - * 2. The publisher appends to the end of the message the current logical - * time at which the publishing occurs. - * - * This can be useful if the receiving end will be an instance - * of `MQTTSubscriber` in another Lingua Franca program. - * Note that `include_timestamp` *must* be true if an `MQTTSubcriber` - * that subscribes to this topic has its `use_physical_time` parameter - * set to false (its default is `true`). Otherwise, the subscriber - * will issue a warning. - * + * Reactor that publishes strings (or arbitrary byte arrays cast to `char*`) to a specified MQTT + * topic. + * + * This publisher ensures in-order delivery messages to subscribers. If an attempt is made to send a + * message before the delivery of the previous message has completed, then the reaction that sends + * the message (the reaction to an input 'in') will block until the previous delivery has completed. + * + * If `include_timestamp` is true (the default is `false`), then two things happen: + * + * 1. The publisher ensures that the message is null terminated by adding a null terminator if + * needed. This ensures that the message can be treated as a string at the receiving end. 2. The + * publisher appends to the end of the message the current logical time at which the publishing + * occurs. + * + * This can be useful if the receiving end will be an instance of `MQTTSubscriber` in another Lingua + * Franca program. Note that `include_timestamp` *must* be true if an `MQTTSubcriber` that + * subscribes to this topic has its `use_physical_time` parameter set to false (its default is + * `true`). Otherwise, the subscriber will issue a warning. + * * @param address The IP address of the MQTT broker. * @param timeout Timeout for completion of message sending in milliseconds. * @see MQTTSubscriber. @@ -32,31 +27,30 @@ target C * @author Ravi Akella * @author Edward A. Lee */ -reactor MQTTPublisher ( - topic:string("DefaultTopic"), - address:string("tcp://localhost:1883"), - include_timestamp:bool(false), - timeout:time(10 sec) -) { +reactor MQTTPublisher( + topic: string = "DefaultTopic", + address: string = "tcp://localhost:1883", + include_timestamp: bool = false, + timeout: time = 10 sec) { preamble {= #include "MQTTClient.h" #include "core/federated/net_util.h" - + // Count of instances of this reactor so that unique client IDs are generated. static size_t _lf_MQTTPublisher_count = 0; - + // Connection options for the client. // Making this global means that all instances of this reactor have // the same connection options. MQTTClient_connectOptions pub_connect_options = MQTTClient_connectOptions_initializer; - + // Struct type used to keep track of messages in flight between reactions. typedef struct inflight_t { bool message_in_flight; MQTTClient_deliveryToken delivery_token; char* message; } inflight_t; - + // Callback invoked once delivery is complete. void pub_delivered(void *inflight, MQTTClient_deliveryToken dt) { LF_PRINT_LOG("MQTTPublisher: Message with token value %d delivery confirmed\n", dt); @@ -70,36 +64,36 @@ reactor MQTTPublisher ( lf_print_error("\nMQTTPublisher: Connection lost. Cause: %s\n", cause); } =} - + /** - * Input type char* instead of string is used for dynamically - * allocated character arrays (as opposed to static constant strings). + * Input type char* instead of string is used for dynamically allocated character arrays (as + * opposed to static constant strings). */ - input in:char*; - + input in: char* + /** State variable that keeps track of a message in flight. */ - state inflight:inflight_t({={false, 0, NULL}=}); - + state inflight: inflight_t = {= {false, 0, NULL} =} + /** Client ID. This is automatically generated. */ - state clientID:char*({= NULL =}); - + state clientID: char* = {= NULL =} + /** The client object. */ - state client:MQTTClient({=NULL=}); - + state client: MQTTClient = {= NULL =} + /** The message object. */ - state mqtt_msg:MQTTClient_message({=MQTTClient_message_initializer=}); - + state mqtt_msg: MQTTClient_message = {= MQTTClient_message_initializer =} + /** Connect to the broker. Exit if this fails. */ - reaction(startup){= + reaction(startup) {= // In case there are multiple instances of this or the subscriber, enter // a critical section. The Paho MQTT functions are not thread safe. lf_critical_section_enter(); - + // Create a unique ID. if (asprintf(&self->clientID, "LF_MQTTPublisher_%zu", _lf_MQTTPublisher_count++) < 0) { lf_print_error_and_exit("MQTTPublisher: Failed to create client ID."); } - + MQTTClient_create(&self->client, self->address, self->clientID, MQTTCLIENT_PERSISTENCE_NONE, NULL); pub_connect_options.keepAliveInterval = 20; pub_connect_options.cleansession = 1; @@ -111,31 +105,30 @@ reactor MQTTPublisher ( // Second argument is a pointer to context that will be passed to pub_delivered, // which in this case is a pointer to the inflight state variable. MQTTClient_setCallbacks(self->client, &self->inflight, pub_connection_lost, NULL, pub_delivered); - + // Connect to the broker. int rc; // response code. if ((rc = MQTTClient_connect(self->client, &pub_connect_options)) != MQTTCLIENT_SUCCESS) { lf_print_error_and_exit("MQTTPublisher: Failed to connect to MQTT broker.\n" "Perhaps one is not running? Return code: %d", rc); } - + lf_critical_section_exit(); - + LF_PRINT_LOG("MQTTPublisher: connected to broker."); =} - + /** - * React to an input by sending a message with the value of the input as the payload. - * If delivery has not yet completed for a previously sent message, then wait for - * it to complete before proceeding (blocking this reaction). - * This copies the message from the input into a buffer, so the input can - * freed upon return from this reaction. + * React to an input by sending a message with the value of the input as the payload. If delivery + * has not yet completed for a previously sent message, then wait for it to complete before + * proceeding (blocking this reaction). This copies the message from the input into a buffer, so + * the input can freed upon return from this reaction. */ reaction(in) {= // In case there are multiple instances of this or the subscriber, enter // a critical section. The Paho MQTT functions are not thread safe. lf_critical_section_enter(); - + if(self->inflight.message_in_flight) { // Wait for message delivery to be complete. LF_PRINT_LOG("MQTTPublisher: Waiting for confirmation of publication of previous message"); @@ -150,7 +143,7 @@ reactor MQTTPublisher ( } LF_PRINT_LOG("MQTTPublisher: Publishing message: %s", in->value); LF_PRINT_LOG("MQTTPublisher: on topic '%s' for publisher with ClientID: %s", self->topic, self->clientID); - + // Allocate memory for a copy of the message. // The default length is just the length of the incoming message. int length = in->length; @@ -187,25 +180,25 @@ reactor MQTTPublisher ( } self->mqtt_msg.payload = self->inflight.message; self->mqtt_msg.payloadlen = length; - + // QoS 2 means that the message will be delivered exactly once. self->mqtt_msg.qos = 2; - + // Retained messages are held by the server and sent to future new subscribers. // Specify that this message should not be retained. // It will be sent only to subscribers currently subscribed. self->mqtt_msg.retained = 0; - + MQTTClient_publishMessage(self->client, self->topic, &self->mqtt_msg, &self->inflight.delivery_token); self->inflight.message_in_flight = true; - + lf_critical_section_exit(); - + // It is not clear why the following is needed, but the message // does not go out until the next invocation without it. - MQTTClient_yield(); + MQTTClient_yield(); =} - + /** Disconnect the client. */ reaction(shutdown) {= LF_PRINT_LOG("MQTTPublisher: Client ID %s disconnecting.", self->clientID); diff --git a/C/src/mqtt/lib/MQTTSubscriber.lf b/C/src/mqtt/lib/MQTTSubscriber.lf index a0f205bd..dce172cf 100644 --- a/C/src/mqtt/lib/MQTTSubscriber.lf +++ b/C/src/mqtt/lib/MQTTSubscriber.lf @@ -1,42 +1,34 @@ target C + /** - * Reactor that subscribes to a specified MQTT topic on which - * string messages are published. The timestamp of the output - * will depend on the use_physical_time parameter and (if present) - * the timestamp carried by the incoming message. - * - * If `use_physical_time` is `tru`e (the default), then this reactor - * uses the current physical time when the subscription notification - * arrives, plus the `offset`, as the desired output timestamp. - * If the incoming message is carrying a timestamp (the publisher - * is an instance of `MQTTPublisher` with `include_timestamp` set - * to `true), then this reactor measures the *apparent latency* - * (the physical time of arrival minus the timestamp in the message). - * At shutdown, this reactor will report that maximum and average - * apparent latencies. - * - * If `use_physical_time` is `false`, then this reactor - * extracts the publisher's timestamp from the message and adds the - * specified offset to get the desired output timestamp. If there - * is no timestamp on the incoming message, then this prints a - * warning and uses physical time. If the received timestamp equals - * current logical time, then a microstep is added. If the desired - * output timestamp is in the past, then a warning will be printed and - * the tag of the message will be one microstep later than - * the current tag when it arrives. - * - * Note that if the publisher and subscriber are both Lingua Franca - * programs, then the communication behaves a physical connection - * if `use_physical_time` is true (the default). The offset is + * Reactor that subscribes to a specified MQTT topic on which string messages are published. The + * timestamp of the output will depend on the use_physical_time parameter and (if present) the + * timestamp carried by the incoming message. + * + * If `use_physical_time` is `tru`e (the default), then this reactor uses the current physical time + * when the subscription notification arrives, plus the `offset`, as the desired output timestamp. + * If the incoming message is carrying a timestamp (the publisher is an instance of `MQTTPublisher` + * with `include_timestamp` set to `true), then this reactor measures the *apparent latency* (the + * physical time of arrival minus the timestamp in the message). At shutdown, this reactor will + * report that maximum and average apparent latencies. + * + * If `use_physical_time` is `false`, then this reactor extracts the publisher's timestamp from the + * message and adds the specified offset to get the desired output timestamp. If there is no + * timestamp on the incoming message, then this prints a warning and uses physical time. If the + * received timestamp equals current logical time, then a microstep is added. If the desired output + * timestamp is in the past, then a warning will be printed and the tag of the message will be one + * microstep later than the current tag when it arrives. + * + * Note that if the publisher and subscriber are both Lingua Franca programs, then the communication + * behaves a physical connection if `use_physical_time` is true (the default). The offset is * equivalent to an `after` delay. - * - * If `use_physical_time` is false, then the communication attempts - * to behave like a logical connection, but this is not always possible. - * Logical time can advance between when the publisher launches a message, - * sending it to the MQTT broker, and when the subscriber receives it. - * This may make it impossible to match the desired timestamp and will - * result in warning messages being printed. - * + * + * If `use_physical_time` is false, then the communication attempts to behave like a logical + * connection, but this is not always possible. Logical time can advance between when the publisher + * launches a message, sending it to the MQTT broker, and when the subscriber receives it. This may + * make it impossible to match the desired timestamp and will result in warning messages being + * printed. + * * @param address The IP address of the MQTT broker. * @param topic The topic name to which to subscribe. * @param use_physical_time If true, then use physical time (the default). @@ -46,22 +38,21 @@ target C * @author Ravi Akella * @author Edward A. Lee */ -reactor MQTTSubscriber ( - address:string("tcp://localhost:1883"), - topic:string("DefaultTopic"), - use_physical_time:bool(true), - offset:time(0) -) { +reactor MQTTSubscriber( + address: string = "tcp://localhost:1883", + topic: string = "DefaultTopic", + use_physical_time: bool = true, + offset: time = 0) { preamble {= #include "MQTTClient.h" #include "core/federated/net_util.h" - + // Fix the QoS to indicate that the message will be delivered reliably exactly once. #define QOS 2 // Count of instances of this reactor so that unique client IDs are generated. static size_t _lf_MQTTSubscriber_count = 0; - + typedef struct MQTTSubscriber_info_t { void* logical_action; interval_t offset; @@ -70,12 +61,12 @@ reactor MQTTSubscriber ( interval_t max_latency; size_t count; } MQTTSubscriber_info_t; - + // Connection options for the client. // Making this global means that all instances of this reactor have // the same connection options. MQTTClient_connectOptions sub_connect_options = MQTTClient_connectOptions_initializer; - + // Callback function invoked by MQTT when a message arrives. int message_arrived( void *info, @@ -89,17 +80,17 @@ reactor MQTTSubscriber ( LF_PRINT_LOG( "MQTTSubscriber: Message arrived on topic %s: %s", topic_name, (char*)message->payload ); - + MQTTSubscriber_info_t* my_info = (MQTTSubscriber_info_t*)info; - + // Enter a critical section so that logical time does not elapse while // we calculate the delay to the logical time for the message. lf_critical_section_enter(); - + interval_t delay; instant_t current_time = lf_time_logical(); interval_t offset = my_info->offset; - + // Extract the publisher's timestamp from the message, if it is present. if ( // Is the string null terminated? @@ -108,15 +99,15 @@ reactor MQTTSubscriber ( && memcmp("LFts", &message->payload[message->payloadlen - sizeof(instant_t) - 4], 4) == 0 ) { my_info->count++; - + instant_t timestamp = extract_int64( (unsigned char*)message->payload + message->payloadlen - sizeof(instant_t) ); instant_t physical_time = lf_time_physical(); - + interval_t latency = physical_time - timestamp; my_info->latencies += latency; - + if (latency > my_info->max_latency) { my_info->max_latency = latency; } @@ -153,52 +144,50 @@ reactor MQTTSubscriber ( message->payloadlen ); } - + LF_PRINT_LOG( "MQTTSubscriber: Received message. Timestamp will be " PRINTF_TIME " ahead of current (elapsed) time, " PRINTF_TIME, delay, current_time - lf_time_start() ); - + lf_critical_section_exit(); // MQTTClient_freeMessage() also frees the memory allocated to the payload, // which is why we have to copy the message here. MQTTClient_freeMessage(&message); MQTTClient_free(topic_name); - + // Return true to indicate that the message has been successfully handled. return 1; } - + /** Callback invoked if the connection is lost. */ void sub_connection_lost(void *info, char *cause) { lf_print_warning("MQTTSubscriber: Connection lost. Cause: %s", cause); } =} - + /** - * Output for sending the incoming MQTT message. - * Use type char* rather than string because it is not - * a static string, but rather dynamically allocated memory. + * Output for sending the incoming MQTT message. Use type char* rather than string because it is + * not a static string, but rather dynamically allocated memory. */ - output message:char*; + output message: char* /** - * Action that is triggered when there is an incoming MQTT message. - * Use a logical action here so that the callback function can - * precisely control timestamp of the received message. + * Action that is triggered when there is an incoming MQTT message. Use a logical action here so + * that the callback function can precisely control timestamp of the received message. */ - logical action act:char*; - + logical action act: char* + /** Client ID. This is automatically generated. */ - state clientID:char*({= NULL =}); + state clientID: char* = {= NULL =} /** State variable storing the MQTT client created for each instance of this reactor. */ - state client:MQTTClient({=NULL=}); - + state client: MQTTClient = {= NULL =} + /** Struct containing the action and offset. */ - state info:MQTTSubscriber_info_t({= {NULL, 0LL, false, 0LL, 0LL, 0} =}) - + state info: MQTTSubscriber_info_t = {= {NULL, 0LL, false, 0LL, 0LL, 0} =} + reaction(startup) -> act {= int rc; // response code. @@ -220,17 +209,17 @@ reactor MQTTSubscriber ( sub_connect_options.keepAliveInterval = 20; sub_connect_options.cleansession = 1; - + self->info.logical_action = act; self->info.offset = self->offset; self->info.use_physical_time = self->use_physical_time; - + // Set up callback functions. // Last argument should be a pointer to a function to // handle notification of delivery of a sent message. // But this reactor isn't sending any messages. MQTTClient_setCallbacks(self->client, &self->info, sub_connection_lost, message_arrived, NULL); - + // Connect to the broker. rc = MQTTClient_connect(self->client, &sub_connect_options); if (rc != MQTTCLIENT_SUCCESS) { @@ -238,19 +227,19 @@ reactor MQTTSubscriber ( "MQTTSubscriber: Failed to connect to MQTT broker.\n" "Perhaps one is not running? Return code: %d\n", rc); } - + MQTTClient_subscribe(self->client, self->topic, QOS); - + lf_critical_section_exit(); =} - + reaction(act) -> message {= // The action contains a token that we can just forward. // The allocated memory will be freed when the token's reference count hits 0. // Note that this token will still contain the publisher's timestamp. lf_set_token(message, act->token); =} - + reaction(shutdown) {= if (self->info.count > 0) { lf_print( diff --git a/C/src/mqtt/lib/MQTTTestReactors.lf b/C/src/mqtt/lib/MQTTTestReactors.lf index 0aed0127..754bfbf0 100644 --- a/C/src/mqtt/lib/MQTTTestReactors.lf +++ b/C/src/mqtt/lib/MQTTTestReactors.lf @@ -1,26 +1,26 @@ /** * Reactors used to test MQTT publishing and subscribing. - * + * * @author Ravi Akella * @author Edward A. Lee */ target C - + /** - * Reactor that generates a sequence of messages, one per second. - * The message will be a string consisting of a root string followed - * by a count. + * Reactor that generates a sequence of messages, one per second. The message will be a string + * consisting of a root string followed by a count. * @param root The root string. * @output message The message. */ -reactor MessageGenerator(root:string(""), period:time(1 sec)) { +reactor MessageGenerator(root: string = "", period: time = 1 sec) { // Output type char* instead of string is used for dynamically // allocated character arrays (as opposed to static constant strings). - output message:char*; - state count:int(1); + output message: char* + state count: int = 1 // Send first message after 1 sec so that the startup reactions // do not factor into the transport time measurement on the first message. - timer t(1 sec, period); + timer t(1 sec, period) + reaction(t) -> message {= // With NULL, 0 arguments, snprintf tells us how many bytes are needed. // Add one for the null terminator. @@ -36,14 +36,15 @@ reactor MessageGenerator(root:string(""), period:time(1 sec)) { ); =} } - + /** * Reactor that prints an incoming string. * @param prefix A prefix for the message. * @input message The message. */ reactor PrintMessage { - input message:char*; + input message: char* + reaction(message) {= tag_t tag = lf_tag(); lf_print("PrintMessage: At (elapsed) time " PRINTF_TAG ", subscriber receives: %s", diff --git a/C/src/patterns/Chain_01_SendReceive.lf b/C/src/patterns/Chain_01_SendReceive.lf index 6395ad08..c5d53102 100644 --- a/C/src/patterns/Chain_01_SendReceive.lf +++ b/C/src/patterns/Chain_01_SendReceive.lf @@ -1,15 +1,15 @@ /** - * Very basic chain with just two reactors, one that sends - * a message a startup feeding one that reports receiving it. - * + * Very basic chain with just two reactors, one that sends a message a startup feeding one that + * reports receiving it. + * * @author Edward A. Lee */ -target C; +target C -import SendOnce, Receive from "lib/SendersAndReceivers.lf"; +import SendOnce, Receive from "lib/SendersAndReceivers.lf" main reactor { - r1 = new SendOnce(); - r2 = new Receive(); - r1.out -> r2.in; + r1 = new SendOnce() + r2 = new Receive() + r1.out -> r2.in } diff --git a/C/src/patterns/Chain_02_Pipeline.lf b/C/src/patterns/Chain_02_Pipeline.lf index 97bb3497..d19312ce 100644 --- a/C/src/patterns/Chain_02_Pipeline.lf +++ b/C/src/patterns/Chain_02_Pipeline.lf @@ -1,32 +1,27 @@ /** - * Basic pipeline pattern where a periodic source feeds - * a chain of reactors that can all execute in parallel - * at each logical time step. - * - * The threads argument specifies the number of worker - * threads, which enables the reactors in the chain to - * execute on multiple cores simultaneously. - * - * This uses the TakeTime reactor to perform computation - * (it computes Fibonacci numbers). If you reduce the - * number of worker threads to 1, the execution time - * will be approximately four times as long. - * + * Basic pipeline pattern where a periodic source feeds a chain of reactors that can all execute in + * parallel at each logical time step. + * + * The threads argument specifies the number of worker threads, which enables the reactors in the + * chain to execute on multiple cores simultaneously. + * + * This uses the TakeTime reactor to perform computation (it computes Fibonacci numbers). If you + * reduce the number of worker threads to 1, the execution time will be approximately four times as + * long. + * * @author Edward A. Lee */ target C { workers: 4, timeout: 1 sec } - -import SendCount, Receive from "lib/SendersAndReceivers.lf"; -import TakeTime from "lib/TakeTime.lf"; + +import SendCount, Receive from "lib/SendersAndReceivers.lf" +import TakeTime from "lib/TakeTime.lf" main reactor { - r0 = new SendCount(period = 100 msec); - rp = new[4] TakeTime(approximate_time = 100 msec); - r5 = new Receive(); - r0.out, rp.out -> rp.in, r5.in after 100 msec; + r0 = new SendCount(period = 100 msec) + rp = new[4] TakeTime(approximate_time = 100 msec) + r5 = new Receive() + r0.out, rp.out -> rp.in, r5.in after 100 msec } - - \ No newline at end of file diff --git a/C/src/patterns/FullyConnected_00_Broadcast.lf b/C/src/patterns/FullyConnected_00_Broadcast.lf index 3563bff2..32d72c06 100644 --- a/C/src/patterns/FullyConnected_00_Broadcast.lf +++ b/C/src/patterns/FullyConnected_00_Broadcast.lf @@ -1,41 +1,37 @@ /** - * This illustrates bank of reactors where each reactor produces an - * output that is broadcast to all reactors in the bank, including - * itself. - * + * This illustrates bank of reactors where each reactor produces an output that is broadcast to all + * reactors in the bank, including itself. + * * @author Christian Menard * @author Edward A. Lee */ -target C; +target C -reactor Node( - num_nodes: size_t(4), - bank_index: int(0) -) { - input[num_nodes] in: int; - output out: int; - - state received: bool(false); - - reaction (startup) -> out{= - lf_print("Hello from node %d!", self->bank_index); - // broadcast my ID to everyone - lf_set(out, self->bank_index); +reactor Node(num_nodes: size_t = 4, bank_index: int = 0) { + input[num_nodes] in: int + output out: int + + state received: bool = false + + reaction(startup) -> out {= + lf_print("Hello from node %d!", self->bank_index); + // broadcast my ID to everyone + lf_set(out, self->bank_index); =} - - reaction (in) {= + + reaction(in) {= printf("Node %d received messages from ", self->bank_index); for (int i = 0; i < in_width; i++) { if (in[i]->is_present) { self->received = true; printf("%d, ", in[i]->value); } - } + } printf("\n"); =} } -main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - (nodes.out)+ -> nodes.in; +main reactor(num_nodes: size_t = 4) { + nodes = new[num_nodes] Node(num_nodes=num_nodes) + (nodes.out)+ -> nodes.in } diff --git a/C/src/patterns/FullyConnected_01_Addressable.lf b/C/src/patterns/FullyConnected_01_Addressable.lf index 0c524e7d..445548a9 100644 --- a/C/src/patterns/FullyConnected_01_Addressable.lf +++ b/C/src/patterns/FullyConnected_01_Addressable.lf @@ -1,42 +1,36 @@ /** - * This illustrates bank of reactors where each reactor produces an - * output that is sent to a reactor in the bank of its choice. In this - * particular example, each reactor chooses to send its output to the - * reactor with the next higher bank_index, wrapping around when it gets - * to the end of the bank. - * + * This illustrates bank of reactors where each reactor produces an output that is sent to a reactor + * in the bank of its choice. In this particular example, each reactor chooses to send its output to + * the reactor with the next higher bank_index, wrapping around when it gets to the end of the bank. + * * @author Christian Menard * @author Edward A. Lee */ // In this pattern, each node can send direct messages to individual other nodes +target C -target C; +reactor Node(num_nodes: size_t = 4, bank_index: int = 0) { + input[num_nodes] in: int + output[num_nodes] out: int -reactor Node( - num_nodes: size_t(4), - bank_index: int(0) -) { - input[num_nodes] in: int; - output[num_nodes] out: int; - - reaction (startup) -> out{= - lf_print("Hello from node %d!", self->bank_index); - // broadcast my ID to everyone - lf_set(out[(self->bank_index + 1) % self->num_nodes], self->bank_index); + reaction(startup) -> out {= + lf_print("Hello from node %d!", self->bank_index); + // broadcast my ID to everyone + lf_set(out[(self->bank_index + 1) % self->num_nodes], self->bank_index); =} - - reaction (in) {= + + reaction(in) {= for (int i = 0; i < in_width; i++) { if (in[i]->is_present) { lf_print("Node %d received %d on channel %d.", self->bank_index, in[i]->value, i ); } - } + } =} } -main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - nodes.out -> interleaved(nodes.in); +main reactor(num_nodes: size_t = 4) { + nodes = new[num_nodes] Node(num_nodes=num_nodes) + nodes.out -> interleaved(nodes.in) } diff --git a/C/src/patterns/Loop_01_Single.lf b/C/src/patterns/Loop_01_Single.lf index 27039160..4e5455a1 100644 --- a/C/src/patterns/Loop_01_Single.lf +++ b/C/src/patterns/Loop_01_Single.lf @@ -1,20 +1,18 @@ /** - * A single reactor sends data to itself. - * The receiving reaction has to be appear lexically later - * in the source code, so that the sending reaction has - * higher priority, or else a causality loop arises. - * You can observe the causality loop by replacing the - * imported reactor with ReceiveAndSendPeriodically. - * + * A single reactor sends data to itself. The receiving reaction has to be appear lexically later in + * the source code, so that the sending reaction has higher priority, or else a causality loop + * arises. You can observe the causality loop by replacing the imported reactor with + * ReceiveAndSendPeriodically. + * * @author Edward A. Lee */ target C { timeout: 5 sec -}; +} -import SendPeriodicallyAndReceive from "lib/SendersAndReceivers.lf"; +import SendPeriodicallyAndReceive from "lib/SendersAndReceivers.lf" main reactor { - r1 = new SendPeriodicallyAndReceive(); - r1.out -> r1.in; + r1 = new SendPeriodicallyAndReceive() + r1.out -> r1.in } diff --git a/C/src/patterns/Loop_02_SingleDelay.lf b/C/src/patterns/Loop_02_SingleDelay.lf index 2be5e599..eeff2933 100644 --- a/C/src/patterns/Loop_02_SingleDelay.lf +++ b/C/src/patterns/Loop_02_SingleDelay.lf @@ -1,24 +1,19 @@ /** - * A single reactor sends data to itself with delay. - * Unlike Loop_01_Single.lf, this can have reactions - * appear in reverse order, where the receiving reaction - * occurs before the sending reaction. This is because the - * delay of a single microstep breaks the causality loop. - * - * Note that the received values are reported at microstep 1 - * rather than 0. - * + * A single reactor sends data to itself with delay. Unlike Loop_01_Single.lf, this can have + * reactions appear in reverse order, where the receiving reaction occurs before the sending + * reaction. This is because the delay of a single microstep breaks the causality loop. + * + * Note that the received values are reported at microstep 1 rather than 0. + * * @author Edward A. Lee */ target C { timeout: 5 sec -}; +} -import ReceiveAndSendPeriodically from "lib/SendersAndReceivers.lf"; +import ReceiveAndSendPeriodically from "lib/SendersAndReceivers.lf" -main reactor ( - a_1_1:time(0) -) { - r1 = new ReceiveAndSendPeriodically(); - r1.out -> r1.in after a_1_1; +main reactor(a_1_1: time = 0) { + r1 = new ReceiveAndSendPeriodically() + r1.out -> r1.in after a_1_1 } diff --git a/C/src/patterns/lib/SendersAndReceivers.lf b/C/src/patterns/lib/SendersAndReceivers.lf index f36a7ea6..bc706b13 100644 --- a/C/src/patterns/lib/SendersAndReceivers.lf +++ b/C/src/patterns/lib/SendersAndReceivers.lf @@ -1,49 +1,40 @@ /** * Library of reactors that are reused in various design pattern examples. - * + * * @author Edward A. Lee */ -target C; +target C -/** - * Send an output (42) at startup. - */ +/** Send an output (42) at startup. */ reactor SendOnce { - output out:int; - reaction(startup) -> out {= - lf_set(out, 42); - =} + output out: int + + reaction(startup) -> out {= lf_set(out, 42); =} } /** * Send counting sequence periodically. - * + * * @param offset The starting time. * @param period The period. * @param start The first output. * @param increment The increment between outputs */ -reactor SendCount( - offset:time(0), - period:time(1 sec), - start:int(0), - increment:int(1) -) { - state count:int(start); - output out:int; - timer t(offset, period); +reactor SendCount(offset: time = 0, period: time = 1 sec, start: int = 0, increment: int = 1) { + state count: int = start + output out: int + timer t(offset, period) + reaction(t) -> out {= lf_set(out, self->count); self->count += self->increment; =} } -/** - * Receive an input and report the elpased logical tag - * and the value of the input. - */ +/** Receive an input and report the elpased logical tag and the value of the input. */ reactor Receive { - input in:int; + input in: int + reaction(in) {= lf_print("At elapsed tag (%lld, %d), received %d.", lf_time_logical_elapsed(), lf_tag().microstep, @@ -53,18 +44,18 @@ reactor Receive { } reactor ReceiveAndSend { - input in:int; - output out:int; - reaction(in) -> out {= - lf_set(out, in->value); - =} + input in: int + output out: int + + reaction(in) -> out {= lf_set(out, in->value); =} } + reactor SendOnceAndReceive { - input in:int; - output out:int; - reaction(startup) -> out {= - lf_set(out, 42); - =} + input in: int + output out: int + + reaction(startup) -> out {= lf_set(out, 42); =} + reaction(in) {= lf_print("At tag (%lld, %d), received %d.", lf_time_logical_elapsed(), @@ -75,64 +66,62 @@ reactor SendOnceAndReceive { } /** - * This reactor periodically increments its state and sends it out. - * When an input is received, it simply reports the value. - * + * This reactor periodically increments its state and sends it out. When an input is received, it + * simply reports the value. + * * @param offset The time of the first output. * @param period The period of the outputs. * @param start The initial output value. * @param increment The increment between outputs. - * + * * @input in The input to report. * @output out The counting output. - * + * * @label Increment the state, send, report received. */ reactor SendPeriodicallyAndReceive extends SendCount, Receive { - // This reactor simply composes SendCount and Receive, in that order. } /** - * This reactor periodically increments its state and sends it out. - * When an input is received, it simply reports the value. - * + * This reactor periodically increments its state and sends it out. When an input is received, it + * simply reports the value. + * * @param offset The time of the first output. * @param period The period of the outputs. * @param start The initial output value. * @param increment The increment between outputs. - * + * * @input in The input to report. * @output out The counting output. - * + * * @label Increment the state, send, report received. */ reactor ReceiveAndSendPeriodically extends Receive, SendCount { - // This reactor simply composes Receive and SendCount, in that order. } /** - * This reactor periodically increments its state and sends it out. - * When an input is received, it simply reports the value. + * This reactor periodically increments its state and sends it out. When an input is received, it + * simply reports the value. * @label Increment the state, send, report received. */ -reactor SendPeriodicallyAndReceiveMultiport ( - offset:time(0), - period:time(1 sec), - start:int(0), - increment:int(1), - width:int(4) -) { - input[width] in:int; - output out:int; - - timer t(offset, period); - - state count:int(start); - +reactor SendPeriodicallyAndReceiveMultiport( + offset: time = 0, + period: time = 1 sec, + start: int = 0, + increment: int = 1, + width: int = 4) { + input[width] in: int + output out: int + + timer t(offset, period) + + state count: int = start + reaction(t) -> out {= lf_set(out, self->count); self->count += self->increment; =} + reaction(in) {= lf_print("At tag (%lld, %d), received:", lf_time_logical_elapsed(), lf_tag().microstep @@ -144,26 +133,23 @@ reactor SendPeriodicallyAndReceiveMultiport ( } /** - * This reactor maintains a local state that periodically incremented - * and incremented whenever an input arrives. + * This reactor maintains a local state that periodically incremented and incremented whenever an + * input arrives. * @label Increment the state, send increment, accept increment. */ -reactor LocalRemoteUpdates( - offset:time(0), - period:time(1 sec), - increment:int(1) -) { - input in:int; - output out:int; - - timer t(offset, period); - - state count:int(0); - +reactor LocalRemoteUpdates(offset: time = 0, period: time = 1 sec, increment: int = 1) { + input in: int + output out: int + + timer t(offset, period) + + state count: int = 0 + reaction(t) -> out {= lf_set(out, self->increment); self->count += self->increment; =} + reaction(in) {= self->count += in->value; lf_print("At tag (%lld, %d), count is %d", @@ -172,34 +158,24 @@ reactor LocalRemoteUpdates( ); =} } + // @label Accumulate local/remote increments, locally query. -reactor SendAndReceiveWithLocalQuery( - query_offset:time(0), - query_period:time(1 sec) -) extends LocalRemoteUpdates { - local_query = new SendPeriodicallyAndReceive( - offset = query_offset, - period = query_period - ); - reaction(local_query.out) -> local_query.in {= - lf_set(local_query.in, self->count); - =} +reactor SendAndReceiveWithLocalQuery(query_offset: time = 0, query_period: time = 1 sec) + extends LocalRemoteUpdates { + local_query = new SendPeriodicallyAndReceive(offset=query_offset, period=query_period) + + reaction(local_query.out) -> local_query.in {= lf_set(local_query.in, self->count); =} } + // @label Accumulate local/remote increments, delayed query. reactor SendAndReceiveWithDelayedQuery( - query_offset:time(0), - query_period:time(1 sec), - query_delay:time(10 msec) -) extends LocalRemoteUpdates { - local_query = new SendPeriodicallyAndReceive( - offset = query_offset, - period = query_period - ); - logical action a(query_delay); - reaction(local_query.out) -> a {= - lf_schedule(a, 0); - =} - reaction(a) -> local_query.in {= - lf_set(local_query.in, self->count); - =} + query_offset: time = 0, + query_period: time = 1 sec, + query_delay: time = 10 msec) extends LocalRemoteUpdates { + local_query = new SendPeriodicallyAndReceive(offset=query_offset, period=query_period) + logical action a(query_delay) + + reaction(local_query.out) -> a {= lf_schedule(a, 0); =} + + reaction(a) -> local_query.in {= lf_set(local_query.in, self->count); =} } diff --git a/C/src/patterns/lib/TakeTime.lf b/C/src/patterns/lib/TakeTime.lf index 5c3f3440..9946ef49 100644 --- a/C/src/patterns/lib/TakeTime.lf +++ b/C/src/patterns/lib/TakeTime.lf @@ -1,25 +1,21 @@ -target C; +target C + /** - * Compute Fibonacci numbers until at least the specified - * physical time has passed. This will compute numbers until - * they overflow the 64-bit representation, and then start over. - * When at least the specified physical time has elapased, - * report the last number computed. - * This is used in some design patterns to provide - * significant computation. - * - * @param approximate_time The approximate amount of physical - * time to take for each input. - * + * Compute Fibonacci numbers until at least the specified physical time has passed. This will + * compute numbers until they overflow the 64-bit representation, and then start over. When at least + * the specified physical time has elapased, report the last number computed. This is used in some + * design patterns to provide significant computation. + * + * @param approximate_time The approximate amount of physical time to take for each input. + * * @input in A triggering input. - * - * @output out The computed Fibonacci number. + * + * @output out The computed Fibonacci number. */ -reactor TakeTime( - approximate_time:time(100 msec) -) { - input in:int; - output out:int; +reactor TakeTime(approximate_time: time = 100 msec) { + input in: int + output out: int + reaction(in) -> out {= instant_t start_time = lf_time_physical(); int f0 = 0; diff --git a/C/src/rhythm/PlayWaveform.lf b/C/src/rhythm/PlayWaveform.lf index 57750094..4840e4b7 100644 --- a/C/src/rhythm/PlayWaveform.lf +++ b/C/src/rhythm/PlayWaveform.lf @@ -1,107 +1,93 @@ /** - * @brief Reactor to play a waveform defined in a .wav file. - * This serves as a demonstration for how to write Lingua Franca - * programs that use Apple's AudioToolbox. - * - * To use this, you must include the target parameters that are - * given below. - * - * This reactor provides a small collection of built-in audio - * waveforms which are read at startup time from .wav files. - * The waveform input specifies which of the waveforms to play - * upon the next `note` input received. - * It is a number between 0 and NUM_WAVEFORMS. If a number outside - * this range is received, then simple tick sounds will be produced. - * Number 0 is specially interpreted for silence. + * @brief Reactor to play a waveform defined in a .wav file. This serves as a demonstration for how + * to write Lingua Franca programs that use Apple's AudioToolbox. + * + * To use this, you must include the target parameters that are given below. + * + * This reactor provides a small collection of built-in audio waveforms which are read at startup + * time from .wav files. The waveform input specifies which of the waveforms to play upon the next + * `note` input received. It is a number between 0 and NUM_WAVEFORMS. If a number outside this range + * is received, then simple tick sounds will be produced. Number 0 is specially interpreted for + * silence. + * + * The `note` input is a number, normally between 0.0 and 1.0, that specifies the loudness of the + * note. If the loudness exceeds 1.0, or if too many notes are played at once, clipping may occur. + * + * The sound files come from here: https://freewavesamples.com + * + * Sound files are assumed to be wav files with sample rate 44,100, 16-bit samples, linear PCM + * encoded. Use afconvert on Mac to convert to the assumed input format. * - * The `note` input is a number, normally between 0.0 and 1.0, - * that specifies the loudness of the note. If the loudness exceeds - * 1.0, or if too many notes are played at once, clipping may occur. - * - * The sound files come from here: - * https://freewavesamples.com - * - * Sound files are assumed to be wav files with sample rate 44,100, - * 16-bit samples, linear PCM encoded. - * Use afconvert on Mac to convert to the assumed input format. - * * @author Edward A. Lee */ target C { files: [ - "/lib/c/reactor-c/util/wave_file_reader.c", - "/lib/c/reactor-c/util/wave_file_reader.h", - "/lib/c/reactor-c/util/audio_loop_mac.c", - "/lib/c/reactor-c/util/audio_loop.h", - "/lib/c/reactor-c/util/audio_loop_linux.c", - ], + "/lib/c/reactor-c/util/wave_file_reader.c", + "/lib/c/reactor-c/util/wave_file_reader.h", + "/lib/c/reactor-c/util/audio_loop_mac.c", + "/lib/c/reactor-c/util/audio_loop.h", + "/lib/c/reactor-c/util/audio_loop_linux.c"], cmake-include: [ - "/lib/c/reactor-c/util/audio_loop.cmake", - "/lib/c/reactor-c/util/wave_file_reader.cmake" - ] + "/lib/c/reactor-c/util/audio_loop.cmake", + "/lib/c/reactor-c/util/wave_file_reader.cmake"] } - -/** - * Produce a note when a `note` input is received. - */ -reactor PlayWaveform ( - default_waveform_id:int(0) // Silent waveform -) { - preamble {= + +/** Produce a note when a `note` input is received. */ +reactor PlayWaveform( + // Silent waveform + default_waveform_id: int = 0) { + preamble {= #include // Defines strlen() - #include "audio_loop.h" - #include "wave_file_reader.h" - - // wav files giving the waveforms. - // These have to also be included in the files target directive. - #define NUM_WAVEFORMS 9 // Number of waveforms. - #define SOUNDS LF_PACKAGE_DIRECTORY LF_FILE_SEPARATOR "src" LF_FILE_SEPARATOR "rhythm" LF_FILE_SEPARATOR "sounds" LF_FILE_SEPARATOR - char* waveform_files[] = { - SOUNDS "Bass-Drum-1.wav", - SOUNDS "Hi-Bongo.wav", - SOUNDS "Claves.wav", - SOUNDS "High-Conga-1.wav", - SOUNDS "Cowbell-1.wav", - SOUNDS "Cuica-1.wav", - SOUNDS "Guiro.wav", - SOUNDS "Ensoniq-ESQ-1-Snare.wav", - SOUNDS "Floor-Tom-1.wav" - }; - - // The waveforms themselves. - lf_waveform_t* waveforms[NUM_WAVEFORMS + 1]; - - lf_waveform_t empty_waveform = { 0 }; + #include "audio_loop.h" + #include "wave_file_reader.h" + + // wav files giving the waveforms. + // These have to also be included in the files target directive. + #define NUM_WAVEFORMS 9 // Number of waveforms. + #define SOUNDS LF_PACKAGE_DIRECTORY LF_FILE_SEPARATOR "src" LF_FILE_SEPARATOR "rhythm" LF_FILE_SEPARATOR "sounds" LF_FILE_SEPARATOR + char* waveform_files[] = { + SOUNDS "Bass-Drum-1.wav", + SOUNDS "Hi-Bongo.wav", + SOUNDS "Claves.wav", + SOUNDS "High-Conga-1.wav", + SOUNDS "Cowbell-1.wav", + SOUNDS "Cuica-1.wav", + SOUNDS "Guiro.wav", + SOUNDS "Ensoniq-ESQ-1-Snare.wav", + SOUNDS "Floor-Tom-1.wav" + }; + + // The waveforms themselves. + lf_waveform_t* waveforms[NUM_WAVEFORMS + 1]; + + lf_waveform_t empty_waveform = { 0 }; =} - input note:float; - input waveform:int; - + input note: float + input waveform: int + /** * Index of the current waveform. * -1 means no waveform (just make ticks)). */ - state waveform_id:int(default_waveform_id); - + state waveform_id: int = default_waveform_id + reaction(startup) {= - // First waveform is empty. waveforms[0] = &empty_waveform; - + // Open and read waveform files. for (int i = 0; i < NUM_WAVEFORMS; i++) { waveforms[i + 1] = read_wave_file(waveform_files[i]); } - + // Start an audio loop that will become ready to receive // amplitude samples of audio data. lf_start_audio_loop(lf_time_logical()); =} - - reaction(waveform) {= - self->waveform_id = waveform->value; - =} - + + reaction(waveform) {= self->waveform_id = waveform->value; =} + reaction(note) {= if (self->waveform_id < 0 || self->waveform_id > NUM_WAVEFORMS) { lf_play_audio_waveform(NULL, note->value, lf_time_logical()); @@ -109,8 +95,6 @@ reactor PlayWaveform ( lf_play_audio_waveform(waveforms[self->waveform_id], note->value, lf_time_logical()); } =} - - reaction(shutdown) {= - lf_stop_audio_loop(); - =} + + reaction(shutdown) {= lf_stop_audio_loop(); =} } diff --git a/C/src/rhythm/Rhythm.lf b/C/src/rhythm/Rhythm.lf index 23d12610..c0fc1c1f 100644 --- a/C/src/rhythm/Rhythm.lf +++ b/C/src/rhythm/Rhythm.lf @@ -1,25 +1,23 @@ /** - * Rhythm generator using samples of percussion instruments. This program runs - * on MacOS and Linux, at least. + * Rhythm generator using samples of percussion instruments. This program runs on MacOS and Linux, + * at least. * - * This program opens a simple, terminal-based user interface for specifying a - * rhythmic audio output. The rhythm is displayed in the terminal as it is - * generated and produced as audio using sample audio files. + * This program opens a simple, terminal-based user interface for specifying a rhythmic audio + * output. The rhythm is displayed in the terminal as it is generated and produced as audio using + * sample audio files. * - * This program also uses ncurses, which needs to be installed on your machine - * for this to work. It also uses the library utility sensor_simulator, provided - * with Lingua Franca, which uses keyboard input to simulate asynchronous - * sensors and beeps to simulate timed output. See sensor_simulator.h for - * documentation. + * This program also uses ncurses, which needs to be installed on your machine for this to work. It + * also uses the library utility sensor_simulator, provided with Lingua Franca, which uses keyboard + * input to simulate asynchronous sensors and beeps to simulate timed output. See sensor_simulator.h + * for documentation. * * The merengue rhythm comes from here: * https://www.8notes.com/school/lessons/percussion/merengue.asp * * The sound files come from here: https://freewavesamples.com * - * Sound files are assumed to be wav files with sample rate 44,100, 16-bit - * samples, linear PCM encoded. Use afconvert on Mac to convert to the assumed - * input format. + * Sound files are assumed to be wav files with sample rate 44,100, 16-bit samples, linear PCM + * encoded. Use afconvert on Mac to convert to the assumed input format. * * @author Edward A. Lee * @@ -28,13 +26,8 @@ */ target C { keepalive: true, - files: [ - "/lib/c/reactor-c/util/sensor_simulator.c", - "/lib/c/reactor-c/util/sensor_simulator.h", - ], - cmake-include: [ - "/lib/c/reactor-c/util/sensor_simulator.cmake", - ] + files: ["/lib/c/reactor-c/util/sensor_simulator.c", "/lib/c/reactor-c/util/sensor_simulator.h"], + cmake-include: ["/lib/c/reactor-c/util/sensor_simulator.cmake"] } import PlayWaveform from "PlayWaveform.lf" @@ -70,31 +63,28 @@ preamble {= #define SAMBA 0xddda #define SAMBA_EMPHASIS 0x99ca #endif - + extern const char* instructions[]; extern int instructions_length; =} /** - * Reactor that outputs notes (which carry an emphasis)) according to a - * specified rhythm. The minimum time between notes is given by the - * 'tick_duration' state variable. This can be adjusted up or down. This is - * designed to coordinate between multiple instances of this RhythmSource so + * Reactor that outputs notes (which carry an emphasis)) according to a specified rhythm. The + * minimum time between notes is given by the 'tick_duration' state variable. This can be adjusted + * up or down. This is designed to coordinate between multiple instances of this RhythmSource so * each can change the rhythm and tempo while keeping the others in sync. * * @param sixteenth Initial duration of one sixteenth note. - * @param delta The amount by which to change sixteenth when tempo is increased - * or decreased. + * @param delta The amount by which to change sixteenth when tempo is increased or decreased. * @param message An array of strings to display. * @param message_length The length of the message array. */ reactor RhythmSource( - sixteenth: time = 200 msec, - delta: time = 10 msec, - message: {= const char** =} = {= instructions =}, - message_length: int = {= instructions_length =}, - log_to_file: bool = false -) { + sixteenth: time = 200 msec, + delta: time = 10 msec, + message: {= const char** =} = {= instructions =}, + message_length: int = {= instructions_length =}, + log_to_file: bool = false) { preamble {= const char* instructions[] = { "Basic control:", @@ -118,46 +108,34 @@ reactor RhythmSource( " b: bossa nova", " s: samba" }; - int instructions_length = 20; + int instructions_length = 20; =} - // To change the rhythm. - input rhythm_change_in: char - // To change the tempo. - input tempo_change_in: interval_t + input rhythm_change_in: char // To change the rhythm. + input tempo_change_in: interval_t // To change the tempo. - // To play a note with the given emphasis. - output note: float - // Instrument selection. - output instrument: int - // To change the rhythm. - output rhythm_change: char - // To change the tempo. - output tempo_change: interval_t + output note: float // To play a note with the given emphasis. + output instrument: int // Instrument selection. + output rhythm_change: char // To change the rhythm. + output tempo_change: interval_t // To change the tempo. state tick_duration: time = 200 msec logical action tick - // Count of sixteenth notes. - state count: int = 0 + state count: int = 0 // Count of sixteenth notes. - // Action to be invoked when a key is pressed. - physical action key: char + physical action key: char // Action to be invoked when a key is pressed. - // Indicator of when to make a sound. - state rhythm: int = {= DOWNBEAT =} + state rhythm: int = {= DOWNBEAT =} // Indicator of when to make a sound. - // Indicator of whether to emphasize the sound. - state emphasis: int = {= DOWNBEAT =} + state emphasis: int = {= DOWNBEAT =} // Indicator of whether to emphasize the sound. - // Currently active rhythm. This becomes active from rhythm on the downbeat. - state active_rhythm: int = {= DOWNBEAT =} + state active_rhythm: int = {= DOWNBEAT =} // Currently active rhythm. This becomes active from rhythm on the downbeat. // Currently active emphasis. This becomes active from rhythm on the // downbeat. state active_emphasis: int = {= DOWNBEAT =} - // Position of the cursor in the terminal window. - state cursor: int = 0 + state cursor: int = 0 // Position of the cursor in the terminal window. reaction(startup) -> key, note, tick {= // Start the sensor simulator, which starts ncurses. diff --git a/C/src/rhythm/RhythmDistributed.lf b/C/src/rhythm/RhythmDistributed.lf index 4c61adda..58afa21e 100644 --- a/C/src/rhythm/RhythmDistributed.lf +++ b/C/src/rhythm/RhythmDistributed.lf @@ -1,59 +1,57 @@ /** - * Demonstration of timed distributed Lingua Franca programs. - * This program runs on MacOS and Linux, at least. - * - * This program elaborates Rhythm to have two players that - * run on different machines. Both players can select a musical - * instrument, but only one of the players can control the rhythm - * and the tempo. - * - * To run this program, open three distinct terminal windows - * and run the following generated binaries, one in each window: - * + * Demonstration of timed distributed Lingua Franca programs. This program runs on MacOS and Linux, + * at least. + * + * This program elaborates Rhythm to have two players that run on different machines. Both players + * can select a musical instrument, but only one of the players can control the rhythm and the + * tempo. + * + * To run this program, open three distinct terminal windows and run the following generated + * binaries, one in each window: + * * * RTI -n 2 * * fed-gen/RhythmDistributed/bin/player1 * * fed-gen/RhythmDistributed/bin/player2 - * - * The `-n 2` argument specifies the number of federates. - * Note that you have to have installed the RTI on your path. - * See the instructions in the README file in this directory in the LF repo: - * - * org.lflang/src/lib/core/federated/RTI - * - * You can also map these three to distinct machines by specifying an `at` clause - * on the lines at the end of this file that instantiate the reactors. - * See [[https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution]]. - * + * + * The `-n 2` argument specifies the number of federates. Note that you have to have installed the + * RTI on your path. See the instructions in the README file in this directory in the LF repo: + * + * org.lflang/src/lib/core/federated/RTI + * + * You can also map these three to distinct machines by specifying an `at` clause on the lines at + * the end of this file that instantiate the reactors. See + * [[https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution]]. + * * @see Rhythm.lf * @see RhythmDistributedNoUI.lf * * @author Edward A. Lee */ target C - + import RhythmSource from "Rhythm.lf" import PlayWaveform from "PlayWaveform.lf" reactor Player { - input tempo_change_in:interval_t; // To accept a tempo change. - input rhythm_change_in:char; // To accept a rhythm change. - output tempo_change:interval_t; // To change the tempo. - output rhythm_change:char; // To change the rhythm. - source = new RhythmSource(); - play = new PlayWaveform(); - source.note -> play.note; - source.instrument -> play.waveform; - source.rhythm_change -> rhythm_change; - source.tempo_change -> tempo_change; - rhythm_change_in -> source.rhythm_change_in; - tempo_change_in -> source.tempo_change_in; + input tempo_change_in: interval_t // To accept a tempo change. + input rhythm_change_in: char // To accept a rhythm change. + output tempo_change: interval_t // To change the tempo. + output rhythm_change: char // To change the rhythm. + source = new RhythmSource() + play = new PlayWaveform() + source.note -> play.note + source.instrument -> play.waveform + source.rhythm_change -> rhythm_change + source.tempo_change -> tempo_change + rhythm_change_in -> source.rhythm_change_in + tempo_change_in -> source.tempo_change_in } federated reactor { - player1 = new Player(); - player2 = new Player(); - player1.rhythm_change -> player2.rhythm_change_in; - player1.tempo_change -> player2.tempo_change_in; - player2.rhythm_change -> player1.rhythm_change_in; - player2.tempo_change -> player1.tempo_change_in; + player1 = new Player() + player2 = new Player() + player1.rhythm_change -> player2.rhythm_change_in + player1.tempo_change -> player2.tempo_change_in + player2.rhythm_change -> player1.rhythm_change_in + player2.tempo_change -> player1.tempo_change_in } diff --git a/C/src/rhythm/RhythmDistributedNoUI.lf b/C/src/rhythm/RhythmDistributedNoUI.lf index 8852ee38..96a60794 100644 --- a/C/src/rhythm/RhythmDistributedNoUI.lf +++ b/C/src/rhythm/RhythmDistributedNoUI.lf @@ -1,17 +1,14 @@ /** - * Rhythm generator using samples of percussion instruments. - * This version can run on multiple machines leveraging - * synchronized clocks to stay in sync. - * This program runs on MacOS and Linux, at least. - * - * This program tests clock synchronization by producing - * sounds on each of two computers that, if the clocks are - * synchronized well enough, sound like they are occurring - * at identical times. In this version, there is no communication - * between the components other than clock synchronization. - * - * To map these programs onto distinct machines, at the end - * of this file, change the lines like this: + * Rhythm generator using samples of percussion instruments. This version can run on multiple + * machines leveraging synchronized clocks to stay in sync. This program runs on MacOS and Linux, at + * least. + * + * This program tests clock synchronization by producing sounds on each of two computers that, if + * the clocks are synchronized well enough, sound like they are occurring at identical times. In + * this version, there is no communication between the components other than clock synchronization. + * + * To map these programs onto distinct machines, at the end of this file, change the lines like + * this: * ``` * player1 = new RhythmPlayer(); * ``` @@ -19,13 +16,12 @@ * ``` * player1 = new RhythmPlayer() at 10.0.0.42; * ``` - * where the last part is the IP address or machine name of - * a machine on which you want to run the particular component. - * See [[https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution]]. - * + * where the last part is the IP address or machine name of a machine on which you want to run the + * particular component. See [[https://github.com/icyphy/lingua-franca/wiki/Distributed-Execution]]. + * * @see Rhythm.lf * @see RhythmDistributed.lf - * + * * @author Edward A. Lee */ target C { @@ -39,30 +35,26 @@ target C { trials: 10, attenuation: 10 } -}; +} + import PlayWaveform from "PlayWaveform.lf" -/** - * Reactor that outputs a note request at the specified period. - */ -reactor BeatSource(period:time(1600 msec)) { - output note:float; // To play a note with the given emphasis. - - timer tick(0, period); - - reaction(tick) -> note {= - lf_set(note, 1.0f); - =} +/** Reactor that outputs a note request at the specified period. */ +reactor BeatSource(period: time = 1600 msec) { + output note: float // To play a note with the given emphasis. + + timer tick(0, period) + + reaction(tick) -> note {= lf_set(note, 1.0f); =} } reactor RhythmPlayer { - source = new BeatSource(); - play = new PlayWaveform(default_waveform_id = 3); - source.note -> play.note; + source = new BeatSource() + play = new PlayWaveform(default_waveform_id=3) + source.note -> play.note } federated reactor { - player1 = new RhythmPlayer(); - player2 = new RhythmPlayer(); + player1 = new RhythmPlayer() + player2 = new RhythmPlayer() } - diff --git a/C/src/rhythm/SensorSimulator.lf b/C/src/rhythm/SensorSimulator.lf index 50ad7a42..197cee8b 100644 --- a/C/src/rhythm/SensorSimulator.lf +++ b/C/src/rhythm/SensorSimulator.lf @@ -1,38 +1,36 @@ /** - * Simple demonstration of the sensor simulator used in the Rhythm examples. - * This has no audio output, but just tests the ncurses interface. + * Simple demonstration of the sensor simulator used in the Rhythm examples. This has no audio + * output, but just tests the ncurses interface. */ target C { keepalive: true, - files: [ - "/lib/c/reactor-c/util/sensor_simulator.c", - "/lib/c/reactor-c/util/sensor_simulator.h", - ], - cmake-include: [ - "/lib/c/reactor-c/util/sensor_simulator.cmake", - ] -}; + files: ["/lib/c/reactor-c/util/sensor_simulator.c", "/lib/c/reactor-c/util/sensor_simulator.h"], + cmake-include: ["/lib/c/reactor-c/util/sensor_simulator.cmake"] +} + main reactor { preamble {= #include "sensor_simulator.h" const char* messages[] = {"Hello", "World"}; int num_messages = 2; =} - timer t(0, 1 sec); - timer r(0, 2 sec); - physical action key:char*; + timer t(0, 1 sec) + timer r(0, 2 sec) + physical action key: char* + reaction(startup) -> key {= lf_print("Starting sensor simulator."); start_sensor_simulator(messages, num_messages, 16, NULL, LOG_LEVEL_INFO); register_sensor_key('\0', key); - =} - reaction(t) {= - show_tick("*"); =} + + reaction(t) {= show_tick("*"); =} + reaction(r) {= lf_print("Elapsed logical time: %lld.", lf_time_logical_elapsed()); show_tick("."); =} + reaction(key) {= lf_print("You typed '%s' at elapsed time %lld.", key->value, lf_time_logical_elapsed()); =} diff --git a/C/src/rhythm/Sound.lf b/C/src/rhythm/Sound.lf index 4cebb4de..02dc5f3c 100644 --- a/C/src/rhythm/Sound.lf +++ b/C/src/rhythm/Sound.lf @@ -1,6 +1,5 @@ /** - * Sound test. This program runs on MacOS and Linux, at least. - * This program plays a simple rhythm. + * Sound test. This program runs on MacOS and Linux, at least. This program plays a simple rhythm. * * The merengue rhythm comes from here: * https://www.8notes.com/school/lessons/percussion/merengue.asp @@ -12,7 +11,7 @@ * @see Rhythm.lf */ target C { - keepalive: true, + keepalive: true } import PlayWaveform from "PlayWaveform.lf" @@ -29,29 +28,24 @@ preamble {= =} /** - * Reactor that outputs notes (which carry an emphasis)) according to a - * specified rhythm. The minimum time between notes is given by the - * 'sixteenth' parameter. - * + * Reactor that outputs notes (which carry an emphasis)) according to a specified rhythm. The + * minimum time between notes is given by the 'sixteenth' parameter. + * * @param sixteenth Duration of one sixteenth note. * @param rhythm Binary specification of a rhythm in reverse order. * @param emphasis Binary specification of emphasis in reverse order. */ reactor RhythmSource( - sixteenth: time = 200 msec, - rhythm: int = {= MERENGUE =}, - emphasis: int = {= MERENGUE_EMPHASIS =} -) { + sixteenth: time = 200 msec, + rhythm: int = {= MERENGUE =}, + emphasis: int = {= MERENGUE_EMPHASIS =}) { output note: float - + logical action tick - // Count of sixteenth notes. - state count: int = 0 + state count: int = 0 // Count of sixteenth notes. - reaction(startup) -> note, tick {= - lf_schedule(tick, self->sixteenth); - =} + reaction(startup) -> note, tick {= lf_schedule(tick, self->sixteenth); =} reaction(tick) -> note, tick {= int position = 1 << self->count; @@ -73,6 +67,6 @@ reactor RhythmSource( main reactor { source = new RhythmSource() - play = new PlayWaveform(default_waveform_id = 1) + play = new PlayWaveform(default_waveform_id=1) source.note -> play.note } diff --git a/C/src/robot/CompositeRobot.lf b/C/src/robot/CompositeRobot.lf index dd8a91c2..394f8308 100644 --- a/C/src/robot/CompositeRobot.lf +++ b/C/src/robot/CompositeRobot.lf @@ -1,8 +1,7 @@ /** - * This is a (very incomplete) starting point for a composite robot - * controller, where a handler coordinates control of a gripper, - * an autonomous vehicle, and a robot arm. - * + * This is a (very incomplete) starting point for a composite robot controller, where a handler + * coordinates control of a gripper, an autonomous vehicle, and a robot arm. + * * @author Yi Peng Zhu * @author Edward A. Lee */ @@ -11,65 +10,69 @@ target C import PoissonClock from "../lib/PoissonClock.lf" reactor Handler { - input job:int - input pause:bool // true to pause, false to resume - input arm_status:bool // true for done, false for error - input agv_status:int // batter full/low, errors, etc. - input gripper_status:int - - output move_arm:int[3] // assuming vector command - output pause_arm:bool // true to pause, false to resume - output detect:bool // to request vision detection - output move_agv:int[3] // assuming vector command - output pause_agv:bool // true to pause, false to resume - output gripper_open:bool // true to open, false to close - output pause_gripper:bool - + input job: int + input pause: bool // true to pause, false to resume + input arm_status: bool // true for done, false for error + input agv_status: int // batter full/low, errors, etc. + input gripper_status: int + + output move_arm: int[3] // assuming vector command + output pause_arm: bool // true to pause, false to resume + output detect: bool // to request vision detection + output move_agv: int[3] // assuming vector command + output pause_agv: bool // true to pause, false to resume + output gripper_open: bool // true to open, false to close + output pause_gripper: bool + initial mode IDLE { - reaction(job) -> HANDLING {= + reaction(job) -> reset(HANDLING) {= lf_print("Received job at %lld ns", lf_time_logical_elapsed()); lf_set_mode(HANDLING); =} } + mode HANDLING { - reaction(arm_status, agv_status, gripper_status) -> IDLE {= - lf_set_mode(IDLE); - =} - reaction(job) {= - lf_print("Job rejected at %lld ns. Busy.", lf_time_logical_elapsed()); - =} + reaction(arm_status, agv_status, gripper_status) -> reset(IDLE) {= lf_set_mode(IDLE); =} + + reaction(job) {= lf_print("Job rejected at %lld ns. Busy.", lf_time_logical_elapsed()); =} } } + reactor Jobs { - output job:int - state job_count:int(0) - + output job: int + state job_count: int = 0 + p = new PoissonClock() - reaction(p.event) -> job {= - lf_set(job, self->job_count++); - =} + + reaction(p.event) -> job {= lf_set(job, self->job_count++); =} } + reactor ManualControl { - output pause:bool // true to pause, false to resume + output pause: bool // true to pause, false to resume } + reactor Arm { - input move:int[3] - input[2] pause:bool - output status:bool // true for done, false for error + input move: int[3] + input[2] pause: bool + output status: bool // true for done, false for error } + reactor Vision { - input detect:bool + input detect: bool } + reactor AGV { - input move:int[3] - input[2] pause:bool - output status:int + input move: int[3] + input[2] pause: bool + output status: int } + reactor Gripper { - input open:bool // true for open, false for close - input[2] pause:bool - output status:int + input open: bool // true for open, false for close + input[2] pause: bool + output status: int } + main reactor { h = new Handler() j = new Jobs() @@ -78,7 +81,7 @@ main reactor { i = new Vision() v = new AGV() g = new Gripper() - + j.job -> h.job m.pause -> h.pause m.pause, h.pause_arm -> a.pause diff --git a/C/src/rosace/AircraftSimulator.lf b/C/src/rosace/AircraftSimulator.lf index 35ad161a..5d6baa18 100644 --- a/C/src/rosace/AircraftSimulator.lf +++ b/C/src/rosace/AircraftSimulator.lf @@ -1,34 +1,33 @@ /** * Aircraft model from the ROSACE case study, from: - * - * Claire Pagetti , David Saussiéy, Romain Gratia , Eric Noulard , Pierre Siron, - * "The ROSACE Case Study: From Simulink Specification to Multi/Many-Core Execution," - * RTAS (2014). - * + * + * Claire Pagetti , David Saussiéy, Romain Gratia , Eric Noulard , Pierre Siron, "The ROSACE Case + * Study: From Simulink Specification to Multi/Many-Core Execution," RTAS (2014). + * * This implementation is based on code from: - * - * Deschamps, Henrick and Cappello, Gerlando and Cardoso, Janette and Siron, Pierre - * Coincidence Problem in CPS Simulations: the R-ROSACE Case Study. - * (2018) In: 9th European Congress Embedded Real Time Software and Systems ERTS2 2018, - * 31 January 2018 - 2 February 2018 (Toulouse, France). + * + * Deschamps, Henrick and Cappello, Gerlando and Cardoso, Janette and Siron, Pierre Coincidence + * Problem in CPS Simulations: the R-ROSACE Case Study. (2018) In: 9th European Congress Embedded + * Real Time Software and Systems ERTS2 2018, 31 January 2018 - 2 February 2018 (Toulouse, France). * https://www.erts2018.org/authors_detail_inverted_Cappello%20Gerlando.html - * + * * The code was download from https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. - * + * * Since that original code bears an LGPL license, so does this program: - * + * * (c) 2023 Regents of the University of California, LGPL-v3 * (https://www.gnu.org/licenses/lgpl-3.0.html) * - * This is a forward Euler simulation of an aircraft designed to run at 200 Hz. - * The inputs are elevator controls `delta_ec` and throttle controls `delta_thc`. - * This runs at a fixed 200 Hz rate reading the most recent inputs. - * + * This is a forward Euler simulation of an aircraft designed to run at 200 Hz. The inputs are + * elevator controls `delta_ec` and throttle controls `delta_thc`. This runs at a fixed 200 Hz rate + * reading the most recent inputs. + * * @author Edward A. Lee * @author David Saussie * @author Claire Pagetti */ target C + preamble {= #include // Trimming parameters @@ -41,51 +40,48 @@ preamble {= #define delta_e_eq (0.012009615652468) #endif =} + reactor AircraftDynamics( - period: time(5 ms), - // Trimming parameters - theta_eq: double(0.026485847681737), // Initial angle - - // Atmosphere parameters - rho0: double(1.225), - g0: double(9.80665), // Acceleration of gravity in m/s - T0_0: double(288.15), - T0_h: double(-0.0065), - Rs: double(287.05), - - // Aircraft parameters - masse: double(57837.5), - I_y: double(3781272.0), - S: double(122.6), - cbar: double(4.29), - CD_0: double(0.016), - CD_alpha: double(2.5), - CD_deltae: double(0.05), - CL_alpha: double(5.5), - CL_deltae: double(0.193), - alpha_0: double(-0.05), - Cm_0: double(0.04), - Cm_alpha: double(-0.83), - Cm_deltae: double(-1.5), - Cm_q: double(-30) -) { - timer t(0, period) // 200 Hz Forward Euler simulation rate - - input T: double // Thrust - input delta_e: double // Elevator control - - output Vz:double // Vertical speed - output Va:double // True airspeed - output h:double // Altitude - output az:double // Vertical acceleration - output q:double // Pitch rate - - state u: double(0.0) - state w: double(0.0) - state q: double(0.0) - state theta: double(0.0) // Angle (0.0 is horizontal) - state h: double(0.0) - + period: time = 5 ms, + // Trimming parameters + // Initial angle + theta_eq: double = 0.026485847681737, + rho0: double = 1.225, // Atmosphere parameters + g0: double = 9.80665, // Acceleration of gravity in m/s + T0_0: double = 288.15, + T0_h: double = -0.0065, + Rs: double = 287.05, + masse: double = 57837.5, // Aircraft parameters + I_y: double = 3781272.0, + S: double = 122.6, + cbar: double = 4.29, + CD_0: double = 0.016, + CD_alpha: double = 2.5, + CD_deltae: double = 0.05, + CL_alpha: double = 5.5, + CL_deltae: double = 0.193, + alpha_0: double = -0.05, + Cm_0: double = 0.04, + Cm_alpha: double = -0.83, + Cm_deltae: double = -1.5, + Cm_q: double = -30) { + timer t(0, period) // 200 Hz Forward Euler simulation rate + + input T: double // Thrust + input delta_e: double // Elevator control + + output Vz: double // Vertical speed + output Va: double // True airspeed + output h: double // Altitude + output az: double // Vertical acceleration + output q: double // Pitch rate + + state u: double = 0.0 + state w: double = 0.0 + state q: double = 0.0 + state theta: double = 0.0 // Angle (0.0 is horizontal) + state h: double = 0.0 + reaction(startup) {= self->u = Va_eq * cos(self->theta_eq); // Horizontal speed. self->w = Va_eq * sin(self->theta_eq); // Vertical speed. @@ -93,15 +89,15 @@ reactor AircraftDynamics( self->theta = self->theta_eq; self->h = h_eq; =} - + reaction(t) delta_e, T -> Vz, Va, h, az, q {= const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) - + double u_dot, w_dot, q_dot, theta_dot, h_dot; double CD, CL, Cm; double Xa, Za, Ma; double alpha, qbar, V, rho; - + rho = self->rho0 * pow(1.0 + self->T0_h / self->T0_0 * self->h,- self->g0 / (self->Rs * self->T0_h) - 1.0); alpha = atan(self->w / self->u); V = sqrt(self->u * self->u + self->w * self->w); @@ -112,7 +108,7 @@ reactor AircraftDynamics( Xa = - qbar * self->S * (CD * cos(alpha) - CL * sin(alpha)); Za = - qbar * self->S * (CD * sin(alpha) + CL * cos(alpha)); Ma = qbar * self->cbar * self->S * Cm; - + // Output lf_set(Va, V); lf_set(Vz, self->w * cos(self->theta) - self->u * sin(self->theta)); @@ -126,7 +122,7 @@ reactor AircraftDynamics( q_dot = Ma / self->I_y; theta_dot = self->q; h_dot = self->u * sin(self->theta) - self->w * cos(self->theta); - + // Update State self->u += dt * u_dot; self->w += dt * w_dot; @@ -136,22 +132,16 @@ reactor AircraftDynamics( =} } -reactor Engine( - period: time(5 ms), - scale: double(26350.0), - tau: double(0.75) -) { - input delta_thc: double // Engine control - output T: double // Thrust - - timer t(0, period) // 200 Hz Forward Euler simulation rate - - state x1: double({= delta_th_eq =}) - - reaction(t) -> T {= - lf_set(T, self->scale * self->x1); - =} - +reactor Engine(period: time = 5 ms, scale: double = 26350.0, tau: double = 0.75) { + input delta_thc: double // Engine control + output T: double // Thrust + + timer t(0, period) // 200 Hz Forward Euler simulation rate + + state x1: double = {= delta_th_eq =} + + reaction(t) -> T {= lf_set(T, self->scale * self->x1); =} + reaction(t) delta_thc {= const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) @@ -162,32 +152,26 @@ reactor Engine( =} } -reactor Elevator( - period: time(5 ms), - omega: double(25.0), - xi: double(0.85) -) { - input delta_ec: double // Elevator control +reactor Elevator(period: time = 5 ms, omega: double = 25.0, xi: double = 0.85) { + input delta_ec: double // Elevator control output delta_e: double - - timer t(0, period) // 200 Hz Forward Euler simulation rate - - state x1: double({= delta_e_eq =}) - state x2: double(0.0) - - reaction(t) -> delta_e {= - lf_set(delta_e, self->x1); - =} - + + timer t(0, period) // 200 Hz Forward Euler simulation rate + + state x1: double = {= delta_e_eq =} + state x2: double = 0.0 + + reaction(t) -> delta_e {= lf_set(delta_e, self->x1); =} + reaction(t) delta_ec {= const double dt = ((double)self->period)/1e9; // Period in seconds. (1.0/200.0) - + // State Equation double x1_dot = self->x2; - double x2_dot = -self->omega * self->omega * self->x1 + double x2_dot = -self->omega * self->omega * self->x1 - 2.0 * self->xi * self->omega * self->x2 + self->omega * self->omega * delta_ec->value; - + // Update State self->x1 += dt * x1_dot; self->x2 += dt * x2_dot; @@ -195,22 +179,22 @@ reactor Elevator( } reactor Aircraft( - period: time(5 ms), - theta_eq: double(0.026485847681737) // Initial angle -) { - input delta_thc: double // Engine control - input delta_ec: double // Elevator control - - output Vz:double // Vertical speed - output Va:double // True airspeed - output h:double // Altitude - output az:double // Vertical acceleration - output q:double // Pitch rate - - a = new AircraftDynamics(period = period, theta_eq = theta_eq) - e = new Engine(period = period) - el = new Elevator(period = period) - + period: time = 5 ms, + // Initial angle + theta_eq: double = 0.026485847681737) { + input delta_thc: double // Engine control + input delta_ec: double // Elevator control + + output Vz: double // Vertical speed + output Va: double // True airspeed + output h: double // Altitude + output az: double // Vertical acceleration + output q: double // Pitch rate + + a = new AircraftDynamics(period=period, theta_eq=theta_eq) + e = new Engine(period=period) + el = new Elevator(period=period) + a.Vz, a.Va, a.h, a.az, a.q -> Vz, Va, h, az, q delta_thc -> e.delta_thc e.T -> a.T diff --git a/C/src/rosace/Rosace.lf b/C/src/rosace/Rosace.lf index 83ddd7f7..127efcd4 100644 --- a/C/src/rosace/Rosace.lf +++ b/C/src/rosace/Rosace.lf @@ -1,34 +1,30 @@ /** * ROSACE case study, from: - * - * Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, - * "The ROSACE Case Study: From Simulink Specification to Multi/Many-Core Execution," - * RTAS (2014). - * + * + * Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, "The ROSACE Case + * Study: From Simulink Specification to Multi/Many-Core Execution," RTAS (2014). + * * This implementation is based on code from: - * - * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. + * + * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. * * Since that original code bears an LGPL license, so does this program: - * + * * (c) 2023 Regents of the University of California, LGPL-v3 * (https://www.gnu.org/licenses/lgpl-3.0.html) - * - * This program uses a forward Euler simulation of aircraft dynamics and implements - * throttle and elevator control. The parameters specified execute an elevation - * climb from an initial 10,000 feet to a target 11,000 feet. - * - * The style of execution is that each component has a `period` parameter that - * determines the frequency at which it runs. The program relies on persistence - * of inputs in the C target and on the dependency analysis of the *uses* field - * of reactor signature. - * - * To run this, it should be sufficient to just run `lfc` to compile it. - * The "build" parameter of the target provides a script that compiles the - * generated code, runs it, runs gnuplot to generate the plots, and then - * opens the resulting PDF files. If this script fails for any reason, - * comment the `build` attribute of the target declaration and do this by hand: - * + * + * This program uses a forward Euler simulation of aircraft dynamics and implements throttle and + * elevator control. The parameters specified execute an elevation climb from an initial 10,000 feet + * to a target 11,000 feet. + * + * The style of execution is that each component has a `period` parameter that determines the + * frequency at which it runs. The program relies on persistence of inputs in the C target and on + * the dependency analysis of the *uses* field of reactor signature. + * + * To run this, it should be sufficient to just run `lfc` to compile it. The "build" parameter of + * the target provides a script that compiles the generated code, runs it, runs gnuplot to generate + * the plots, and then opens the resulting PDF files. If this script fails for any reason, comment + * the `build` attribute of the target declaration and do this by hand: * ``` * cd examples-lingua-franca/C * lfc src/rosace/Rosace.lf @@ -36,19 +32,17 @@ * gnuplot src/rosace/rosace.gnuplot * open rosace.pdf * ``` - * - * You should see a smooth climb from 10,000 feet to 11,000 feet. - * You can experiment, for example, with the period with which the filters - * sample the sensor outputs from the aircraft model. If you change the - * `filter_period` parameter of the `RosaceController` from its default - * 10 ms to 100 ms, for example, you will far worse behavior from the - * aircraft. - * + + * + * You should see a smooth climb from 10,000 feet to 11,000 feet. You can experiment, for example, + * with the period with which the filters sample the sensor outputs from the aircraft model. If you + * change the `filter_period` parameter of the `RosaceController` from its default 10 ms to 100 ms, + * for example, you will far worse behavior from the aircraft. + * * @author Edward A. Lee * @author David Saussie * @author Claire Pagetti */ - target C { fast: true, build: "./build_run_plot.sh Rosace", @@ -66,42 +60,38 @@ preamble {= #define h_eq (10000.0) #define delta_th_eq (1.5868660794926) - #define delta_e_eq (0.012009615652468) + #define delta_e_eq (0.012009615652468) =} // Documentation @icon("Variables.png") -reactor Variables {} +reactor Variables { +} -reactor Command( - period: time(100 ms), - value: double(0.0) -) { +reactor Command(period: time = 100 ms, value: double = 0.0) { timer t(0, period) - output c:double - reaction(t) -> c {= - lf_set(c, self->value); - =} + output c: double + + reaction(t) -> c {= lf_set(c, self->value); =} } -main reactor(filter_period: time(10 ms)) { +main reactor(filter_period: time = 10 ms) { variables = new Variables() a = new Aircraft() - c = new RosaceController(filter_period = filter_period) - altitude = new Command(value = 11000) // Altitude command - speed = new Command(value = 0.0) // Delta airspeed from nominal Va_eq (230) - - p_h = new PrintToFile(filename = "altitude.data") - p_Va = new PrintToFile(filename = "airspeed.data") - + c = new RosaceController(filter_period=filter_period) + altitude = new Command(value=11000) // Altitude command + speed = new Command(value=0.0) // Delta airspeed from nominal Va_eq (230) + + p_h = new PrintToFile(filename="altitude.data") + p_Va = new PrintToFile(filename="airspeed.data") + a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va - altitude.c -> c.c + altitude.c -> c.c speed.c -> c.s - + c.delta_ec -> a.delta_ec c.delta_thc -> a.delta_thc - - // Print connections. - a.h -> p_h.y + + a.h -> p_h.y // Print connections. a.Va -> p_Va.y } diff --git a/C/src/rosace/RosaceController.lf b/C/src/rosace/RosaceController.lf index c0262e86..eb7a45d2 100644 --- a/C/src/rosace/RosaceController.lf +++ b/C/src/rosace/RosaceController.lf @@ -1,37 +1,33 @@ /** * Controller for the ROSACE case study, from: - * - * Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, - * "The ROSACE Case Study: From Simulink Specification to Multi/Many-Core Execution," - * RTAS (2014). - * + * + * Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, "The ROSACE Case + * Study: From Simulink Specification to Multi/Many-Core Execution," RTAS (2014). + * * This implementation is based on code from: - * - * Deschamps, Henrick and Cappello, Gerlando and Cardoso, Janette and Siron, Pierre - * Coincidence Problem in CPS Simulations: the R-ROSACE Case Study. - * (2018) In: 9th European Congress Embedded Real Time Software and Systems ERTS2 2018, - * 31 January 2018 - 2 February 2018 (Toulouse, France). + * + * Deschamps, Henrick and Cappello, Gerlando and Cardoso, Janette and Siron, Pierre Coincidence + * Problem in CPS Simulations: the R-ROSACE Case Study. (2018) In: 9th European Congress Embedded + * Real Time Software and Systems ERTS2 2018, 31 January 2018 - 2 February 2018 (Toulouse, France). * https://www.erts2018.org/authors_detail_inverted_Cappello%20Gerlando.html - * + * * The code was download from https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. - * + * * Since that original code bears an LGPL license, so does this program: - * + * * (c) 2023 Regents of the University of California, LGPL-v3 * (https://www.gnu.org/licenses/lgpl-3.0.html) - * + * * This program implements throttle and elevator control. - * - * The style of execution is that each component has a `period` parameter that - * determines the frequency at which it runs. The program relies on persistence - * of inputs in the C target and on the dependency analysis of the *uses* field - * of reactor signature. - * + * + * The style of execution is that each component has a `period` parameter that determines the + * frequency at which it runs. The program relies on persistence of inputs in the C target and on + * the dependency analysis of the *uses* field of reactor signature. + * * @author Edward A. Lee * @author David Saussie * @author Claire Pagetti */ - target C preamble {= @@ -47,76 +43,64 @@ preamble {= =} reactor Filter( - period: time(10 ms), - - a:double[](0.0, 0.0), - b:double[](0.0, 0.0), - - init_x1:double(0.0), - init_x2:double(0.0) -) { - input x:double - output y:double - - state x1: double(0.0) - state x2: double(0.0) + period: time = 10 ms, + a: double[] = {0.0, 0.0}, + b: double[] = {0.0, 0.0}, + init_x1: double = 0.0, + init_x2: double = 0.0) { + input x: double + output y: double + + state x1: double = 0.0 + state x2: double = 0.0 timer t(0, period) - + reaction(startup) {= self->x1 = self->init_x1; self->x2 = self->init_x2; =} - - reaction(t) -> y {= - lf_set(y, self->x2); - =} - + + reaction(t) -> y {= lf_set(y, self->x2); =} + reaction(t) x {= double x1_tmp = - self->a[0] * self->x2 + self->b[0] * x->value; double x2_tmp = self->x1 - self->a[1] * self->x2 + self->b[1] * x->value; // Update self->x1 = x1_tmp; - self->x2 = x2_tmp; + self->x2 = x2_tmp; =} } -reactor Command( - period: time(100 ms), - value: double(0.0) -) { +reactor Command(period: time = 100 ms, value: double = 0.0) { timer t(0, period) - output c:double - reaction(t) -> c {= - lf_set(c, self->value); - =} + output c: double + + reaction(t) -> c {= lf_set(c, self->value); =} } reactor Hold( - period:time(20 ms), - - Kp_h: double(0.1014048), - Ki_h: double(0.0048288), - h_switch: double(50.0), - - Vz_c: double(-2.5), - Va_c: double(0.0), - h_c: double(11000) -) { + period: time = 20 ms, + Kp_h: double = 0.1014048, + Ki_h: double = 0.0048288, + h_switch: double = 50.0, + Vz_c: double = -2.5, + Va_c: double = 0.0, + h_c: double = 11000) { timer t(0, period) - + input s: double // Set point input x: double // Measurement - + output c: double // Command - state integrator: double(532.2730285) - + state integrator: double = 532.2730285 + reaction(t) s, x -> c {= double y = 0.0; double Ts_h = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) - + if ((x->value - s->value) < -self->h_switch) { // Output y = self->Vz_c; @@ -129,145 +113,135 @@ reactor Hold( // State self->integrator += Ts_h * (x->value - s->value); } - + lf_set(c, y); =} } reactor VzControl( - period:time(20 ms), - - K2_intVz: double(0.000627342822264), - K2_Vz: double(-0.003252836726554), - K2_q: double(0.376071446897134), - K2_az: double(-0.001566907423747) -) { + period: time = 20 ms, + K2_intVz: double = 0.000627342822264, + K2_Vz: double = -0.003252836726554, + K2_q: double = 0.376071446897134, + K2_az: double = -0.001566907423747) { timer t(0, period) - + input Vzc: double input azf: double input Vzf: double input qf: double - + output delta_ec: double - - state integrator: double(0.0) + + state integrator: double = 0.0 reaction(t) Vzc, azf, Vzf, qf -> delta_ec {= - double Ts_K2 = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) - + // Output double y = self->K2_intVz * self->integrator - + self->K2_Vz * Vzf->value + + self->K2_Vz * Vzf->value + self->K2_q * qf->value + self->K2_az * azf->value + delta_e_eq; // State self->integrator += Ts_K2 * (Vzc->value - Vzf->value); - + lf_set(delta_ec, y); =} } reactor VaControl( - period: time(20 ms), - K1_intVa: double(0.049802610664357), - K1_Va: double(-0.486813084356079), - K1_Vz: double(-0.077603095495388), - K1_q: double(21.692383376322041) -) { + period: time = 20 ms, + K1_intVa: double = 0.049802610664357, + K1_Va: double = -0.486813084356079, + K1_Vz: double = -0.077603095495388, + K1_q: double = 21.692383376322041) { timer t(0, period) - + input Vzf: double input Vaf: double input Vac: double input qf: double - + output delta_thc: double - - state integrator: double(0.0) - + + state integrator: double = 0.0 + reaction(t) Vzf, Vaf, Vac, qf -> delta_thc {= double Ts_K1 = ((double)self->period)/1e9; // Period in seconds. (1.0/50.0) - + // Output - double y = self->K1_intVa * self->integrator + double y = self->K1_intVa * self->integrator + self->K1_Va * (Vaf->value - Va_eq) + self->K1_Vz * Vzf->value + self->K1_q * qf->value + delta_th_eq; - + // State self->integrator += Ts_K1 * (Vac->value - Vaf->value + Va_eq); - + lf_set(delta_thc, y); - =} } -reactor RosaceController( - filter_period: time(10 ms) -) { +reactor RosaceController(filter_period: time = 10 ms) { // Sensor inputs from aircraft - input Vz:double // Vertical speed - input Va:double // True airspeed - input h:double // Altitude measurement - input az:double // Vertical acceleration - input q:double // Pitch rate - + // Vertical speed + input Vz: double + input Va: double // True airspeed + input h: double // Altitude measurement + input az: double // Vertical acceleration + input q: double // Pitch rate + // Command inputs - input c:double // Altitude command - input s:double // Speed command - - output delta_thc: double // Engine control - output delta_ec: double // Elevator control - + // Altitude command + input c: double + input s: double // Speed command + + output delta_thc: double // Engine control + output delta_ec: double // Elevator control + h_c = new Hold() - + h_f = new Filter( - period = 100 ms, - init_x1 = {= h_eq * (1.0 - 1.477888930110354 /*a[1]*/ - 0.049596808318647 /*b1*/) =}, - init_x2 = {= h_eq =}, - a = (0.586756156020839, -1.477888930110354), - b = (0.049596808318647, 0.059270417591839) - ) + period = 100 ms, + init_x1 = {= h_eq * (1.0 - 1.477888930110354 /*a[1]*/ - 0.049596808318647 /*b1*/) =}, + init_x2 = {= h_eq =}, + a = {0.586756156020839, -1.477888930110354}, + b = {0.049596808318647, 0.059270417591839}) az_f = new Filter( - period = filter_period, - init_x1 = 0.0, - init_x2 = 0.0, - a = (0.169118914523145, -0.518588903229759), - b = (0.229019233988375, 0.421510777305010) - ) + period=filter_period, + init_x1=0.0, + init_x2=0.0, + a = {0.169118914523145, -0.518588903229759}, + b = {0.229019233988375, 0.421510777305010}) Vz_f = new Filter( - period = filter_period, - init_x1 = 0.0, - init_x2 = 0.0, - a = (0.914975803093201, -1.911199519984605), - b = (0.001860178914816, 0.001916104193780) - ) + period=filter_period, + init_x1=0.0, + init_x2=0.0, + a = {0.914975803093201, -1.911199519984605}, + b = {0.001860178914816, 0.001916104193780}) q_f = new Filter( - period = filter_period, - init_x1 = 0.0, - init_x2 = 0.0, - a = (0.586756156020839, -1.477888930110354), - b = (0.049596808318647, 0.059270417591839) - ) + period=filter_period, + init_x1=0.0, + init_x2=0.0, + a = {0.586756156020839, -1.477888930110354}, + b = {0.049596808318647, 0.059270417591839}) Va_f = new Filter( - period = filter_period, - init_x1 = {= Va_eq * (1.0 - 1.911199519984605 /*a[1]*/ - 0.001916104193780 /*b[1]*/) =}, - init_x2 = {= Va_eq =}, - a = (0.914975803093201, -1.911199519984605), - b = (0.001860178914816, 0.001916104193780) - ) - + period=filter_period, + init_x1 = {= Va_eq * (1.0 - 1.911199519984605 /*a[1]*/ - 0.001916104193780 /*b[1]*/) =}, + init_x2 = {= Va_eq =}, + a = {0.914975803093201, -1.911199519984605}, + b = {0.001860178914816, 0.001916104193780}) + Vz_ct = new VzControl() Va_ct = new VaControl() - + h, az, Vz, q, Va -> h_f.x, az_f.x, Vz_f.x, q_f.x, Va_f.x c -> h_c.s h_f.y -> h_c.x - + Vz_f.y, az_f.y, h_c.c, q_f.y -> Vz_ct.Vzf, Vz_ct.azf, Vz_ct.Vzc, Vz_ct.qf Vz_ct.delta_ec -> delta_ec - + Va_f.y, Vz_f.y, s, q_f.y -> Va_ct.Vaf, Va_ct.Vzf, Va_ct.Vac, Va_ct.qf Va_ct.delta_thc -> delta_thc } diff --git a/C/src/rosace/RosaceWithUI.lf b/C/src/rosace/RosaceWithUI.lf index 0d54b2f2..884d4f16 100644 --- a/C/src/rosace/RosaceWithUI.lf +++ b/C/src/rosace/RosaceWithUI.lf @@ -1,34 +1,30 @@ /** * ROSACE case study, from: - * - * Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, - * "The ROSACE Case Study: From Simulink Specification to Multi/Many-Core Execution," - * RTAS (2014). - * + * + * Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, "The ROSACE Case + * Study: From Simulink Specification to Multi/Many-Core Execution," RTAS (2014). + * * This implementation is based on code from: - * - * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. + * + * https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/. * * Since that original code bears an LGPL license, so does this program: - * + * * (c) 2023 Regents of the University of California, LGPL-v3 * (https://www.gnu.org/licenses/lgpl-3.0.html) - * - * This program uses a forward Euler simulation of aircraft dynamics and implements - * throttle and elevator control. The parameters specified execute an elevation - * climb from an initial 10,000 feet to a target 11,000 feet. - * - * The style of execution is that each component has a `period` parameter that - * determines the frequency at which it runs. The program relies on persistence - * of inputs in the C target and on the dependency analysis of the *uses* field - * of reactor signature. - * - * To run this, it should be sufficient to just run `lfc` to compile it. - * The "build" parameter of the target provides a script that compiles the - * generated code, runs it, runs gnuplot to generate the plots, and then - * opens the resulting PDF files. If this script fails for any reason, - * comment the `build` attribute of the target declaration and do this by hand: - * + * + * This program uses a forward Euler simulation of aircraft dynamics and implements throttle and + * elevator control. The parameters specified execute an elevation climb from an initial 10,000 feet + * to a target 11,000 feet. + * + * The style of execution is that each component has a `period` parameter that determines the + * frequency at which it runs. The program relies on persistence of inputs in the C target and on + * the dependency analysis of the *uses* field of reactor signature. + * + * To run this, it should be sufficient to just run `lfc` to compile it. The "build" parameter of + * the target provides a script that compiles the generated code, runs it, runs gnuplot to generate + * the plots, and then opens the resulting PDF files. If this script fails for any reason, comment + * the `build` attribute of the target declaration and do this by hand: * ``` * cd examples-lingua-franca/C * lfc src/rosace/Rosace.lf @@ -36,19 +32,17 @@ * gnuplot src/rosace/rosace.gnuplot * open rosace.pdf * ``` - * - * You should see a smooth climb from 10,000 feet to 11,000 feet. - * You can experiment, for example, with the period with which the filters - * sample the sensor outputs from the aircraft model. If you change the - * `filter_period` parameter of the `RosaceController` from its default - * 10 ms to 100 ms, for example, you will far worse behavior from the - * aircraft. - * + + * + * You should see a smooth climb from 10,000 feet to 11,000 feet. You can experiment, for example, + * with the period with which the filters sample the sensor outputs from the aircraft model. If you + * change the `filter_period` parameter of the `RosaceController` from its default 10 ms to 100 ms, + * for example, you will far worse behavior from the aircraft. + * * @author Edward A. Lee * @author David Saussie * @author Claire Pagetti */ - target C { keepalive: true, timeout: 10 min @@ -56,7 +50,6 @@ target C { import Aircraft from "AircraftSimulator.lf" import RosaceController from "RosaceController.lf" - import ServerUI from "../browser-ui/BrowserUI.lf" preamble {= @@ -66,30 +59,30 @@ preamble {= #define h_eq (10000.0) #define delta_th_eq (1.5868660794926) - #define delta_e_eq (0.012009615652468) - - #include // Define strtol() + #define delta_e_eq (0.012009615652468) + + #include // Define strtol() =} reactor UserInterface( - period: time = 100 ms, - altitude_target: double = 10000, - airspeed_delta: double = 0.0 // From nominal Va_eq (230) -) { - timer command(0, period) // Frequency with which to issue commands. - output altitude:double - output airspeed:double + period: time = 100 ms, + altitude_target: double = 10000, + // From nominal Va_eq (230) + airspeed_delta: double = 0.0) { + timer command(0, period) // Frequency with which to issue commands. + output altitude: double + output airspeed: double + + input Va: double + input h: double + + s = new ServerUI() - input Va:double - input h:double - reaction(command) -> altitude, airspeed {= lf_set(altitude, self->altitude_target); lf_set(airspeed, self->airspeed_delta); =} - - s = new ServerUI() - + reaction(s.request) Va, h -> s.response {= char* response; if(strncmp("/data", s.request->value, 5) == 0) { @@ -111,17 +104,16 @@ reactor UserInterface( main reactor(filter_period: time = 10 ms) { a = new Aircraft() - c = new RosaceController(filter_period = filter_period) + c = new RosaceController(filter_period=filter_period) ui = new UserInterface() - + a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va - ui.altitude -> c.c + ui.altitude -> c.c ui.airspeed -> c.s - + c.delta_ec -> a.delta_ec c.delta_thc -> a.delta_thc - - // Print connections. - a.h -> ui.h + + a.h -> ui.h // Print connections. a.Va -> ui.Va } diff --git a/C/src/sdv/ParkingAssist.lf b/C/src/sdv/ParkingAssist.lf index e139db06..3373a0e3 100644 --- a/C/src/sdv/ParkingAssist.lf +++ b/C/src/sdv/ParkingAssist.lf @@ -1,11 +1,11 @@ /** * This is an illustration of one way to build a software-defined vehicle. The program has three * components, one that emulates the sensors in a vehicle, one that provides a dashboard display, - * and one that provides sound alerts. The ParkingAssist.html page provides a user interface - * with a dashboard display at the top and controls at the bottom. - * - * The program assumes that the vehicle starts in a position with an obstacle in front of it. - * FIXME: more detail. + * and one that provides sound alerts. The ParkingAssist.html page provides a user interface with a + * dashboard display at the top and controls at the bottom. + * + * The program assumes that the vehicle starts in a position with an obstacle in front of it. FIXME: + * more detail. * * The dashboard display is emulated by an HTML page that connects to this application via a web * socket. After starting the program, open ParkingAssist.html in your favorite browser and a simple @@ -48,78 +48,73 @@ import PlayWaveform from "../rhythm/PlayWaveform.lf" * it at the specified initial front distance. */ reactor Sensors( - sample_interval: time = 100 ms, - initial_front_distance: int = 100 // In pixels -) { + sample_interval: time = 100 ms, + // In pixels + initial_front_distance: int = 100) { preamble {= - #include // Defines abs() - #include // Defines lround() + #include // Defines abs() + #include // Defines lround() =} - output speed: int // In pixels/second + output speed: int // In pixels/second output front_distance: int // In pixels state velocity: double = 0 // In pixels/second state position: double = 0 // In pixels - state gear: int = 1 // 1 for forward, -1 for reverse. + state gear: int = 1 // 1 for forward, -1 for reverse. timer t(0, sample_interval) - s = new WebSocketServer(hostport=240, max_clients = 1) // Allow only one client - + s = new WebSocketServer(hostport=240, max_clients=1) // Allow only one client + /** - * Given JSON of the form {..., "key":"value", ... }, if the value is an int, - * put that int value into the result and return 0. Otherwise, return -1 if the - * key is not present, -2 if the value is not an int, and -3 if the JSON string - * does not have the expected form. + * Given JSON of the form {..., "key":"value", ... }, if the value is an int, put that int value + * into the result and return 0. Otherwise, return -1 if the key is not present, -2 if the value + * is not an int, and -3 if the JSON string does not have the expected form. */ - method json_to_int( - json: {= const char* =}, - key: {= const char* =}, - result: int* - ): int {= - char* key_position = strstr(json, key); - if (key_position == NULL) return -1; - // Check that the next character is closing quotation mark. - if (strncmp((key_position + strlen(key)), "\"", 1) != 0) return -3; - // Find the opening quotation mark for the value string. - char* start = strchr((key_position + strlen(key) + 1), '"'); - if (start == NULL) return -3; - *result = atoi(start + 1); - return 0; + method json_to_int(json: {= const char* =}, key: {= const char* =}, result: int*): int {= + char* key_position = strstr(json, key); + if (key_position == NULL) return -1; + // Check that the next character is closing quotation mark. + if (strncmp((key_position + strlen(key)), "\"", 1) != 0) return -3; + // Find the opening quotation mark for the value string. + char* start = strchr((key_position + strlen(key) + 1), '"'); + if (start == NULL) return -3; + *result = atoi(start + 1); + return 0; =} - + reaction(s.received) {= - char* json = s.received->value->message; - lf_print("Received control: %s", json); - int accelerator; - if (json_to_int(json, "accelerator", &accelerator) == 0) { - // FIXME: Setting the velocity to be a simple linear - // function of the accelerator position. Pretty naive - // vehicle model. - self->velocity = accelerator * 0.1; - } - int reset_value; - if (json_to_int(json, "reset", &reset_value) == 0) { - self->velocity = 0.0; - self->position = 0.0; - self->gear = 1; - } - int gear; - if (json_to_int(json, "gear", &gear) == 0) { - self->gear = gear; - } + char* json = s.received->value->message; + lf_print("Received control: %s", json); + int accelerator; + if (json_to_int(json, "accelerator", &accelerator) == 0) { + // FIXME: Setting the velocity to be a simple linear + // function of the accelerator position. Pretty naive + // vehicle model. + self->velocity = accelerator * 0.1; + } + int reset_value; + if (json_to_int(json, "reset", &reset_value) == 0) { + self->velocity = 0.0; + self->position = 0.0; + self->gear = 1; + } + int gear; + if (json_to_int(json, "gear", &gear) == 0) { + self->gear = gear; + } =} reaction(t) -> speed, front_distance {= - lf_set(speed, abs((int)lround(self->velocity))); + lf_set(speed, abs((int)lround(self->velocity))); - // Update position. - // Careful with rounding. - self->position += self->gear * self->velocity * (self->sample_interval / MSEC(1)) / 1000.0; + // Update position. + // Careful with rounding. + self->position += self->gear * self->velocity * (self->sample_interval / MSEC(1)) / 1000.0; - // lf_print("Position: %d\n", (int)lround(self->position)); + // lf_print("Position: %d\n", (int)lround(self->position)); - lf_set(front_distance, self->initial_front_distance - (int)lround(self->position)); + lf_set(front_distance, self->initial_front_distance - (int)lround(self->position)); =} } @@ -129,23 +124,23 @@ reactor Dashboard { state connection: web_socket_instance_t = {= {0} =} - s = new WebSocketServer(hostport = 241, max_clients = 1) // Allow only one client + s = new WebSocketServer(hostport=241, max_clients=1) // Allow only one client reaction(speed, front_distance) -> s.send {= - // Ignore the inputs if we are not connected. - if (self->connection.connected) { - // Construct payload. - char* message; - asprintf(&message, "{\"front_distance\": %d, \"speed\": %d}", front_distance->value, speed->value); - - // Construct struct to send. - web_socket_message_t* response = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); - response->length = strlen(message); // Do not include the null terminator. - response->wsi = self->connection.wsi; - response->message = message; - - lf_set(s.send, response); - } + // Ignore the inputs if we are not connected. + if (self->connection.connected) { + // Construct payload. + char* message; + asprintf(&message, "{\"front_distance\": %d, \"speed\": %d}", front_distance->value, speed->value); + + // Construct struct to send. + web_socket_message_t* response = (web_socket_message_t*)malloc(sizeof(web_socket_message_t)); + response->length = strlen(message); // Do not include the null terminator. + response->wsi = self->connection.wsi; + response->message = message; + + lf_set(s.send, response); + } =} // Make sure disconnections occur after messages are sent. @@ -156,42 +151,42 @@ reactor SoundAlert { input front_distance: int logical action ding state ding_interval: time = 0 // 0 means no sound - p = new PlayWaveform(default_waveform_id = 3) + p = new PlayWaveform(default_waveform_id=3) reaction(front_distance) -> ding, p.waveform {= - instant_t previous_interval = self->ding_interval; - // Change the period of the sound. - if (front_distance->value > 75) { - // Go silent. - self->ding_interval = MSEC(0); - lf_set(p.waveform, 0); - } else if (front_distance->value > 50) { - self->ding_interval = MSEC(2000); - lf_set(p.waveform, 3); - } else if (front_distance->value > 25) { - self->ding_interval = MSEC(1000); - lf_set(p.waveform, 3); - } else if (front_distance->value > 15) { - self->ding_interval = MSEC(500); - lf_set(p.waveform, 9); - } else if (front_distance->value > 5) { - self->ding_interval = MSEC(200); - lf_set(p.waveform, 9); - } else { - self->ding_interval = MSEC(100); - lf_set(p.waveform, 8); - } - // If no sound is playing, start it playing. - if (self->ding_interval > MSEC(0) && previous_interval == MSEC(0)) { - lf_schedule(ding, 0); - } + instant_t previous_interval = self->ding_interval; + // Change the period of the sound. + if (front_distance->value > 75) { + // Go silent. + self->ding_interval = MSEC(0); + lf_set(p.waveform, 0); + } else if (front_distance->value > 50) { + self->ding_interval = MSEC(2000); + lf_set(p.waveform, 3); + } else if (front_distance->value > 25) { + self->ding_interval = MSEC(1000); + lf_set(p.waveform, 3); + } else if (front_distance->value > 15) { + self->ding_interval = MSEC(500); + lf_set(p.waveform, 9); + } else if (front_distance->value > 5) { + self->ding_interval = MSEC(200); + lf_set(p.waveform, 9); + } else { + self->ding_interval = MSEC(100); + lf_set(p.waveform, 8); + } + // If no sound is playing, start it playing. + if (self->ding_interval > MSEC(0) && previous_interval == MSEC(0)) { + lf_schedule(ding, 0); + } =} reaction(ding) -> p.note, ding {= - if (self->ding_interval > MSEC(0)) { - lf_set(p.note, 1); - lf_schedule(ding, self->ding_interval); - } + if (self->ding_interval > MSEC(0)) { + lf_set(p.note, 1); + lf_schedule(ding, self->ding_interval); + } =} } diff --git a/C/src/simulation/MemoryHierarchy.lf b/C/src/simulation/MemoryHierarchy.lf index 5789cb1c..583b661d 100644 --- a/C/src/simulation/MemoryHierarchy.lf +++ b/C/src/simulation/MemoryHierarchy.lf @@ -1,55 +1,47 @@ /** - * This program simulates a simple memory hierarchy - * where a random client (with Poisson arrivals) accesses - * a cache. With a specified probability, the cache misses - * and delegates the request to a memory controller. - * The memory controller, in turn farms out the request - * to a bank of memories, which return after a random - * amount of time (with an exponential distribution). - * When all the requests have sent responses to the cache, - * the cache replies to the client. - * If a request arrives while there are requests pending, - * then it queues the request in finite-size queue. - * If the queue is full, then it returns an error signal - * to the client. - * + * This program simulates a simple memory hierarchy where a random client (with Poisson arrivals) + * accesses a cache. With a specified probability, the cache misses and delegates the request to a + * memory controller. The memory controller, in turn farms out the request to a bank of memories, + * which return after a random amount of time (with an exponential distribution). When all the + * requests have sent responses to the cache, the cache replies to the client. If a request arrives + * while there are requests pending, then it queues the request in finite-size queue. If the queue + * is full, then it returns an error signal to the client. + * * @author Edward A. Lee */ target C { timeout: 10 us } + import Random from "../lib/Random.lf" import PoissonClock from "../lib/PoissonClock.lf" import RandomDelay from "../lib/RandomDelay.lf" main reactor { - client = new Client(seed = 0) - cache = new Cache(seed = 0, miss_probability = 0.3) - memory = new Memory(seed = 0, average_delay = 100 ns) + client = new Client(seed=0) + cache = new Cache(seed=0, miss_probability=0.3) + memory = new Memory(seed=0, average_delay = 100 ns) client.request -> cache.request - cache.response ->client.response + cache.response -> client.response cache.miss -> memory.request memory.response -> cache.fill } /** - * Generate requests according to a PoissonProcess with rate lambda - * (1/lambda is the average time between requests). - * This defaults to 10,000,000 requests per second (100 ns - * average time between requests). - * Each request carries the time that the request is initiated. - * When the same time-value is received at the response input, - * print the time it took for that response to be received. - * If a negative time is received at the response input, then - * interpret this to be an error and print an error message. + * Generate requests according to a PoissonProcess with rate lambda (1/lambda is the average time + * between requests). This defaults to 10,000,000 requests per second (100 ns average time between + * requests). Each request carries the time that the request is initiated. When the same time-value + * is received at the response input, print the time it took for that response to be received. If a + * negative time is received at the response input, then interpret this to be an error and print an + * error message. */ -reactor Client(seed:int(0), lambda:double(10000000.0)) { - input response:time - output request:time - p = new PoissonClock(seed = seed, lambda = lambda) - reaction(p.event) -> request {= - lf_set(request, lf_time_logical_elapsed()); - =} +reactor Client(seed: int = 0, lambda: double = 10000000.0) { + input response: time + output request: time + p = new PoissonClock(seed=seed, lambda=lambda) + + reaction(p.event) -> request {= lf_set(request, lf_time_logical_elapsed()); =} + reaction(response) {= if (response->value > 0) { interval_t delay = lf_time_logical_elapsed() - response->value; @@ -62,33 +54,24 @@ reactor Client(seed:int(0), lambda:double(10000000.0)) { } /** - * Simulate a cache. A cache miss will occur at - * random with the probability given by miss_probability, - * a double between 0.0 and 1.0, with default 0.1. - * The input is a time value and the response will be - * either the same value or its negative if an error occurs. - * Upon a cache hit, the output will be delayed by the - * time value given by hit_delay. - * Upon a cache miss, the memory fetch will be delegated - * to the miss output and the Cache will wait for a - * response on the fill input. If a miss occurs - * while it is waiting, then an error will occur. - * Upon an error, the response will be sent immediately - * and will have the negative of the request value. + * Simulate a cache. A cache miss will occur at random with the probability given by + * miss_probability, a double between 0.0 and 1.0, with default 0.1. The input is a time value and + * the response will be either the same value or its negative if an error occurs. Upon a cache hit, + * the output will be delayed by the time value given by hit_delay. Upon a cache miss, the memory + * fetch will be delegated to the miss output and the Cache will wait for a response on the fill + * input. If a miss occurs while it is waiting, then an error will occur. Upon an error, the + * response will be sent immediately and will have the negative of the request value. */ -reactor Cache( - miss_probability:double(0.1), - hit_delay:time(10 ns) -) extends Random { - input request:time - output response:time - output miss:time - input fill:time - state pending:bool(false) - logical action a:time - reaction(a) -> response {= - lf_set(response, a->value); - =} +reactor Cache(miss_probability: double = 0.1, hit_delay: time = 10 ns) extends Random { + input request: time + output response: time + output miss: time + input fill: time + state pending: bool = false + logical action a: time + + reaction(a) -> response {= lf_set(response, a->value); =} + reaction(request) -> a, response, miss {= bool hit = random() >= (int)(self->miss_probability * RAND_MAX); if (self->pending && !hit) { @@ -101,23 +84,20 @@ reactor Cache( lf_set(miss, request->value); } =} + reaction(fill) -> response {= lf_set(response, fill->value); self->pending = false; =} } -/** - * Simulate a memory that responds after a random time. - */ -reactor Memory(seed:int(0), average_delay:time(100 ns)) { - input request:time - output response:time - delay = new RandomDelay(average = average_delay) - reaction(request) -> delay.in {= - lf_set(delay.in, request->value); - =} - reaction(delay.out) -> response {= - lf_set(response, delay.out->value); - =} +/** Simulate a memory that responds after a random time. */ +reactor Memory(seed: int = 0, average_delay: time = 100 ns) { + input request: time + output response: time + delay = new RandomDelay(average=average_delay) + + reaction(request) -> delay.in {= lf_set(delay.in, request->value); =} + + reaction(delay.out) -> response {= lf_set(response, delay.out->value); =} } diff --git a/C/src/simulation/PoissonProcess.lf b/C/src/simulation/PoissonProcess.lf index 4f0cb572..d8c95738 100644 --- a/C/src/simulation/PoissonProcess.lf +++ b/C/src/simulation/PoissonProcess.lf @@ -1,24 +1,24 @@ /** - * Generate two Poisson processes, one with a fixed seed - * and the other using the default seed. The one with the - * fixed seed will produce repeatable outputs. - * A third random process is generated using the - * RandomDelay reactor. - * - * This demonstrates the use of the PoissonClock - * and RandomDelay reactors. - * + * Generate two Poisson processes, one with a fixed seed and the other using the default seed. The + * one with the fixed seed will produce repeatable outputs. A third random process is generated + * using the RandomDelay reactor. + * + * This demonstrates the use of the PoissonClock and RandomDelay reactors. + * * @author Edward A. Lee */ target C { timeout: 5 sec } + import PoissonClock from "../lib/PoissonClock.lf" import RandomDelay from "../lib/RandomDelay.lf" + main reactor { non_repeatable = new PoissonClock() - repeatable = new PoissonClock(seed = 1) + repeatable = new PoissonClock(seed=1) delay = new RandomDelay() + reaction(non_repeatable.event, repeatable.event) {= double seconds = ((double)lf_time_logical_elapsed())/SEC(1); if (non_repeatable.event->is_present) { @@ -28,6 +28,7 @@ main reactor { lf_print("repeatable event at time %f seconds", seconds); } =} + reaction(startup, delay.out) -> delay.in {= lf_set(delay.in, lf_time_logical_elapsed()); if(delay.out->is_present) { diff --git a/CCpp/src/DoorLock/DoorLock.lf b/CCpp/src/DoorLock/DoorLock.lf index 46995099..724a466b 100644 --- a/CCpp/src/DoorLock/DoorLock.lf +++ b/CCpp/src/DoorLock/DoorLock.lf @@ -1,22 +1,20 @@ /** - * A simple car door lock system with a single door that can be - * controlled in any of three ways: - * 1. buttons on the physical door, - * 2. an external system (like an RFID key fob), or - * 3. a mobile device (like a phone) via a cloud service. - * + * A simple car door lock system with a single door that can be controlled in any of three ways: 1. + * buttons on the physical door, 2. an external system (like an RFID key fob), or 3. a mobile device + * (like a phone) via a cloud service. + * * Potentially interesting behaviors: - * - * If the door is locked and closed and receives simultaneous open - * and unlock commands, it unlocks the door, but the door remains closed. - * + * + * If the door is locked and closed and receives simultaneous open and unlock commands, it unlocks + * the door, but the door remains closed. + * * @author Ravi Akella * @author Edward A. Lee */ - target CCpp { - keepalive: true, + keepalive: true } + import UserInteraction from "lib/UserInteraction.lf" import PropagationDelaySim from "lib/PropagationDelaySim.lf" import AuthSim from "lib/AuthSim.lf" @@ -28,18 +26,17 @@ preamble {= =} reactor DoorLockController { - input door:OpenEvent - input button:LockCommand - input fob:LockCommand - input mobile:LockCommand + input door: OpenEvent + input button: LockCommand + input fob: LockCommand + input mobile: LockCommand + + output actuate: LockCommand - output actuate:LockCommand - initial mode ClosedLocked { - reaction(reset) {= - lf_print("*** Door is closed and has been locked."); - =} - reaction(button, fob, mobile) -> ClosedUnlocked, actuate {= + reaction(reset) {= lf_print("*** Door is closed and has been locked."); =} + + reaction(button, fob, mobile) -> reset(ClosedUnlocked), actuate {= if ( (button->is_present && button->value == UNLOCK) || (fob->is_present && fob->value == UNLOCK) @@ -51,15 +48,14 @@ reactor DoorLockController { lf_print("Door is already locked."); } =} - reaction(door) {= - lf_print("Door is closed and locked."); - =} + + reaction(door) {= lf_print("Door is closed and locked."); =} } + mode ClosedUnlocked { - reaction(reset) {= - lf_print("*** Door has been closed and is unlocked."); - =} - reaction(button, fob, mobile) -> ClosedLocked, actuate {= + reaction(reset) {= lf_print("*** Door has been closed and is unlocked."); =} + + reaction(button, fob, mobile) -> reset(ClosedLocked), actuate {= if ( (button->is_present && button->value == LOCK) || (fob->is_present && fob->value == LOCK) @@ -71,27 +67,27 @@ reactor DoorLockController { lf_print("Door is already unlocked."); } =} - reaction(door) -> Open {= + + reaction(door) -> reset(Open) {= if (door->value == OPEN) lf_set_mode(Open); else lf_print("Door is already closed."); =} } + mode Open { - reaction(reset) {= - lf_print("*** Door has been opened."); - =} - reaction(door) -> ClosedUnlocked {= + reaction(reset) {= lf_print("*** Door has been opened."); =} + + reaction(door) -> reset(ClosedUnlocked) {= if (door->value == CLOSE) lf_set_mode(ClosedUnlocked); else lf_print("Door is already open."); =} - reaction(button, fob, mobile) {= - lf_print("Door is open."); - =} + + reaction(button, fob, mobile) {= lf_print("Door is open."); =} } } -reactor DoorLockActuator(tolerance:time(1000 msec)) { - input in:LockCommand +reactor DoorLockActuator(tolerance: time = 1000 msec) { + input in: LockCommand reaction(in) {= interval_t lag = (lf_time_physical() - lf_time_logical()); @@ -100,7 +96,7 @@ reactor DoorLockActuator(tolerance:time(1000 msec)) { std::cout << lf_time_physical_elapsed() << ": Command " << command_string << " executed successfully. Time lag: " << lag << " ns." << std::endl; - =} deadline(tolerance){= + =} deadline(tolerance) {= interval_t lag = (lf_time_physical() - lf_time_logical()); std::string command_string = (in->value == LOCK) ? "LOCK" : "UNLOCK"; @@ -108,11 +104,12 @@ reactor DoorLockActuator(tolerance:time(1000 msec)) { << " executed " << lag - self->tolerance << " nsecs later then expected." << std::endl; =} } + reactor DoorLockSystem { - input door:OpenEvent - input button:LockCommand - input fob:LockCommand - input mobile:LockCommand + input door: OpenEvent + input button: LockCommand + input fob: LockCommand + input mobile: LockCommand dlc = new DoorLockController() a = new DoorLockActuator() diff --git a/CCpp/src/DoorLock/lib/AuthSim.lf b/CCpp/src/DoorLock/lib/AuthSim.lf index 45b651f8..2641372d 100644 --- a/CCpp/src/DoorLock/lib/AuthSim.lf +++ b/CCpp/src/DoorLock/lib/AuthSim.lf @@ -1,19 +1,18 @@ /** - * Simulate authentication. This simulator rejects - * authentication one of every five attempts, prints + * Simulate authentication. This simulator rejects authentication one of every five attempts, prints * a message, and produces no output. - * + * * @author Ravi Akella * @author Edward A. Lee */ target CCpp reactor AuthSim { - input in:LockCommand - output out:LockCommand + input in: LockCommand + output out: LockCommand + + state count: int = 0 - state count:int(0) - reaction(in) -> out {= if (++self->count % 5 == 0) { lf_print("!!! Authentication rejected."); diff --git a/CCpp/src/DoorLock/lib/PropagationDelaySim.lf b/CCpp/src/DoorLock/lib/PropagationDelaySim.lf index 65417170..f4640bb1 100644 --- a/CCpp/src/DoorLock/lib/PropagationDelaySim.lf +++ b/CCpp/src/DoorLock/lib/PropagationDelaySim.lf @@ -1,9 +1,7 @@ /** - * Simulate propagation delay (in physical time) by - * taking time to copy the input to the output. - * The time taken is uniformly between min_delay - * and ten times min_delay. - * + * Simulate propagation delay (in physical time) by taking time to copy the input to the output. The + * time taken is uniformly between min_delay and ten times min_delay. + * * @author Ravi Akella * @author Edward A. Lee */ @@ -12,14 +10,16 @@ target CCpp preamble {= #include /* srand, rand */ =} -reactor PropagationDelaySim(min_delay:time(100 msec)) { - input in:LockCommand - output out:LockCommand + +reactor PropagationDelaySim(min_delay: time = 100 msec) { + input in: LockCommand + output out: LockCommand reaction(startup) {= // Seed the random number generator. srand(lf_time_logical()); =} + reaction(in) -> out {= lf_nanosleep((rand()%10+1) * self->min_delay); lf_set(out, in->value); diff --git a/CCpp/src/DoorLock/lib/UserInteraction.lf b/CCpp/src/DoorLock/lib/UserInteraction.lf index 0b7267c6..1e08fa38 100644 --- a/CCpp/src/DoorLock/lib/UserInteraction.lf +++ b/CCpp/src/DoorLock/lib/UserInteraction.lf @@ -1,24 +1,19 @@ /** - * A simulator that generates command for a simple car door - * lock system with a single door that can be - * controlled in any of three ways: - * 1. buttons on the physical door, - * 2. an external system (like an RFID key fob), or - * 3. a mobile device (like a phone) via a cloud service. - * + * A simulator that generates command for a simple car door lock system with a single door that can + * be controlled in any of three ways: 1. buttons on the physical door, 2. an external system (like + * an RFID key fob), or 3. a mobile device (like a phone) via a cloud service. + * * @author Ravi Akella * @author Edward A. Lee */ - target C { keepalive: true, - files: [ - "../include/types.h", - ], + files: ["../include/types.h"] } + preamble {= #include "types.h" - + void* read_input(void* user_response) { lf_print("****************************************************\n" "Press the following keys for the following actions:\n" @@ -43,15 +38,15 @@ preamble {= return NULL; } =} - + reactor UserInteraction { - output door:OpenEvent; - output button:LockCommand; - output fob:LockCommand; - output mobile:LockCommand; - - physical action key:int; - + output door: OpenEvent + output button: LockCommand + output fob: LockCommand + output mobile: LockCommand + + physical action key: int + reaction(startup) -> key {= // start new thread lf_thread_t thread_id; @@ -60,7 +55,7 @@ reactor UserInteraction { reaction(key) -> door, button, fob, mobile {= // lf_print("You typed '%c' at elapsed time %lld.", key->value, lf_time_logical_elapsed()); - + switch(key->value) { case 'o': lf_set(door, OPEN); diff --git a/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf b/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf index 2c3f32e7..d9db6d51 100644 --- a/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf +++ b/CCpp/src/ROS/MigrationGuide/lf-project/src/Main.lf @@ -1,10 +1,11 @@ // src/Main.lf -target CCpp; +target CCpp + import Sender from "Sender.lf" import Receiver from "Receiver.lf" federated reactor { - sender = new Sender(); - receiver = new Receiver(); - sender.out -> receiver.in serializer "ROS2"; + sender = new Sender() + receiver = new Receiver() + sender.out -> receiver.in serializer "ROS2" } diff --git a/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf b/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf index 881b6a82..e21400b8 100644 --- a/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf +++ b/CCpp/src/ROS/MigrationGuide/lf-project/src/Receiver.lf @@ -1,7 +1,7 @@ // src/Receiver.lf target CCpp { cmake-include: "include/composition.cmake" -}; +} preamble {= #include "subscriber_member_function.h" @@ -9,8 +9,8 @@ preamble {= reactor Receiver { // Instantiate the subscriber node as a sate variable - state subscriber_node : std::shared_ptr; - input in:std::shared_ptr; + state subscriber_node: std::shared_ptr + input in: std::shared_ptr reaction(startup) {= // Initialize rclcpp @@ -20,12 +20,9 @@ reactor Receiver { =} reaction(in) {= - lf_print("[LF receiver] Received %s", in->value->data.c_str()); self->subscriber_node->topic_callback(in->value); =} - reaction(shutdown) {= - rclcpp::shutdown(); - =} + reaction(shutdown) {= rclcpp::shutdown(); =} } diff --git a/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf b/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf index bd4910e0..88f92246 100644 --- a/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf +++ b/CCpp/src/ROS/MigrationGuide/lf-project/src/Sender.lf @@ -1,7 +1,7 @@ // src/Sender.lf target CCpp { cmake-include: "include/composition.cmake" -}; +} preamble {= #include "publisher_member_function.h" @@ -9,10 +9,10 @@ preamble {= reactor Sender { // Instantiate the publisher node as a sate variable - state publisher_node : std::shared_ptr; - output out:std::shared_ptr; - timer t(0, 1 sec); - state count:int(0); + state publisher_node: std::shared_ptr + output out: std::shared_ptr + timer t(0, 1 sec) + state count: int = 0 reaction(startup) {= // Initialize rclcpp @@ -28,7 +28,5 @@ reactor Sender { lf_set(out, message); =} - reaction(shutdown) {= - rclcpp::shutdown(); - =} + reaction(shutdown) {= rclcpp::shutdown(); =} } diff --git a/CCpp/src/ROS/ROSBuiltInSerialization.lf b/CCpp/src/ROS/ROSBuiltInSerialization.lf index c01fc36b..cd234972 100644 --- a/CCpp/src/ROS/ROSBuiltInSerialization.lf +++ b/CCpp/src/ROS/ROSBuiltInSerialization.lf @@ -1,68 +1,61 @@ /** - * This example showcases the infrastructure that is built into the - * CCpp target that can automatically serialize and deserialize ROS2 - * messages in federated programs. - * - * This example contains a sender-receiver federated program in which - * the 'sender' federate sends a std_msgs::msg::Int32 message to the - * 'receiver' federate. - * - * To run this example, make sure that your terminal is properly sourced - * for ROS2. See + * This example showcases the infrastructure that is built into the CCpp target that can + * automatically serialize and deserialize ROS2 messages in federated programs. + * + * This example contains a sender-receiver federated program in which the 'sender' federate sends a + * std_msgs::msg::Int32 message to the 'receiver' federate. + * + * To run this example, make sure that your terminal is properly sourced for ROS2. See * https://docs.ros.org/en/foxy/Tutorials/Configuring-ROS2-Environment.html. - * + * * Then you can use lfc to compile this program: - * - * lfc ROSBuiltInSerialization.lf - * + * + * lfc ROSBuiltInSerialization.lf + * * And launch the federated program in the `bin` folder: - * + * * example/C/bin/ROSBuiltInSerialization - * + * * @author Soroush Bateni */ - target CCpp { +target CCpp { cmake: true, // Only CMake is supported - cmake-include: "include/CMakeListsExtension.txt", -}; + cmake-include: "include/CMakeListsExtension.txt" +} preamble {= #include "std_msgs/msg/int32.hpp" - =} reactor Sender { - output out:std_msgs::msg::Int32; - - // state serialized_msg_pcl:rclcpp::SerializedMessage({=0u=}); - state count:int(0); - - timer t (0, 1 sec); - - reaction (t) -> out {= - std_msgs::msg::Int32 ros_message; - ros_message.data = self->count++; - lf_set(out, ros_message); - + output out: std_msgs::msg::Int32 + + state count: int = 0 // state serialized_msg_pcl:rclcpp::SerializedMessage({=0u=}); + + timer t(0, 1 sec) + + reaction(t) -> out {= + std_msgs::msg::Int32 ros_message; + ros_message.data = self->count++; + lf_set(out, ros_message); =} } - reactor Receiver { - input in:std_msgs::msg::Int32; - - reaction (in) {= + input in: std_msgs::msg::Int32 + + reaction(in) {= // Print the ROS2 message data - lf_print( - "Serialized integer after deserialization: %d", - in->value.data - ); + lf_print( + "Serialized integer after deserialization: %d", + in->value.data + ); =} } federated reactor { - sender = new Sender(); - receiver = new Receiver(); - - sender.out -> receiver.in serializer "ros2"; + sender = new Sender() + receiver = new Receiver() + + sender.out -> receiver.in serializer "ros2" } diff --git a/CCpp/src/ROS/ROSSerialization.lf b/CCpp/src/ROS/ROSSerialization.lf index 8765e155..0e745ce5 100644 --- a/CCpp/src/ROS/ROSSerialization.lf +++ b/CCpp/src/ROS/ROSSerialization.lf @@ -1,20 +1,19 @@ /** - * A simple source-sink example that uses the federated Lingua Franca runtime to - * send ROS messages of type std_msgs::msg::Int32. The serialization/deserialization - * techniques are exposed here in the sender/receiver reactions. This mechanism should - * work for any ROS2 message type. - * - * Note: the terminal must be properly sourced for ROS2. See + * A simple source-sink example that uses the federated Lingua Franca runtime to send ROS messages + * of type std_msgs::msg::Int32. The serialization/deserialization techniques are exposed here in + * the sender/receiver reactions. This mechanism should work for any ROS2 message type. + * + * Note: the terminal must be properly sourced for ROS2. See * https://docs.ros.org/en/foxy/Tutorials/Configuring-ROS2-Environment.html. * - * There is a variant of this example called ROSBuiltInSerialization.lf that uses - * the built-in ROS 2 serialization feature that is provided in LF. + * There is a variant of this example called ROSBuiltInSerialization.lf that uses the built-in ROS 2 + * serialization feature that is provided in LF. */ target CCpp { coordination: decentralized, timeout: 10 sec, cmake-include: "include/CMakeListsExtension.txt" -}; +} preamble {= #include "rclcpp/rclcpp.hpp" @@ -22,43 +21,45 @@ preamble {= #include "rcutils/allocator.h" #include "rclcpp/rclcpp.hpp" #include "rclcpp/serialization.hpp" - - + + #include "rclcpp/serialized_message.hpp" - - #include "rosidl_typesupport_cpp/message_type_support.hpp" + + #include "rosidl_typesupport_cpp/message_type_support.hpp" =} -reactor Clock(offset:time(0), period:time(1 sec)) { - output y:uint8_t*; - - timer t(0, period); - state count:int(0); - state serialized_msg:rclcpp::SerializedMessage({=0u=}); +reactor Clock(offset: time = 0, period: time = 1 sec) { + output y: uint8_t* + + timer t(0, period) + state count: int = 0 + state serialized_msg: rclcpp::SerializedMessage = {= 0u =} + reaction(t) -> y {= - (self->count)++; - + // From https://github.com/ros2/demos/blob/master/demo_nodes_cpp/src/topics/talker_serialized_message.cpp - + auto msg = std::make_shared(); msg->data = self->count; - + auto message_header_length = 8u; auto message_payload_length = 10u; self->serialized_msg.reserve(message_header_length + message_payload_length); - + static rclcpp::Serialization serializer_obj; serializer_obj.serialize_message(msg.get(), &self->serialized_msg); - + SET_NEW_ARRAY(y, self->serialized_msg.size()); y->value = self->serialized_msg.get_rcl_serialized_message().buffer; =} } + reactor Destination { - input x:uint8_t*; - - state s:int(1); + input x: uint8_t* + + state s: int = 1 + reaction(x) {= auto message = std::make_unique( rcl_serialized_message_t{ .buffer = (uint8_t*)x->token->value, @@ -66,11 +67,11 @@ reactor Destination { .buffer_capacity = x->token->length, .allocator = rcl_get_default_allocator() }); - + // Check the ref count == 1 // lf_print("%d", x->token->ref_count); // assert(x->token->ref_count == 1); // Might be optimized away (see validate in cpp target) - + //rclcpp::SerializedMessage* msg = new rclcpp::SerializedMessage(x->token->length, rcl_get_default_allocator()); auto msg = std::make_unique(std::move(*message.get())); x->token->value = NULL; // Manually move it @@ -80,7 +81,7 @@ reactor Destination { std_msgs::msg::Int32 int_msg; auto serializer_obj = rclcpp::Serialization(); serializer_obj.deserialize_message(msg.get(), &int_msg); - + lf_print("Received %d.", int_msg.data); if (int_msg.data != self->s) { lf_print_warning("Expected %d and got %d.", self->s, int_msg.data); @@ -88,8 +89,9 @@ reactor Destination { self->s++; =} } -federated reactor (period:time(1 sec)) { - c = new Clock(period = period); - d = new Destination(); - c.y -> d.x; + +federated reactor(period: time = 1 sec) { + c = new Clock(period=period) + d = new Destination() + c.y -> d.x } diff --git a/Cpp/AlarmClock/src/AlarmClock.lf b/Cpp/AlarmClock/src/AlarmClock.lf index 36b1d97f..113186a5 100644 --- a/Cpp/AlarmClock/src/AlarmClock.lf +++ b/Cpp/AlarmClock/src/AlarmClock.lf @@ -1,40 +1,31 @@ -/* -* This is a minimal example of an alarmclock implemeted using the -* features lingua franca supplies. -* -* This is just an extract and simplification from the main project -* which you can find here: https://github.com/revol-xut/lf-alarm-clock -* -* This file contains the networking implementation it is really just an -* simple socket application which parses simple http headers and respondes -* in text/plain -* -* @author Tassilo Tanneberer -*/ - -target Cpp{ +/** + * This is a minimal example of an alarmclock implemeted using the features lingua franca supplies. + * + * This is just an extract and simplification from the main project which you can find here: + * https://github.com/revol-xut/lf-alarm-clock + * + * This file contains the networking implementation it is really just an simple socket application + * which parses simple http headers and respondes in text/plain + * + * @author Tassilo Tanneberer + */ +target Cpp { cmake-include: "AlarmClock.cmake", keepalive: true -}; - -import Network from "./Network.lf"; -import Clock from "./Clock.lf"; +} -#import Network.lf; -#import Clock.lf; +import Network from "./Network.lf" +import Clock from "./Clock.lf" +// import Network.lf; +// import Clock.lf; main reactor AlarmClock { - clock = new Clock(); - network = new Network(); + clock = new Clock() + network = new Network() - // additon of a new event - network.event -> clock.event; - network.delete_index -> clock.cancel_by_index; - clock.event_dump -> network.updated_events; + network.event -> clock.event // additon of a new event + network.delete_index -> clock.cancel_by_index + clock.event_dump -> network.updated_events - reaction (startup) {= - std::cout << "Starting Lingua Franca AlarmClock" << std::endl; - =} + reaction(startup) {= std::cout << "Starting Lingua Franca AlarmClock" << std::endl; =} } - - diff --git a/Cpp/AlarmClock/src/Clock.lf b/Cpp/AlarmClock/src/Clock.lf index 753a1964..196d1d49 100644 --- a/Cpp/AlarmClock/src/Clock.lf +++ b/Cpp/AlarmClock/src/Clock.lf @@ -1,18 +1,15 @@ -/* -* This is a minimal example of an alarmclock implemeted using the -* features lingua franca supplies. -* -* This is just an extract and simplification from the main project -* which you can find here: https://github.com/revol-xut/lf-alarm-clock -* -* Author: Tassilo Tanneberer -*/ - - -target Cpp{ +/** + * This is a minimal example of an alarmclock implemeted using the features lingua franca supplies. + * + * This is just an extract and simplification from the main project which you can find here: + * https://github.com/revol-xut/lf-alarm-clock + * + * Author: Tassilo Tanneberer + */ +target Cpp { cmake-include: "AlarmClock.cmake", keepalive: true -}; +} public preamble {= #include "shared_header.hpp" @@ -23,21 +20,21 @@ reactor Trigger { auto convert_to_relative = [](long time_stamp){ const auto t = std::chrono::system_clock::now(); std::chrono::seconds desired_time = std::chrono::seconds(time_stamp); - std::chrono::seconds current_time = + std::chrono::seconds current_time = std::chrono::duration_cast(t.time_since_epoch()); std::chrono::seconds delta_t = desired_time - current_time; return delta_t; }; =} - input input_event: {=Event=}; - input input_interrupt: long; - logical action interrupt; - logical action triggered_event: {=std::string=}; - state ignore_flag: bool; + input input_event: {= Event =} + input input_interrupt: long + logical action interrupt + logical action triggered_event: {= std::string =} + state ignore_flag: bool - //the input_event will scheduled - reaction (input_event) -> triggered_event {= + // the input_event will scheduled + reaction(input_event) -> triggered_event {= if(input_event.is_present()) { auto extracted = input_event.get().get(); auto delta_t = convert_to_relative(extracted->time_stamp_); @@ -45,14 +42,14 @@ reactor Trigger { } =} - reaction (input_interrupt) -> interrupt {= + reaction(input_interrupt) -> interrupt {= if(input_interrupt.is_present()){ auto delta_t = convert_to_relative(*(input_interrupt.get().get())); interrupt.schedule(delta_t); } =} - // reaction which will be triggered when a event is due + // reaction which will be triggered when a event is due reaction(triggered_event) {= auto select_random_file = []{ std::vector files; @@ -74,9 +71,7 @@ reactor Trigger { ignore_flag = false; =} - reaction (interrupt) {= - ignore_flag = true; - =} + reaction(interrupt) {= ignore_flag = true; =} } reactor Clock { @@ -89,22 +84,17 @@ reactor Clock { } =} - // trigger reactor which handles the execution of the scheduled reaction - trigger = new Trigger(); - // this event will be scheduled and added to persistent storage - input event: Event; - input cancel_by_index: std::size_t; - // list of events - output event_dump: {= std::vector =}; - // timer which triggers clear and save - timer maintance(10 sec, 30 sec); + trigger = new Trigger() // trigger reactor which handles the execution of the scheduled reaction + input event: Event // this event will be scheduled and added to persistent storage + input cancel_by_index: std::size_t + output event_dump: {= std::vector =} // list of events + timer maintance(10 sec, 30 sec) // timer which triggers clear and save - // persistant storage - state events: std::vector(); + state events: std::vector() // persistant storage // reaction that appends new events which will be scheduled // the newtwork reactor is updated - reaction (event) -> trigger.input_event, event_dump {= + reaction(event) -> trigger.input_event, event_dump {= if (event.is_present() and not time_over(*event.get())){ trigger.input_event.set(*event.get()); events.push_back(*event.get()); @@ -112,8 +102,8 @@ reactor Clock { } =} - // initiation ... reading file to create state - reaction (startup) -> trigger.input_event, event_dump {= + // initiation ... reading file to create state + reaction(startup) -> trigger.input_event, event_dump {= // if the calender file doesn't exists it's created if (not std::filesystem::exists(kFile)){ std::ofstream{kFile}; @@ -155,16 +145,16 @@ reactor Clock { =} // state needs to be saved to file - reaction (shutdown, maintance) -> event_dump {= + reaction(shutdown, maintance) -> event_dump {= remove_events(); save(); event_dump.set(events); =} - reaction (cancel_by_index) -> trigger.input_interrupt, event_dump {= + reaction(cancel_by_index) -> trigger.input_interrupt, event_dump {= if(cancel_by_index.is_present()) { std::size_t index = *(cancel_by_index.get().get()); - + if( index < events.size()){ auto tag = events.at(index).time_stamp_; trigger.input_interrupt.set(tag); @@ -203,4 +193,3 @@ reactor Clock { file.close(); =} } - diff --git a/Cpp/AlarmClock/src/Network.lf b/Cpp/AlarmClock/src/Network.lf index 38ee1599..45aa78cc 100644 --- a/Cpp/AlarmClock/src/Network.lf +++ b/Cpp/AlarmClock/src/Network.lf @@ -1,21 +1,18 @@ -/* -* This is a minimal example of an alarmclock implemeted using the -* features lingua franca supplies. -* -* This is just an extract and simplification from the main project -* which you can find here: https://github.com/revol-xut/lf-alarm-clock - -* This file contains the networking implementation it is really just an -* simple socket application which parses simple http headers and respondes -* in text/plain -* -* @author Tassilo Tanneberer -*/ - -target Cpp{ +/** + * This is a minimal example of an alarmclock implemeted using the features lingua franca supplies. + * + * This is just an extract and simplification from the main project which you can find here: + * https://github.com/revol-xut/lf-alarm-clock + * + * This file contains the networking implementation it is really just an simple socket application + * which parses simple http headers and respondes in text/plain + * + * @author Tassilo Tanneberer + */ +target Cpp { cmake-include: "AlarmClock.cmake", keepalive: true -}; +} public preamble {= #include "shared_header.hpp" @@ -30,33 +27,33 @@ reactor Network { #include =} - // physical event which is triggered by receiving a request - physical action new_event: Event; - physical action delete_request: std::size_t; + physical action new_event: Event // physical event which is triggered by receiving a request + physical action delete_request: std::size_t // variables for the receive thread - state thread: std::thread; // receive thread - state events: std::vector; // copy + // receive thread + state thread: std::thread + state events: std::vector // copy - input updated_events: std::vector; - output event: Event; // event which will be added to the clock - output delete_index: std::size_t; + input updated_events: std::vector + output event: Event // event which will be added to the clock + output delete_index: std::size_t // this reaction transforms a physical action into a logical reaction - reaction (new_event) -> event {= + reaction(new_event) -> event {= if(new_event.is_present()){ event.set(new_event.get()); } =} - reaction (delete_request) -> delete_index {= + reaction(delete_request) -> delete_index {= if(delete_request.is_present()){ delete_index.set(delete_request.get()); } - =} + =} // main starts receive thread - reaction (startup) -> delete_request, new_event{= + reaction(startup) -> delete_request, new_event {= thread = std::thread([&] { crow::SimpleApp app; @@ -105,7 +102,7 @@ reactor Network { return crow::response(response); }); - // adds new event by relativ times + // adds new event by relativ times CROW_ROUTE(app, "/add_event_relative").methods("POST"_method) ([&new_event](const crow::request& req){ auto relativ_time = 0l; @@ -130,7 +127,7 @@ reactor Network { const auto now = std::chrono::system_clock::now(); auto current_time = std::chrono::duration_cast(now.time_since_epoch()).count(); - + std::cout << "current_time: " << current_time << " offset:" << relativ_time << std::endl; Event serialized_event { json_body["message"].s(), @@ -147,7 +144,7 @@ reactor Network { // will set the timer in the text 24 hours CROW_ROUTE(app, "/add_event_time").methods("POST"_method) ([&new_event](const crow::request& req){ - // just % doesn't work because it is the remainder operator + // just % doesn't work because it is the remainder operator // and does not behave like modulo for negative numbers auto mod = [](int a, int b) { int r = a % b; @@ -215,11 +212,8 @@ reactor Network { app.port(kPort).multithreaded().run(); }); =} - reaction (updated_events) {= - events = std::move(*updated_events.get()); - =} - reaction ( shutdown ) {= - thread.join(); - =} + reaction(updated_events) {= events = std::move(*updated_events.get()); =} + + reaction(shutdown) {= thread.join(); =} } diff --git a/Cpp/CarBrake/src/CarBrake.lf b/Cpp/CarBrake/src/CarBrake.lf index ac857777..a1f3957a 100644 --- a/Cpp/CarBrake/src/CarBrake.lf +++ b/Cpp/CarBrake/src/CarBrake.lf @@ -1,40 +1,38 @@ /** - * The given Lingua Franca (LF) program models a simplified automatic braking - * system in a car, which consists of a camera, a braking assistant, a brake pedal, - * and a brake. The camera continuously generates frames at a regular interval of - * 20 milliseconds. The braking assistant processes these frames and sends a brake - * signal every 10 frames, simulating an automatic braking decision based on the - * image processing. Simultaneously, a simulated brake pedal is "pressed" roughly - * every second, triggering a manual brake signal. Both automatic and manual brake - * signals are directed to the brake reactor, which reacts by outputting a message - * indicating that the brake has been triggered and the source of the signal. It - * also checks whether the reactions to these signals meet certain deadlines (3 - * milliseconds for manual and 15 milliseconds for automatic braking), printing an - * error message if a deadline is violated. + * The given Lingua Franca (LF) program models a simplified automatic braking system in a car, which + * consists of a camera, a braking assistant, a brake pedal, and a brake. The camera continuously + * generates frames at a regular interval of 20 milliseconds. The braking assistant processes these + * frames and sends a brake signal every 10 frames, simulating an automatic braking decision based + * on the image processing. Simultaneously, a simulated brake pedal is "pressed" roughly every + * second, triggering a manual brake signal. Both automatic and manual brake signals are directed to + * the brake reactor, which reacts by outputting a message indicating that the brake has been + * triggered and the source of the signal. It also checks whether the reactions to these signals + * meet certain deadlines (3 milliseconds for manual and 15 milliseconds for automatic braking), + * printing an error message if a deadline is violated. */ target Cpp { cmake-include: "threads.cmake" -}; +} reactor Camera { - timer t(20msecs, 20msecs) + timer t(20 msecs, 20 msecs) output frame: void - reaction (t) -> frame {= + reaction(t) -> frame {= frame.set(); // send a "frame" =} } reactor BrakingAssistant { - input frame: void; - output trigger_brake: void; - - state counter: int(0); + input frame: void + output trigger_brake: void + + state counter: int(0) reaction(frame) -> trigger_brake {= // processing takes some time std::this_thread::sleep_for(10ms); - + if (counter % 10 == 0) { std::cout << "[automatic] Send the brake signal - " << get_physical_time() << std::endl; trigger_brake.set(); @@ -44,10 +42,10 @@ reactor BrakingAssistant { } reactor BrakePedal { - physical action pedal; - output trigger_brake: void; + physical action pedal + output trigger_brake: void - state thread: std::thread; + state thread: std::thread reaction(startup) -> pedal {= this->thread = std::thread([&] () { @@ -65,9 +63,7 @@ reactor BrakePedal { trigger_brake.set(); =} - reaction(shutdown) {= - thread.join(); - =} + reaction(shutdown) {= thread.join(); =} } reactor Brake { @@ -75,31 +71,31 @@ reactor Brake { #include =} - input brake_assistant: void; - input brake_pedal: void; - + input brake_assistant: void + input brake_pedal: void + reaction(brake_pedal) {= std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; std::cout << "[system] source: manual" << std::endl; - =} deadline (3msecs) {= + =} deadline(3 msecs) {= std::cout << "\033[1;31m[error]\033[0m Deadline on manual braking violated - " << get_physical_time() << std::endl; =} - + reaction(brake_assistant) {= std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; std::cout << "[system] source: assistant" << std::endl; - =} deadline (15msecs) {= + =} deadline(15 msecs) {= std::cout << "\033[1;31m[error]\033[0m Deadline on automatic braking violated - " << get_physical_time() << std::endl; =} } main reactor { - camera = new Camera(); - assistant = new BrakingAssistant(); - pedal = new BrakePedal(); - brake = new Brake(); - - camera.frame -> assistant.frame; - assistant.trigger_brake -> brake.brake_assistant; - pedal.trigger_brake -> brake.brake_pedal; + camera = new Camera() + assistant = new BrakingAssistant() + pedal = new BrakePedal() + brake = new Brake() + + camera.frame -> assistant.frame + assistant.trigger_brake -> brake.brake_assistant + pedal.trigger_brake -> brake.brake_pedal } diff --git a/Cpp/CarBrake/src/CarBrake2.lf b/Cpp/CarBrake/src/CarBrake2.lf index 56b7841a..d3ede35a 100644 --- a/Cpp/CarBrake/src/CarBrake2.lf +++ b/Cpp/CarBrake/src/CarBrake2.lf @@ -1,26 +1,26 @@ /** - * This version of the CarBreak example decouples the Vision analysis. - * If this execution is federated, then the Vision component has no effect - * on the ability to meet deadlines in the response to brake pedal actions. + * This version of the CarBreak example decouples the Vision analysis. If this execution is + * federated, then the Vision component has no effect on the ability to meet deadlines in the + * response to brake pedal actions. */ target Cpp { cmake-include: "threads.cmake" -}; +} reactor Camera { - timer t(20msecs, 20msecs) + timer t(20 msecs, 20 msecs) output frame: void - reaction (t) -> frame {= + reaction(t) -> frame {= frame.set(); // send a "frame" =} } reactor BrakingAssistant { - input frame: void; - output trigger_brake: void; + input frame: void + output trigger_brake: void - state counter: int(0); + state counter: int(0) reaction(frame) -> trigger_brake {= // processing takes some time @@ -30,15 +30,15 @@ reactor BrakingAssistant { std::cout << "[automatic] Send the brake signal - " << get_physical_time() << std::endl; trigger_brake.set(); } - counter++; + counter++; =} } reactor BrakePedal { - physical action pedal; - output trigger_brake: void; + physical action pedal + output trigger_brake: void - state thread: std::thread; + state thread: std::thread reaction(startup) -> pedal {= this->thread = std::thread([&] () { @@ -57,9 +57,7 @@ reactor BrakePedal { trigger_brake.set(); =} - reaction(shutdown) {= - thread.join(); - =} + reaction(shutdown) {= thread.join(); =} } reactor Brake { @@ -67,45 +65,45 @@ reactor Brake { #include =} - input brake_assistant: void; - input brake_pedal: void; + input brake_assistant: void + input brake_pedal: void reaction(brake_pedal) {= std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; std::cout << "[system] source: manual" << std::endl; - =} deadline (3msecs) {= + =} deadline(3 msecs) {= std::cout << "\033[1;31m[error]\033[0m Deadline on manual braking violated - " << get_physical_time() << std::endl; =} reaction(brake_assistant) {= std::cout << "[system] Brake triggered - " << get_physical_time() << std::endl; std::cout << "[system] source: assistant" << std::endl; - =} deadline (15msecs) {= + =} deadline(15 msecs) {= std::cout << "\033[1;31m[error]\033[0m Deadline on automatic braking violated - " << get_physical_time() << std::endl; =} } reactor Braking { - input brake_assistant: void; - pedal = new BrakePedal(); - brake = new Brake(); + input brake_assistant: void + pedal = new BrakePedal() + brake = new Brake() - pedal.trigger_brake -> brake.brake_pedal; - brake_assistant -> brake.brake_assistant; + pedal.trigger_brake -> brake.brake_pedal + brake_assistant -> brake.brake_assistant } reactor Vision { - output trigger_brake: void; - camera = new Camera(); - assistant = new BrakingAssistant(); + output trigger_brake: void + camera = new Camera() + assistant = new BrakingAssistant() - camera.frame -> assistant.frame; - assistant.trigger_brake -> trigger_brake; + camera.frame -> assistant.frame + assistant.trigger_brake -> trigger_brake } main reactor { - braking = new Braking(); - vision = new Vision(); + braking = new Braking() + vision = new Vision() - vision.trigger_brake ~> braking.brake_assistant; + vision.trigger_brake ~> braking.brake_assistant } diff --git a/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf b/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf index 52e9b46e..9f620071 100644 --- a/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf +++ b/Cpp/Patterns/src/FullyConnected_00_Broadcast.lf @@ -1,26 +1,23 @@ /** - * This illustrates bank of reactors where each reactor produces an - * output that is broadcast to all reactors in the bank, including - * itself. - * + * This illustrates bank of reactors where each reactor produces an output that is broadcast to all + * reactors in the bank, including itself. + * * @author Christian Menard * @author Edward A. Lee */ - -target Cpp { -} +target Cpp reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) { input[num_nodes] in: size_t output out: size_t - reaction (startup) -> out{= + reaction(startup) -> out {= std::cout << "Hello from node " << bank_index << "!\n"; // broadcast my ID to everyone out.set(bank_index); =} - reaction (in) {= + reaction(in) {= std::cout << "Node " << bank_index << " received messages from "; for (auto& port : in) { if (port.is_present()) { @@ -32,7 +29,6 @@ reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) { } main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - (nodes.out)+ -> nodes.in; + nodes = new[num_nodes] Node(num_nodes=num_nodes) + (nodes.out)+ -> nodes.in } - diff --git a/Cpp/Patterns/src/FullyConnected_01_Addressable.lf b/Cpp/Patterns/src/FullyConnected_01_Addressable.lf index 23752214..e007d3c9 100644 --- a/Cpp/Patterns/src/FullyConnected_01_Addressable.lf +++ b/Cpp/Patterns/src/FullyConnected_01_Addressable.lf @@ -1,28 +1,24 @@ /** - * This illustrates bank of reactors where each reactor produces an - * output that is sent to a reactor in the bank of its choice. In this - * particular example, each reactor chooses to send its output to the - * reactor with the next higher bank_index, wrapping around when it gets - * to the end of the bank. - * + * This illustrates bank of reactors where each reactor produces an output that is sent to a reactor + * in the bank of its choice. In this particular example, each reactor chooses to send its output to + * the reactor with the next higher bank_index, wrapping around when it gets to the end of the bank. + * * @author Christian Menard * @author Edward A. Lee */ - -target Cpp { -} +target Cpp reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) { input[num_nodes] in: size_t output[num_nodes] out: size_t - reaction (startup) -> out{= + reaction(startup) -> out {= std::cout << "Hello from node " << bank_index << "!\n"; // send my ID only to my right neighbour out[(bank_index + 1) % num_nodes].set(bank_index); =} - reaction (in) {= + reaction(in) {= std::cout << "Node " << bank_index << " received messages from "; for (auto& port : in) { if (port.is_present()) { @@ -34,7 +30,6 @@ reactor Node(bank_index: size_t(0), num_nodes: size_t(4)) { } main reactor(num_nodes: size_t(4)) { - nodes = new[num_nodes] Node(num_nodes=num_nodes); - nodes.out -> interleaved(nodes.in); + nodes = new[num_nodes] Node(num_nodes=num_nodes) + nodes.out -> interleaved(nodes.in) } - diff --git a/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf b/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf index 16e0dfb2..796b116f 100644 --- a/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf +++ b/Cpp/Patterns/src/MatrixConnectedRowsAndColumns.lf @@ -1,10 +1,8 @@ -// This pattern creates a matrix of nodes, where each of the nodes can send +// This pattern creates a matrix of nodes, where each of the nodes can send // messages to all other nodes in the same row or in the same column. Since -// banks in LF are one dimensional, we use hierachy to implement the second +// banks in LF are one dimensional, we use hierachy to implement the second // dimension. Nodes are organized in Rows which are grouped to form the matrix. - -target Cpp { -}; +target Cpp public preamble {= struct Pos { @@ -22,7 +20,11 @@ private preamble {= } =} -reactor Node(bank_index: size_t(0), row_index: size_t(0), num_rows: size_t(4), num_cols:size_t(4)) { +reactor Node( + bank_index: size_t(0), + row_index: size_t(0), + num_rows: size_t(4), + num_cols: size_t(4)) { input[num_cols] fromRow: Pos input[num_rows] fromCol: Pos @@ -30,13 +32,13 @@ reactor Node(bank_index: size_t(0), row_index: size_t(0), num_rows: size_t(4), n state pos: Pos{bank_index, row_index} - reaction (startup) -> toRowAndCol {= + reaction(startup) -> toRowAndCol {= std::cout << "Hello from " << pos << '\n'; // send my position to everyone else in my row and column toRowAndCol.set(pos); =} - reaction (fromRow) {= + reaction(fromRow) {= std::cout << pos << " received row messages from: "; for (auto& port : fromRow) { if (port.is_present()) { @@ -46,7 +48,7 @@ reactor Node(bank_index: size_t(0), row_index: size_t(0), num_rows: size_t(4), n std::cout << '\n'; =} - reaction (fromCol) {= + reaction(fromCol) {= std::cout << pos << " received col messages from: "; for (auto& port : fromCol) { if (port.is_present()) { @@ -57,19 +59,18 @@ reactor Node(bank_index: size_t(0), row_index: size_t(0), num_rows: size_t(4), n =} } -reactor Row(bank_index: size_t(0), num_rows:size_t(4), num_cols:size_t(4)) { +reactor Row(bank_index: size_t(0), num_rows: size_t(4), num_cols: size_t(4)) { nodes = new[num_cols] Node(row_index=bank_index, num_rows=num_rows, num_cols=num_cols) - input[{=num_rows * num_cols=}] fromCol: Pos + input[{= num_rows * num_cols =}] fromCol: Pos output[num_cols] toCol: Pos (nodes.toRowAndCol)+ -> nodes.fromRow - nodes.toRowAndCol -> toCol + nodes.toRowAndCol -> toCol fromCol -> interleaved(nodes.fromCol) } -main reactor (num_rows:size_t(4), num_cols:size_t(4)) { +main reactor(num_rows: size_t(4), num_cols: size_t(4)) { rows = new[num_rows] Row(num_rows=num_rows, num_cols=num_cols) - (rows.toCol)+ -> rows.fromCol; + (rows.toCol)+ -> rows.fromCol } - diff --git a/Cpp/ROS2/src/MinimalPublisher.lf b/Cpp/ROS2/src/MinimalPublisher.lf index 8206eea7..18fe48a1 100644 --- a/Cpp/ROS2/src/MinimalPublisher.lf +++ b/Cpp/ROS2/src/MinimalPublisher.lf @@ -1,15 +1,14 @@ /** - * The provided Lingua Franca (LF) program is designed to publish messages onto a - * ROS2 topic at a regular interval. Specifically, it publishes a - * 'std_msgs/msg/String' message on the topic named "topic" every 500 milliseconds. - * The message contains a string "Hello, world!" followed by a counter value that - * increments with each published message. This LF program illustrates a simple - * example of a ROS2 publisher, making use of LF's precise timing semantics to - * ensure regular message publication. + * The provided Lingua Franca (LF) program is designed to publish messages onto a ROS2 topic at a + * regular interval. Specifically, it publishes a 'std_msgs/msg/String' message on the topic named + * "topic" every 500 milliseconds. The message contains a string "Hello, world!" followed by a + * counter value that increments with each published message. This LF program illustrates a simple + * example of a ROS2 publisher, making use of LF's precise timing semantics to ensure regular + * message publication. */ target Cpp { ros2: true, - ros2-dependencies: ["std_msgs"], + ros2-dependencies: ["std_msgs"] } public preamble {= @@ -28,9 +27,7 @@ main reactor { timer t(0, 500 ms) - reaction(startup) {= - publisher = lf_node->create_publisher("topic", 10); - =} + reaction(startup) {= publisher = lf_node->create_publisher("topic", 10); =} reaction(t) {= auto message = std_msgs::msg::String(); diff --git a/Cpp/ROS2/src/MinimalSubscriber.lf b/Cpp/ROS2/src/MinimalSubscriber.lf index 899bc7c5..406fc07a 100644 --- a/Cpp/ROS2/src/MinimalSubscriber.lf +++ b/Cpp/ROS2/src/MinimalSubscriber.lf @@ -1,16 +1,15 @@ /** - * The provided Lingua Franca (LF) program represents a simple ROS2 subscriber that - * listens for messages on a particular topic. Specifically, it subscribes to a - * topic named "topic" and waits for 'std_msgs/msg/String' type messages. When a - * message arrives, it is scheduled as a physical action, triggering a reaction - * that logs the received message. Therefore, this program demonstrates how an LF - * program can interface with ROS2 to receive and process messages from a ROS2 - * topic in a reactive manner. + * The provided Lingua Franca (LF) program represents a simple ROS2 subscriber that listens for + * messages on a particular topic. Specifically, it subscribes to a topic named "topic" and waits + * for 'std_msgs/msg/String' type messages. When a message arrives, it is scheduled as a physical + * action, triggering a reaction that logs the received message. Therefore, this program + * demonstrates how an LF program can interface with ROS2 to receive and process messages from a + * ROS2 topic in a reactive manner. */ target Cpp { ros2: true, keepalive: true, - ros2-dependencies: ["std_msgs"], + ros2-dependencies: ["std_msgs"] } public preamble {= @@ -27,7 +26,7 @@ main reactor { state subscription: {= rclcpp::Subscription::SharedPtr =} state count: unsigned(0) - physical action message: std::string; + physical action message: std::string reaction(startup) -> message {= subscription = lf_node->create_subscription( @@ -36,7 +35,5 @@ main reactor { // const std_msgs::msg::String::SharedPtr& msg =} - reaction(message) {= - reactor::log::Info() << "I heard: " << *message.get(); - =} + reaction(message) {= reactor::log::Info() << "I heard: " << *message.get(); =} } diff --git a/Cpp/ReflexGame/src/ReflexGame.lf b/Cpp/ReflexGame/src/ReflexGame.lf index 35d13d69..4f5074da 100644 --- a/Cpp/ReflexGame/src/ReflexGame.lf +++ b/Cpp/ReflexGame/src/ReflexGame.lf @@ -1,8 +1,7 @@ /** - * This example illustrates the use of logical and physical actions, - * asynchronous external inputs, the use of startup and shutdown - * reactions, and the use of actions with values. - * + * This example illustrates the use of logical and physical actions, asynchronous external inputs, + * the use of startup and shutdown reactions, and the use of actions with values. + * * @author Felix Wittwer * @author Edward A. Lee * @author Marten Lohstroh @@ -10,29 +9,29 @@ target Cpp { keepalive: true, cmake-include: "ReflexGame.cmake" -}; +} /** - * Produce a counting sequence at random times with a minimum - * and maximum time between outputs specified as parameters. - * + * Produce a counting sequence at random times with a minimum and maximum time between outputs + * specified as parameters. + * * @param min_time The minimum time between outputs. * @param max_time The maximum time between outputs. */ -reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { +reactor RandomSource(min_time: time(2 sec), max_time: time(8 sec)) { private preamble {= - // Generate a random additional delay over the minimum. + // Generate a random additional delay over the minimum. // Assume millisecond precision is enough. reactor::Duration additional_time(reactor::Duration min_time, reactor::Duration max_time) { int interval_in_msec = (max_time - min_time) / std::chrono::milliseconds(1); return (std::rand() % interval_in_msec) * std::chrono::milliseconds(1); } =} - input another: void; - output out: void; - logical action prompt(min_time); - state count: int(0); - + input another: void + output out: void + logical action prompt(min_time) + state count: int(0) + reaction(startup) -> prompt {= std::cout << "***********************************************" << std::endl; std::cout << "Watch for the prompt, then hit Return or Enter." << std::endl; @@ -45,11 +44,13 @@ reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { // Schedule the first event. prompt.schedule(additional_time(0ms, max_time - min_time)); =} + reaction(prompt) -> out {= count++; std::cout << count << ". Hit Return or Enter!" << std::endl << std::flush; out.set(); =} + reaction(another) -> prompt {= // Schedule the next event. prompt.schedule(additional_time(0ms, max_time - min_time)); @@ -57,24 +58,23 @@ reactor RandomSource(min_time:time(2 sec), max_time:time(8 sec)) { } /** - * Upon receiving a prompt, record the time of the prompt, - * then listen for user input. When the user hits return, - * then schedule a physical action that records the time - * of this event and then report the response time. + * Upon receiving a prompt, record the time of the prompt, then listen for user input. When the user + * hits return, then schedule a physical action that records the time of this event and then report + * the response time. */ reactor GetUserInput { public preamble {= #include =} - physical action user_response: char; - state prompt_time: {= reactor::TimePoint =} ({= reactor::TimePoint::min() =}); - state total_time: time(0); - state count: int(0); - state thread: {= std::thread =}; + physical action user_response: char + state prompt_time: {= reactor::TimePoint =}({= reactor::TimePoint::min() =}) + state total_time: time(0) + state count: int(0) + state thread: {= std::thread =} - input prompt: void; - output another: void; + input prompt: void + output another: void reaction(startup) -> user_response {= // Start the thread that listens for Enter or Return. @@ -86,13 +86,11 @@ reactor GetUserInput { } user_response.schedule(c, 0ms); if (c == EOF) break; - } + } }); =} - reaction(prompt) {= - prompt_time = get_physical_time(); - =} + reaction(prompt) {= prompt_time = get_physical_time(); =} reaction(user_response) -> another {= auto c = user_response.get(); @@ -128,10 +126,10 @@ reactor GetUserInput { } =} } + main reactor ReflexGame { - p = new RandomSource(); - g = new GetUserInput(); - p.out -> g.prompt; - g.another -> p.another; + p = new RandomSource() + g = new GetUserInput() + p.out -> g.prompt + g.another -> p.another } - diff --git a/Cpp/RequestResponse/src/Add.lf b/Cpp/RequestResponse/src/Add.lf index 2cd17aac..16d0f6f6 100644 --- a/Cpp/RequestResponse/src/Add.lf +++ b/Cpp/RequestResponse/src/Add.lf @@ -1,27 +1,24 @@ /** - * The given Lingua Franca (LF) program represents a client-service model where the - * client sends addition requests to a service and receives responses. The client - * reactor generates a request every 100 milliseconds, each time asking the - * addition service to add its current counter value (which increments with each - * request) to 42. The request is sent to an instance of the 'AddService' (not - * shown in the code), which presumably performs the addition and returns the - * result. The client then logs the received response, displaying the result of the - * addition. This cycle continues indefinitely, with the client sending - * incrementally changing requests and logging the responses. + * The given Lingua Franca (LF) program represents a client-service model where the client sends + * addition requests to a service and receives responses. The client reactor generates a request + * every 100 milliseconds, each time asking the addition service to add its current counter value + * (which increments with each request) to 42. The request is sent to an instance of the + * 'AddService' (not shown in the code), which presumably performs the addition and returns the + * result. The client then logs the received response, displaying the result of the addition. This + * cycle continues indefinitely, with the client sending incrementally changing requests and logging + * the responses. */ target Cpp import AddService from "AddService.lf" - reactor Client { - timer t(0, 100ms) + timer t(0, 100 ms) output add_request: Request> input add_response: Response state counter: int(0) - reaction(t) -> add_request {= auto req = Request(std::make_pair(counter, 42)); add_request.set(req); @@ -29,9 +26,7 @@ reactor Client { counter++; =} - reaction(add_response) {= - reactor::log::Info() << "It is " << add_response.get()->data(); - =} + reaction(add_response) {= reactor::log::Info() << "It is " << add_response.get()->data(); =} } main reactor { diff --git a/Cpp/RequestResponse/src/AddWithContext.lf b/Cpp/RequestResponse/src/AddWithContext.lf index ccbc2eae..282f1097 100644 --- a/Cpp/RequestResponse/src/AddWithContext.lf +++ b/Cpp/RequestResponse/src/AddWithContext.lf @@ -3,20 +3,25 @@ target Cpp import AddService from "AddService.lf" import ContextManager from "ContextManager.lf" - reactor Client { - timer t(0, 100ms) + timer t(0, 100 ms) output add_request: Request> input add_response: Response state counter: int(0) - add_cm = new ContextManager<{=Request>=}, {=Response=}, {=std::function=}>() + add_cm = new ContextManager< + {= Request> =}, + {= Response =}, + {= std::function =}>() + + add_cm.request_out -> add_request + add_response -> add_cm.response_in reaction(t) -> add_cm.request_in {= auto req = Request(std::make_pair(counter, 42)); - int c = counter; // This is a weird corner case in C++ where the clojure below - // cannot capture counter by value. Copying it to a local variable helps... + int c = counter; // This is a weird corner case in C++ where the clojure below + // cannot capture counter by value. Copying it to a local variable helps... auto callback = [c](int sum) { reactor::log::Info() << "Result: " << c << " + 42 = " << sum; }; @@ -30,9 +35,6 @@ reactor Client { auto const& callback = add_cm.response_out.get()->second; callback(resp.data()); =} - - add_cm.request_out -> add_request - add_response -> add_cm.response_in } main reactor { diff --git a/Cpp/RequestResponse/src/ContextManager.lf b/Cpp/RequestResponse/src/ContextManager.lf index d3bfdd96..238c194c 100644 --- a/Cpp/RequestResponse/src/ContextManager.lf +++ b/Cpp/RequestResponse/src/ContextManager.lf @@ -13,14 +13,14 @@ reactor ContextManager { state context_buffer: {= std::map =} - reaction (request_in) -> request_out {= + reaction(request_in) -> request_out {= const auto& req = request_in.get()->first; const auto& ctx = request_in.get()->second; context_buffer[req.uid()] = ctx; request_out.set(req); =} - reaction (response_in) -> response_out {= + reaction(response_in) -> response_out {= const auto& resp = *response_in.get(); const auto& ctx = context_buffer[resp.uid()]; response_out.set(std::make_pair(resp, ctx)); diff --git a/Cpp/RequestResponse/src/MAC.lf b/Cpp/RequestResponse/src/MAC.lf index ce3db56c..42c93c62 100644 --- a/Cpp/RequestResponse/src/MAC.lf +++ b/Cpp/RequestResponse/src/MAC.lf @@ -13,7 +13,6 @@ public preamble {= =} reactor MACService { - input request: Request output response: Response @@ -23,8 +22,19 @@ reactor MACService { output add_request: Request> input add_response: Response - add_cm = new ContextManager<{=Request>=}, {=Response=}, {=Request=}>() - mul_cm = new ContextManager<{=Request>=}, {=Response=}, {=Request=}>() + add_cm = new ContextManager< + {= Request> =}, + {= Response =}, + {= Request =}>() + mul_cm = new ContextManager< + {= Request> =}, + {= Response =}, + {= Request =}>() + + mul_cm.request_out -> mul_request + add_cm.request_out -> add_request + mul_response -> mul_cm.response_in + add_response -> add_cm.response_in reaction(request) -> mul_cm.request_in {= auto& original_request = *request.get(); @@ -45,27 +55,25 @@ reactor MACService { auto& original_request = add_cm.response_out.get()->second; response.set(original_request.make_response(result)); =} - - mul_cm.request_out -> mul_request - add_cm.request_out -> add_request - mul_response -> mul_cm.response_in - add_response -> add_cm.response_in } reactor Client { - timer t(0, 100ms) + timer t(0, 100 ms) output mac_request: Request input mac_response: Response state counter: int(0) - mac_cm = new ContextManager<{=Request=}, {=Response=}, MACData>() + mac_cm = new ContextManager<{= Request =}, {= Response =}, MACData>() + + mac_cm.request_out -> mac_request + mac_response -> mac_cm.response_in reaction(t) -> mac_cm.request_in {= auto data = MACData{counter, counter + 1, counter + 2}; auto req = Request(data); mac_cm.request_in.set(std::make_pair(req, data)); - reactor::log::Info() << "Client asks what " << data.factor1 << " * " << data.factor2 + reactor::log::Info() << "Client asks what " << data.factor1 << " * " << data.factor2 << " + " << data.summand << " is"; counter++; =} @@ -73,12 +81,9 @@ reactor Client { reaction(mac_cm.response_out) {= auto const result = mac_cm.response_out.get()->first.data(); auto const& data = mac_cm.response_out.get()->second; - reactor::log::Info() << "Result: " << data.factor1 << " * " << data.factor2 + reactor::log::Info() << "Result: " << data.factor1 << " * " << data.factor2 << " + " << data.summand << " = " << result; =} - - mac_cm.request_out -> mac_request - mac_response -> mac_cm.response_in } main reactor { diff --git a/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf b/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf index 795c7956..a110e132 100644 --- a/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf +++ b/Python/src/DigitalTwin/DoubleUnlock/DoubleUnlockDemo.lf @@ -1,16 +1,15 @@ /** - * Example of a basic digital twin setup, with two federates - * maintaining a shared state "lock state". + * 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 (housengw@berkeley.edu) */ - - target Python { +target Python { docker: true, files: ["../utils.py"] -}; +} preamble {= import curses @@ -30,32 +29,32 @@ preamble {= =} /** - * A key fob that detects "lock" and "unlock" key presses, - * and sends and receives lock state to and from other key fobs. + * 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); +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): @@ -75,7 +74,7 @@ reactor DoubleUnlockKeyFob(auto_lock_duration(5)) { 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}), " @@ -92,12 +91,12 @@ reactor DoubleUnlockKeyFob(auto_lock_duration(5)) { key = "" while key != ord("q"): key = self.window.getch() - if key == ord("l"): + 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 =} @@ -132,16 +131,12 @@ reactor DoubleUnlockKeyFob(auto_lock_duration(5)) { t.start() =} - reaction(press_lock) -> handle_press_lock {= - handle_press_lock.schedule(0) - =} + reaction(press_lock) -> handle_press_lock {= handle_press_lock.schedule(0) =} - reaction(press_unlock) -> handle_press_unlock {= - handle_press_unlock.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) + self.append_log(auto=False, remote=False, do_lock=True) do_lock.schedule(0, True) send_lock_action.set(True) =} @@ -193,8 +188,8 @@ reactor DoubleUnlockKeyFob(auto_lock_duration(5)) { } federated reactor { - fob = new DoubleUnlockKeyFob(); - twin = new DoubleUnlockKeyFob(); - fob.send_lock_action -> twin.get_lock_action; - twin.send_lock_action -> fob.get_lock_action; + fob = new DoubleUnlockKeyFob() + twin = new DoubleUnlockKeyFob() + fob.send_lock_action -> twin.get_lock_action + twin.send_lock_action -> fob.get_lock_action } diff --git a/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf b/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf index a0527167..3d07efae 100644 --- a/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf +++ b/Python/src/DigitalTwin/DoubleUnlock/Simulator.lf @@ -1,30 +1,27 @@ /** - * Example of a reactor that sends simulated key presses at arbitrary - * logical time to the key fobs. + * 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 (housengw@berkeley.edu) */ - target Python { docker: true, files: ["../utils.py"] -}; +} -import DoubleUnlockKeyFob from "DoubleUnlockDemo.lf"; +import DoubleUnlockKeyFob from "DoubleUnlockDemo.lf" +reactor DoubleUnlockKeyFobTester(initial_delay=5) { + state logger = {= None =} + state window = {= None =} -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 - 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)) @@ -53,11 +50,11 @@ reactor DoubleUnlockKeyFobTester(initial_delay(5)) { } 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; + 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 } diff --git a/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf b/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf index e7afef5e..bd8f90fc 100644 --- a/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf +++ b/Python/src/DigitalTwin/KeyFob/KeyFobDemo.lf @@ -1,16 +1,14 @@ /** - * Example of a basic digital twin setup, with two federates - * maintaining a shared state "locked". + * Example of a basic digital twin setup, with two federates maintaining a shared state "locked". * * For run instructions, see README.md in the same directory. - * + * * @author Hou Seng Wong (housengw@berkeley.edu) */ - target Python { docker: true, files: ["../utils.py"] -}; +} preamble {= import curses @@ -19,28 +17,28 @@ preamble {= =} /** - * A key fob that detects "lock" and "unlock" key presses, - * and sends and receives lock state to and from other key fobs. + * A key fob that detects "lock" and "unlock" key presses, and sends and receives lock state to and + * from other key fobs. */ reactor KeyFob { - /* logger / window related state variables */ - state logger({=None=}); - state window({=None=}); + /** logger / window related state variables */ + state logger = {= None =} + state window = {= None =} - /* KeyFob related state variables */ - state locked({=False=}); - state listener({=None=}); + /** KeyFob related state variables */ + state locked = {= False =} + state listener = {= None =} - /* I/O ports and actions */ - input get_lock_state; - output send_lock_state; - physical action press_lock; - physical action press_unlock; + /** I/O ports and actions */ + input get_lock_state + output send_lock_state + physical action press_lock + physical action press_unlock preamble {= def lock_state_str(self, locked): return "Locked" if locked else "Unlocked" - + def print_lock_state(self): self.window.change_line(1, f"Lock Status: {self.lock_state_str(self.locked)}") @@ -48,7 +46,7 @@ reactor KeyFob { if self.logger.log_size() > 0: for i, line in enumerate(self.logger.get_log()): self.window.change_line(2 + i, line) - + def format_log_message(self, line): elapsed_ptime, tag, remote, locked = line return (f"At (tag: ({'{:,}'.format(tag.time)} ns, {tag.microstep}), " @@ -65,7 +63,7 @@ reactor KeyFob { key = "" while key != ord("q"): key = self.window.getch() - if key == ord("l"): + if key == ord("l"): press_lock.schedule(0) elif key == ord("u"): press_unlock.schedule(0) @@ -120,8 +118,8 @@ reactor KeyFob { } federated reactor { - fob = new KeyFob(); - twin = new KeyFob(); - fob.send_lock_state -> twin.get_lock_state; - twin.send_lock_state -> fob.get_lock_state; + fob = new KeyFob() + twin = new KeyFob() + fob.send_lock_state -> twin.get_lock_state + twin.send_lock_state -> fob.get_lock_state } diff --git a/Python/src/Piano/Piano.lf b/Python/src/Piano/Piano.lf index f4ac1e74..b4cd8d3d 100644 --- a/Python/src/Piano/Piano.lf +++ b/Python/src/Piano/Piano.lf @@ -1,24 +1,19 @@ /** - * This Lingua Franca program simulates a virtual piano that listens for user key - * presses, translates them into corresponding piano notes, plays the associated - * sounds, and updates the graphical interface accordingly. At the start, it - * initializes a FluidSynth instance for playing MIDI sounds and a GUI for the - * piano interface. As the user interacts with the program by pressing keys, these - * key presses are captured and translated into notes. The program then plays the - * corresponding sounds using FluidSynth and updates the GUI to reflect the keys - * that are currently being pressed. The program is designed to run continuously - * until the user decides to stop. + * This Lingua Franca program simulates a virtual piano that listens for user key presses, + * translates them into corresponding piano notes, plays the associated sounds, and updates the + * graphical interface accordingly. At the start, it initializes a FluidSynth instance for playing + * MIDI sounds and a GUI for the piano interface. As the user interacts with the program by pressing + * keys, these key presses are captured and translated into notes. The program then plays the + * corresponding sounds using FluidSynth and updates the GUI to reflect the keys that are currently + * being pressed. The program is designed to run continuously until the user decides to stop. */ target Python { files: [gui.py, keys.png, soundfont.sf2], threading: true, keepalive: true -}; - +} -/* - * Receives key presses from the pygame piano process - */ +/** Receives key presses from the pygame piano process */ reactor GetUserInput { preamble {= import threading @@ -29,40 +24,33 @@ reactor GetUserInput { except EOFError: request_stop() return - # Each time a key press is received, schedule a user_response event + # Each time a key press is received, schedule a user_response event user_response.schedule(0, c) =} - physical action user_response; - input user_input_pipe_init; - output user_input; - state user_input({=None=}) # multiprocessing.connection.PipeConnection - + physical action user_response + input user_input_pipe_init + output user_input + state user_input = {= None =} # multiprocessing.connection.PipeConnection + reaction(user_input_pipe_init) -> user_response {= # starts a thread to receive key presses from the pygame process self.user_input = user_input_pipe_init.value t = self.threading.Thread(target=self.listen_for_input, args=(user_response, )) t.start() =} - - reaction(user_response) -> user_input {= - user_input.set(user_response.value) - =} -} + reaction(user_response) -> user_input {= user_input.set(user_response.value) =} +} -/* - * Sends graphics updates to the pygame piano process - */ +/** Sends graphics updates to the pygame piano process */ reactor UpdateGraphics { - input note; - input update_graphics_pipe_init; - state update_graphics({=None=}); # multiprocessing.connection.PipeConnection - state pressed_keys({=set()=}) - - reaction(update_graphics_pipe_init) {= - self.update_graphics = update_graphics_pipe_init.value - =} - + input note + input update_graphics_pipe_init + state update_graphics = {= None =} # multiprocessing.connection.PipeConnection + state pressed_keys = {= set() =} + + reaction(update_graphics_pipe_init) {= self.update_graphics = update_graphics_pipe_init.value =} + reaction(note) {= key_down, note_t = note.value if key_down and note_t not in self.pressed_keys: @@ -74,22 +62,17 @@ reactor UpdateGraphics { =} } - -/* - * Plays sound using fluidsynth upon receiving signal from TranslateKeyToNote - */ +/** Plays sound using fluidsynth upon receiving signal from TranslateKeyToNote */ reactor PlaySound { - state lowest(4); # the octave of the lowest "C" on the piano. - state channel(8); - state Note; - state fluidsynth; - input note; - input play_sound_init; - - reaction(play_sound_init) {= - self.fluidsynth, self.Note = play_sound_init.value - =} - + state lowest = 4 # the octave of the lowest "C" on the piano. + state channel = 8 + state Note + state fluidsynth + input note + input play_sound_init + + reaction(play_sound_init) {= self.fluidsynth, self.Note = play_sound_init.value =} + reaction(note) {= # upon receiving a note, play or stop the note depending on if its a key down or key up. key_down, note_t = note.value @@ -100,48 +83,44 @@ reactor PlaySound { =} } -/* - * Translates key presses to piano keys and triggers the initialization of StartGui - */ +/** Translates key presses to piano keys and triggers the initialization of StartGui */ reactor TranslateKeyToNote { - preamble {= - piano_keys = { - "z": ("C", 0), - "s": ("C#", 0), - "x": ("D", 0), - "d": ("D#", 0), - "c": ("E", 0), - "v": ("F", 0), - "g": ("F#", 0), - "b": ("G", 0), - "h": ("G#", 0), - "n": ("A", 0), - "j": ("A#", 0), - "m": ("B", 0), - "w": ("C", 1), - "3": ("C#", 1), - "e": ("D", 1), - "4": ("D#", 1), - "r": ("E", 1), - "t": ("F", 1), - "6": ("F#", 1), - "y": ("G", 1), - "7": ("G#", 1), - "u": ("A", 1), - "8": ("A#", 1), - "i": ("B", 1) - } - =} - - input user_input; - input translate_init; - output note; - output gui_init; - - reaction(translate_init) -> gui_init {= - gui_init.set(self.piano_keys) + preamble {= + piano_keys = { + "z": ("C", 0), + "s": ("C#", 0), + "x": ("D", 0), + "d": ("D#", 0), + "c": ("E", 0), + "v": ("F", 0), + "g": ("F#", 0), + "b": ("G", 0), + "h": ("G#", 0), + "n": ("A", 0), + "j": ("A#", 0), + "m": ("B", 0), + "w": ("C", 1), + "3": ("C#", 1), + "e": ("D", 1), + "4": ("D#", 1), + "r": ("E", 1), + "t": ("F", 1), + "6": ("F#", 1), + "y": ("G", 1), + "7": ("G#", 1), + "u": ("A", 1), + "8": ("A#", 1), + "i": ("B", 1) + } =} - + + input user_input + input translate_init + output note + output gui_init + + reaction(translate_init) -> gui_init {= gui_init.set(self.piano_keys) =} + reaction(user_input) -> note {= key_down, c = user_input.value if c in self.piano_keys: @@ -153,7 +132,7 @@ reactor StartFluidSynth { preamble {= import sys import os - + try: from mingus.containers.note import Note except: @@ -173,10 +152,10 @@ reactor StartFluidSynth { request_stop() sys.exit(1) =} - state soundfont({=self.os.path.join(self.os.path.dirname(__file__), "soundfont.sf2")=}) - output translate_init; - output play_sound_init; - + state soundfont = {= self.os.path.join(self.os.path.dirname(__file__), "soundfont.sf2") =} + output translate_init + output play_sound_init + reaction(startup) -> play_sound_init, translate_init {= if not self.os.path.exists(self.soundfont): print("Error: Soundfont file does not exist.") @@ -185,7 +164,7 @@ reactor StartFluidSynth { print("Alternatively, pick and download a soundfont from here:") print("https://github.com/FluidSynth/fluidsynth/wiki/SoundFont") print("Rename the soundfont to \"soundfont.sf2\" and put it under the same directory as Piano.lf.") - request_stop() + request_stop() return # initialize fluidsynth @@ -196,23 +175,19 @@ reactor StartFluidSynth { print("Error: Failed to initialize fluidsynth") request_stop() return - + play_sound_init.set((self.fluidsynth, self.Note)) translate_init.set(0) =} } -/* - * Starts the GUI and triggers initialization of UpdateGraphics and GetUserInput reactors. - */ +/** Starts the GUI and triggers initialization of UpdateGraphics and GetUserInput reactors. */ reactor StartGui { - preamble {= - import gui - =} - input gui_init; - output user_input_pipe; - output update_graphics_pipe; - + preamble {= import gui =} + input gui_init + output user_input_pipe + output update_graphics_pipe + reaction(gui_init) -> user_input_pipe, update_graphics_pipe {= piano_keys = gui_init.value user_input_pout, update_graphics_pin = self.gui.start_gui(piano_keys) @@ -226,11 +201,11 @@ main reactor { fs = new StartFluidSynth() translate = new TranslateKeyToNote() update_graphics = new UpdateGraphics() - get_user_input = new GetUserInput() + get_user_input = new GetUserInput() play_sound = new PlaySound() - - fs.translate_init -> translate.translate_init; - fs.play_sound_init -> play_sound.play_sound_init; + + fs.translate_init -> translate.translate_init + fs.play_sound_init -> play_sound.play_sound_init gui.user_input_pipe -> get_user_input.user_input_pipe_init gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init get_user_input.user_input -> translate.user_input diff --git a/Python/src/ROS/PythonMigration/lf-python/Main.lf b/Python/src/ROS/PythonMigration/lf-python/Main.lf index 91920a86..60ece336 100644 --- a/Python/src/ROS/PythonMigration/lf-python/Main.lf +++ b/Python/src/ROS/PythonMigration/lf-python/Main.lf @@ -1,21 +1,19 @@ /** - * This Lingua Franca program demonstrates a federated reactor system that - * integrates a sender and a receiver, both interfacing with the Robot Operating - * System (ROS). The sender periodically publishes "Hello World" messages to a ROS - * topic, while the receiver listens to the same topic and processes incoming - * messages. The federated design ensures that the sender's output is directed to - * the receiver's input, creating a communication link between the two. It - * showcases the ability of Lingua Franca to support modular, distributed, - * real-time systems where different components interact and communicate - * seamlessly. + * This Lingua Franca program demonstrates a federated reactor system that integrates a sender and a + * receiver, both interfacing with the Robot Operating System (ROS). The sender periodically + * publishes "Hello World" messages to a ROS topic, while the receiver listens to the same topic and + * processes incoming messages. The federated design ensures that the sender's output is directed to + * the receiver's input, creating a communication link between the two. It showcases the ability of + * Lingua Franca to support modular, distributed, real-time systems where different components + * interact and communicate seamlessly. */ -target Python; +target Python import Sender from "Sender.lf" import Receiver from "Receiver.lf" federated reactor { - sender = new Sender(); - receiver = new Receiver(); - sender.topic -> receiver.topic; + sender = new Sender() + receiver = new Receiver() + sender.topic -> receiver.topic } diff --git a/Python/src/ROS/PythonMigration/lf-python/Receiver.lf b/Python/src/ROS/PythonMigration/lf-python/Receiver.lf index 13444af4..f3ab7194 100644 --- a/Python/src/ROS/PythonMigration/lf-python/Receiver.lf +++ b/Python/src/ROS/PythonMigration/lf-python/Receiver.lf @@ -1,20 +1,18 @@ /** - * This Lingua Franca program interfaces with the Robot Operating System (ROS) to - * receive and handle data from a specific topic using the MinimalSubscriber class - * from the ROS py_pubsub package. Upon startup, the program initializes the ROS - * communication and creates a MinimalSubscriber instance. When data arrives on the - * subscribed topic, it is passed to a callback function on the ROS side for - * further processing. Upon shutdown, the program destroys the node and shuts down - * the ROS communication, ensuring a clean exit. The program serves as an example - * of real-time, event-driven programming using Lingua Franca in combination with - * ROS. + * This Lingua Franca program interfaces with the Robot Operating System (ROS) to receive and handle + * data from a specific topic using the MinimalSubscriber class from the ROS py_pubsub package. Upon + * startup, the program initializes the ROS communication and creates a MinimalSubscriber instance. + * When data arrives on the subscribed topic, it is passed to a callback function on the ROS side + * for further processing. Upon shutdown, the program destroys the node and shuts down the ROS + * communication, ensuring a clean exit. The program serves as an example of real-time, event-driven + * programming using Lingua Franca in combination with ROS. */ -// Receiver.lf -target Python; +# Receiver.lf +target Python preamble {= # Locate the MinimalPublisher class written for ROS - # After source the ros package, + # After source the ros package, # Python import would be: # from [package name].[file name] import [class name] from py_pubsub.subscriber_member_function import MinimalSubscriber @@ -26,8 +24,8 @@ preamble {= =} reactor Receiver { - state minimal_subscriber; - input topic; + state minimal_subscriber + input topic reaction(startup) {= rclpy.init(args=None) diff --git a/Python/src/ROS/PythonMigration/lf-python/Sender.lf b/Python/src/ROS/PythonMigration/lf-python/Sender.lf index 2222fc08..1154d343 100644 --- a/Python/src/ROS/PythonMigration/lf-python/Sender.lf +++ b/Python/src/ROS/PythonMigration/lf-python/Sender.lf @@ -1,21 +1,20 @@ /** - * This Lingua Franca program interfaces with the Robot Operating System (ROS) to - * periodically publish messages on a specific topic using the MinimalPublisher - * class from the ROS py_pubsub package. The program initializes ROS communication - * and creates an instance of MinimalPublisher upon startup. Using a timer, it - * generates and publishes a new "Hello World" message every 500 milliseconds, - * incrementing a counter with each message. The message is output to the defined - * topic for consumption by other systems or processes. On shutdown, the program - * destroys the node and shuts down ROS communication, ensuring a clean exit. This - * program showcases the utility of Lingua Franca in enabling deterministic, - * real-time communication in robotics applications using ROS. + * This Lingua Franca program interfaces with the Robot Operating System (ROS) to periodically + * publish messages on a specific topic using the MinimalPublisher class from the ROS py_pubsub + * package. The program initializes ROS communication and creates an instance of MinimalPublisher + * upon startup. Using a timer, it generates and publishes a new "Hello World" message every 500 + * milliseconds, incrementing a counter with each message. The message is output to the defined + * topic for consumption by other systems or processes. On shutdown, the program destroys the node + * and shuts down ROS communication, ensuring a clean exit. This program showcases the utility of + * Lingua Franca in enabling deterministic, real-time communication in robotics applications using + * ROS. */ -// Sender.lf -target Python; +# Sender.lf +target Python preamble {= # Locate the MinimalPublisher class written for ROS - # After source the ros package, + # After source the ros package, # Python import would be: # from [package name].[file name] import [class name] from py_pubsub.publisher_member_function import MinimalPublisher @@ -27,14 +26,14 @@ preamble {= =} reactor Sender { - state minimal_publisher; - timer t(0, 500ms); - output topic; + state minimal_publisher + timer t(0, 500 ms) + output topic reaction(startup) {= rclpy.init(args=None) self.minimal_publisher = MinimalPublisher() - + # rclpy.spin is commented out. #rclpy.spin(self.minimal_publisher) =} diff --git a/Python/src/ReflexGame/ReflexGame.lf b/Python/src/ReflexGame/ReflexGame.lf index fe07b954..d75acd51 100644 --- a/Python/src/ReflexGame/ReflexGame.lf +++ b/Python/src/ReflexGame/ReflexGame.lf @@ -1,32 +1,30 @@ /** - * This Lingua Franca program implements a simple reaction game, where the user - * interacts with a graphical user interface (GUI) and responds to prompts that - * appear at random intervals. The user is asked to press any key in response to - * these prompts, with the time taken to respond being calculated and displayed. - * The game ends when a user presses a key before a prompt shows up, indicating - * they have cheated, with the average response time displayed at the end. This - * program integrates threading for listening to user input, randomization of - * prompt intervals, and inter-process communication to update the GUI, providing - * an interactive, real-time game experience. + * This Lingua Franca program implements a simple reaction game, where the user interacts with a + * graphical user interface (GUI) and responds to prompts that appear at random intervals. The user + * is asked to press any key in response to these prompts, with the time taken to respond being + * calculated and displayed. The game ends when a user presses a key before a prompt shows up, + * indicating they have cheated, with the average response time displayed at the end. This program + * integrates threading for listening to user input, randomization of prompt intervals, and + * inter-process communication to update the GUI, providing an interactive, real-time game + * experience. */ target Python { keepalive: true, files: [gui.py] } -reactor RandomSource(min_time(2 sec), max_time(8 sec)) { +reactor RandomSource(min_time = 2 sec, max_time = 8 sec) { preamble {= import random def additional_time(self, min_time, max_time): return self.random.randint(min_time, max_time) =} - input another; - output out; - logical action prompt(min_time); - state count(0); - reaction(startup) {= - self.random.seed() - =} + input another + output out + logical action prompt(min_time) + state count = 0 + + reaction(startup) {= self.random.seed() =} reaction(prompt) -> out {= self.count += 1 @@ -34,14 +32,12 @@ reactor RandomSource(min_time(2 sec), max_time(8 sec)) { =} reaction(another) -> prompt {= - # schedule a prompt event + # schedule a prompt event prompt.schedule(self.additional_time(0, self.max_time - self.min_time)) =} } -/* - * Receives key presses from the pygame process. - */ +/** Receives key presses from the pygame process. */ reactor GetUserInput { preamble {= import threading @@ -52,104 +48,99 @@ reactor GetUserInput { except EOFError: request_stop() return - # Each time a key press is received, schedule a user_response event + # Each time a key press is received, schedule a user_response event user_response.schedule(0, c) =} - - physical action user_response; - state user_input({=None=}); # multiprocessing.connection.PipeConnection - input user_input_pipe_init; - output user_input; - + + physical action user_response + state user_input = {= None =} # multiprocessing.connection.PipeConnection + input user_input_pipe_init + output user_input + reaction(user_input_pipe_init) -> user_response {= # Stores the Pipe object that will be used to receive key presses from # the pygame process self.user_input = user_input_pipe_init.value - + # Starts the thread that receives key presses from the pygame process t = self.threading.Thread(target=self.listen_for_input, args=(user_response, )) t.start() =} - - reaction(user_response) -> user_input {= - user_input.set(user_response.value) - =} -} + reaction(user_response) -> user_input {= user_input.set(user_response.value) =} +} -/* - * Sends graphics updates to the pygame process. - */ +/** Sends graphics updates to the pygame process. */ reactor UpdateGraphics { - input prompt; - input update_graphics_pipe_init; - input user_input; - output another; - state update_graphics({=None=}); # multiprocessing.connection.PipeConnection - state first({=True=}) - state count(0); - state total_time_in_ms(0); - state prompt_time(0); - + input prompt + input update_graphics_pipe_init + input user_input + output another + state update_graphics = {= None =} # multiprocessing.connection.PipeConnection + state first = {= True =} + state count = 0 + state total_time_in_ms = 0 + state prompt_time = 0 + reaction(update_graphics_pipe_init) {= # Stores the Pipe object that will be used to send graphics update to - # the pygame process + # the pygame process self.update_graphics = update_graphics_pipe_init.value - - # Displays an introductory prompt to the user. + + # Displays an introductory prompt to the user. self.update_graphics.send(((0,0,0), # Color of background (255, 255, 255), # Color of text - "Press any key to begin", - "To end the game, you can either: ", - "1. Close this window", - "2. Press CTRL+C in the Terminal", + "Press any key to begin", + "To end the game, you can either: ", + "1. Close this window", + "2. Press CTRL+C in the Terminal", "3. Press any key before the prompt shows up.")) =} - + reaction(prompt) {= # Ask the user for input upon receiving a prompt input from RandomSource - self.update_graphics.send(((152,251,152), - (0, 0, 0), + self.update_graphics.send(((152,251,152), + (0, 0, 0), "{}. Press any key!".format(prompt.value))) self.prompt_time = lf.time.physical() =} - + reaction(user_input) -> another {= if self.first: # if the first ever key press is detected, set "another" to trigger a prompt from RandomSource self.first = False - self.update_graphics.send(((205,92,92), - (0, 0, 0), + self.update_graphics.send(((205,92,92), + (0, 0, 0), "Wait for the prompt...")) - - # ask for the first ever prompt + + # ask for the first ever prompt another.set(42) elif self.prompt_time == 0: if self.count > 0: - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "YOU CHEATED!", + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "YOU CHEATED!", "Average response time: {:.2f} ms".format(self.total_time_in_ms / self.count))) else: - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "YOU CHEATED!", + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "YOU CHEATED!", "Average response time: undefined")) request_stop() else: time_in_ms = (lf.time.logical() - self.prompt_time) // MSEC(1) - self.update_graphics.send(((205,92,92), - (0, 0, 0), - "Response time in milliseconds: {}".format(time_in_ms), + self.update_graphics.send(((205,92,92), + (0, 0, 0), + "Response time in milliseconds: {}".format(time_in_ms), "Wait for the prompt...")) self.count += 1 self.total_time_in_ms += time_in_ms self.prompt_time = 0 - + # ask for another prompt another.set(42) =} - + reaction(shutdown) {= if self.count > 0: print("Average response time: {:.2f} ms".format(self.total_time_in_ms / self.count)) @@ -158,30 +149,26 @@ reactor UpdateGraphics { =} } -/* - * Starts the GUI and pass the user_input_pout and - * update_graphics_pin Pipe objects - * to GetUserInput and UpdateGraphics +/** + * Starts the GUI and pass the user_input_pout and update_graphics_pin Pipe objects to GetUserInput + * and UpdateGraphics */ reactor StartGui { - preamble {= - import gui - =} + preamble {= import gui =} + + output user_input_pipe + output update_graphics_pipe - output user_input_pipe; - output update_graphics_pipe; - reaction(startup) -> user_input_pipe, update_graphics_pipe {= # Starts the gui pygame process user_input_pout, update_graphics_pin = self.gui.start_gui() - - # Sets the outputs to trigger the initialization of GetUserInput and UpdateGrpahics + + # Sets the outputs to trigger the initialization of GetUserInput and UpdateGrpahics user_input_pipe.set(user_input_pout) update_graphics_pipe.set(update_graphics_pin) =} } - main reactor { random_source = new RandomSource() get_user_input = new GetUserInput() @@ -190,6 +177,6 @@ main reactor { get_user_input.user_input -> update_graphics.user_input update_graphics.another -> random_source.another gui = new StartGui() - gui.user_input_pipe -> get_user_input.user_input_pipe_init; - gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init; + gui.user_input_pipe -> get_user_input.user_input_pipe_init + gui.update_graphics_pipe -> update_graphics.update_graphics_pipe_init } diff --git a/Python/src/TrainDoor/TrainDoor.lf b/Python/src/TrainDoor/TrainDoor.lf index d26e6c35..d63e72c6 100644 --- a/Python/src/TrainDoor/TrainDoor.lf +++ b/Python/src/TrainDoor/TrainDoor.lf @@ -1,9 +1,8 @@ /** - * Program that emulates a train door controller. It has two components: - * one that controls the door and one that senses motion. When the door - * controller receives a request to open the door (a button press), it has - * to first check whether the vehicle was recently in motion. The request - * will be denied if motion has been detected less than two seconds ago. + * Program that emulates a train door controller. It has two components: one that controls the door + * and one that senses motion. When the door controller receives a request to open the door (a + * button press), it has to first check whether the vehicle was recently in motion. The request will + * be denied if motion has been detected less than two seconds ago. */ target Python @@ -16,11 +15,11 @@ reactor MotionDetector { print("Press 'c' and hit return or enter to close the door") print("Press 'm' and hit return or enter perturb the motion sensor") print("Press 'Control-d' to exit") - + global move_action global open_action global close_action - + while 1: try: c = input("> ") @@ -35,20 +34,23 @@ reactor MotionDetector { close_action.schedule(0) =} physical action movement - state timestamp(0) + state timestamp = 0 input check output ok + reaction(startup) -> movement {= global move_action move_action = movement - + t = self.threading.Thread(target=self.listen_for_input) t.start() =} + reaction(movement) {= print("Motion detected!") self.timestamp = lf.time.logical_elapsed() =} + reaction(check) -> ok {= if self.timestamp == 0 or lf.time.logical_elapsed() - self.timestamp > SECS(2): ok.set(True) @@ -58,21 +60,21 @@ reactor MotionDetector { } reactor DoorController { - physical action open physical action close - + output check input ok - state opened(False) - state requested(False) + state opened = False + state requested = False + reaction(startup) -> open, close {= global open_action open_action = open global close_action close_action = close =} - + reaction(open) -> check {= if self.opened: print("The door is already open") @@ -81,16 +83,16 @@ reactor DoorController { check.set(False) self.requested = True =} - + reaction(close) {= print("Closing the door") self.opened = False =} - + reaction(ok) {= if self.requested and ok.value: self.opened = True - print("Opening the door.") + print("Opening the door.") else: print("Cannot open the door recent motion detected.") diff --git a/Python/src/YOLOv5/YOLOv5_Webcam.lf b/Python/src/YOLOv5/YOLOv5_Webcam.lf index 5aafd433..f91e703a 100644 --- a/Python/src/YOLOv5/YOLOv5_Webcam.lf +++ b/Python/src/YOLOv5/YOLOv5_Webcam.lf @@ -1,25 +1,20 @@ /** - * Example of a Deep Neural Network (YOLOv5) in LF. - * Please see README.md for instructions. - * Adapted from + * Example of a Deep Neural Network (YOLOv5) in LF. Please see README.md for instructions. Adapted + * from * https://towardsdatascience.com/implementing-real-time-object-detection-system-using-pytorch-and-opencv-70bac41148f7 */ -target Python; +target Python -preamble {= - BILLION = 1_000_000_000 -=} +preamble {= BILLION = 1_000_000_000 =} /** * Use OpenCV2 to read from the user webcam. - * - * Camera frames are captured into the LF program - * via a physical action. - * - * 'webcam_id' (default 0) can be adjusted - * according your the local setup. + * + * Camera frames are captured into the LF program via a physical action. + * + * 'webcam_id' (default 0) can be adjusted according your the local setup. */ -reactor WebCam(webcam_id(0)) { +reactor WebCam(webcam_id=0) { output camera_frame state stream state video_capture_thread @@ -28,7 +23,7 @@ reactor WebCam(webcam_id(0)) { preamble {= from cv2 import cv2 import threading - + def video_capture(self, frame_action, running): # Read a frame ret, frame = self.stream.read() @@ -39,24 +34,24 @@ reactor WebCam(webcam_id(0)) { ret, frame = self.stream.read() return None =} + reaction(startup) -> frame_action {= self.stream = self.cv2.VideoCapture(self.webcam_id, self.cv2.CAP_ANY) if (self.stream.isOpened() is not True): sys.stderr.write("Error: Failed to capture from the webcam.\n") exit(1) - + self.stream.set(self.cv2.CAP_PROP_FPS, 30) # Set the camera's FPS to 30 - + self.thread_should_be_running = self.threading.Event() self.thread_should_be_running.set() - + self.video_capture_thread = self.threading.Thread(target=self.video_capture, args=(frame_action, self.thread_should_be_running)) self.video_capture_thread.start() =} - reaction(frame_action) -> camera_frame {= - camera_frame.set(frame_action.value) - =} - + + reaction(frame_action) -> camera_frame {= camera_frame.set(frame_action.value) =} + reaction(shutdown) {= self.thread_should_be_running.clear() self.video_capture_thread.join() @@ -65,37 +60,34 @@ reactor WebCam(webcam_id(0)) { } /** - * A YOLOv5 DNN that takes a frame as input and - * produces object 'labels' and object label coordinates - * (where each label/object is on the frame). + * A YOLOv5 DNN that takes a frame as input and produces object 'labels' and object label + * coordinates (where each label/object is on the frame). */ reactor DNN { - // Image input frame - input frame - - // Label outputs - output labels - // Label coordinates - output label_coordinates - // Send the model to anyone who's interested - output model - - state _model # The DNN model - state _device # The device to use (e.g., cpu or cuda) + input frame # Image input frame + + output labels # Label outputs + output label_coordinates # Label coordinates + output model # Send the model to anyone who's interested + + state _model # The DNN model + state _device # The device to use (e.g., cpu or cuda) preamble {= import torch from torch import hub =} + reaction(startup) -> model {= # Load YOLOv5 self._model = self.torch.hub.load("ultralytics/yolov5", "yolov5s", pretrained=True) - # Find out if CUDA is supported + # Find out if CUDA is supported self._device = "cuda" if self.torch.cuda.is_available() else 'cpu' # Send the model to device self._model.to(self._device) # Send the model to whoever is interested (other reactors) model.set(self._model) =} + reaction(frame) -> labels, label_coordinates {= _, frame_data = frame.value # Convert the frame into a tuple @@ -109,50 +101,41 @@ reactor DNN { =} } -/** - * Plot frames with labels superimposed on top of - * each object in the frame. - */ -reactor Plotter(label_deadline(100 msec)) { +/** Plot frames with labels superimposed on top of each object in the frame. */ +reactor Plotter(label_deadline = 100 msec) { input frame input labels input label_coordinates input model - state _model # Keep the model - state _prev_time(0); + state _model # Keep the model + state _prev_time = 0 - preamble {= - from cv2 import cv2 - =} - - /** - * Receive the DNN model - */ + preamble {= from cv2 import cv2 =} + + /** Receive the DNN model */ reaction(model) {= self._model = model.value print("\n******* Press 'q' to exit *******\n") =} - - /** - * Impose a deadline on object labels - */ + + /** Impose a deadline on object labels */ reaction(labels) {= # DNN output was on time =} deadline(label_deadline) {= print(f"Received the DNN output late by about {(lf.time.physical() - lf.time.logical())/1000000}ms.") =} - + /** - * Given a frame, object labels, and the corresponding - * object label coordinates, draw an interactive OpenCV window. + * Given a frame, object labels, and the corresponding object label coordinates, draw an + * interactive OpenCV window. */ reaction(frame, labels, label_coordinates) {= - if (not frame.is_present or - not labels.is_present or + if (not frame.is_present or + not labels.is_present or not label_coordinates.is_present): sys.stderr.write("Error: Expected all inputs to be present at the same time.\n") request_stop() - + elapsed_time, frame_data = frame.value # Get how many labels we have n = len(labels.value) @@ -160,7 +143,7 @@ reactor Plotter(label_deadline(100 msec)) { for i in range(n): row = label_coordinates.value[i] # If score is less than 0.2 we avoid making a prediction. - if row[4] < 0.2: + if row[4] < 0.2: continue x1 = int(row[0]*x_shape) y1 = int(row[1]*y_shape) @@ -176,18 +159,18 @@ reactor Plotter(label_deadline(100 msec)) { classes[int(labels.value[i])], \ (x1, y1), \ label_font, 0.9, bgr, 2) #Put a label over box. - + fps = int(1 / (elapsed_time / BILLION - self._prev_time / BILLION)) self._prev_time = elapsed_time - self.cv2.putText(frame_data, str(fps), (7, 70), - self.cv2.FONT_HERSHEY_SIMPLEX, 3, + self.cv2.putText(frame_data, str(fps), (7, 70), + self.cv2.FONT_HERSHEY_SIMPLEX, 3, (100, 255, 0), 3, self.cv2.LINE_AA) self.cv2.imshow("frame", frame_data) # press 'Q' if you want to exit if self.cv2.waitKey(1) & 0xFF == ord('q'): request_stop() =} - + reaction(shutdown) {= # Destroy the all windows now self.cv2.destroyAllWindows() @@ -198,14 +181,13 @@ main reactor { webcam = new WebCam() dnn = new DNN() plotter = new Plotter() - - // Send the camera frame to the DNN to be process and to the plotter to be depicted + + # Send the camera frame to the DNN to be process and to the plotter to be depicted (webcam.camera_frame)+ -> dnn.frame, plotter.frame - // Send outputs of the DNN (object labels and their coordinates) to the plotter + # Send outputs of the DNN (object labels and their coordinates) to the plotter dnn.labels, dnn.label_coordinates -> plotter.labels, plotter.label_coordinates - - // Send the DNN model to the plotter. It will be used to extract the human-readable names - // of each label. + + # Send the DNN model to the plotter. It will be used to extract the human-readable names + # of each label. dnn.model -> plotter.model - } diff --git a/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf b/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf index fe6ee5fd..bf0db6b0 100644 --- a/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf +++ b/Python/src/YOLOv5/YOLOv5_Webcam_Timer.lf @@ -1,63 +1,55 @@ /** - * Example of a Deep Neural Network (YOLOv5) in LF. - * Please see README.md for instructions. - * This example is similar to YOLOv5_Webcam but uses a timer to - * get camera frames instead of physical actions. - * Adapted from + * Example of a Deep Neural Network (YOLOv5) in LF. Please see README.md for instructions. This + * example is similar to YOLOv5_Webcam but uses a timer to get camera frames instead of physical + * actions. Adapted from * https://towardsdatascience.com/implementing-real-time-object-detection-system-using-pytorch-and-opencv-70bac41148f7 */ -target Python; +target Python import DNN, Plotter from "YOLOv5_Webcam.lf" /** * Use OpenCV2 to read from the user webcam. - * - * Camera frames are captured periodically - * using a timer. - * - * 'webcam_id' (default 0) can be adjusted - * according to your local setup. + * + * Camera frames are captured periodically using a timer. + * + * 'webcam_id' (default 0) can be adjusted according to your local setup. */ reactor WebCam { output camera_frame state stream state video_capture_thread state thread_should_be_running - - preamble {= - import cv2 - =} + + preamble {= import cv2 =} + + timer camera_tick(3 sec, 100 msec) + reaction(startup) {= self.stream = self.cv2.VideoCapture(0, self.cv2.CAP_ANY) if (self.stream.isOpened() is not True): sys.stderr.write("Error: Failed to capture from the webcam.\n") exit(1) - + self.stream.set(self.cv2.CAP_PROP_FPS, 30) # Set the camera's FPS to 30 =} - - timer camera_tick(3 sec, 100 msec); - + reaction(camera_tick) -> camera_frame {= ret, frame = self.stream.read() if ret is True: camera_frame.set((lf.time.physical_elapsed(), frame)) =} - - reaction(shutdown) {= - self.stream.release() - =} + + reaction(shutdown) {= self.stream.release() =} } main reactor { webcam = new WebCam() dnn = new DNN() plotter = new Plotter(label_deadline = 100 msec) - + (webcam.camera_frame)+ -> dnn.frame, plotter.frame dnn.labels, dnn.label_coordinates -> plotter.labels, plotter.label_coordinates - + dnn.model -> plotter.model - } diff --git a/Python/src/acas/ACASXu.lf b/Python/src/acas/ACASXu.lf index 4a67b839..71b9f9da 100644 --- a/Python/src/acas/ACASXu.lf +++ b/Python/src/acas/ACASXu.lf @@ -1,38 +1,35 @@ /** - * This program models two aircraft moving in a two-dimensional space - * for testing an Airborne Collision Avoidance System (ACAS) that is - * realized using neural networks. - * - * It includes a main reactor that applies a simple test where the - * intruder aircraft follows a straight trajectory and the "own" aircraft - * maneuvers to avoid a collision. - * + * This program models two aircraft moving in a two-dimensional space for testing an Airborne + * Collision Avoidance System (ACAS) that is realized using neural networks. + * + * It includes a main reactor that applies a simple test where the intruder aircraft follows a + * straight trajectory and the "own" aircraft maneuvers to avoid a collision. + * * It is based on the following paper: - * - * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, - * Christophe Garion, ans Claire Pagetti, "Verification of machine - * learning based cyber-physical systems: a comparative study," - * International Conference on Hybrid Systems: Computation and Control - * (HSCC), May 2022, Pages 1–16, https://doi.org/10.1145/3501710.3519540 * - * The original Python code on which this is based was provided by - * Arthur Clavière and can be found at: - * - * https://svn.onera.fr/schedmcore/branches/ACAS_CaseStudy/ + * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, Christophe Garion, ans Claire Pagetti, + * "Verification of machine learning based cyber-physical systems: a comparative study," + * International Conference on Hybrid Systems: Computation and Control (HSCC), May 2022, Pages 1–16, + * https://doi.org/10.1145/3501710.3519540 + * + * The original Python code on which this is based was provided by Arthur Clavière and can be found + * at: + * + * https://svn.onera.fr/schedmcore/branches/ACAS_CaseStudy/ * * Since that original code bears an LGPL license, so does this program: - * - * The ML models (in code/src/systems/acasxu/nnets) - * come from https://github.com/guykatzz/ReluplexCav2017 - * + * + * The ML models (in code/src/systems/acasxu/nnets) come from + * https://github.com/guykatzz/ReluplexCav2017 + * * # Prerequisites - * * ``` * pip install wheel * pip install pandas * pip install matplotlib * ``` - * + + * * @author Arthur Clavière * @author Edward A. Lee * @author Claire Pagetti @@ -41,6 +38,7 @@ target Python { fast: true, timeout: 16 s } + import XYPlotter from "lib/XYPlotter.lf" import Aircraft from "lib/Aircraft.lf" import ACASController from "lib/ACASController.lf" @@ -54,6 +52,7 @@ reactor Diff { input y2 output x output y + reaction(x1, y1, x2, y2) -> x, y {= x.set(x1.value - x2.value) y.set(y1.value - y2.value) @@ -62,34 +61,33 @@ reactor Diff { main reactor { own = new Aircraft( - x_init = 0.0, # Initial x position in feet. - y_init = 0.0, # Initial y position in feet. - psi_init = 0.0, # Angle in radians, relative to vertical, positive counterclockwise - v_init = 248.74685927665496, # Initial velocity, in feet/second. - period = 10 ms # Rate of updates. - ) - + x_init=0.0, # Initial x position in feet. + y_init=0.0, # Initial y position in feet. + psi_init=0.0, # Angle in radians, relative to vertical, positive counterclockwise + v_init=248.74685927665496, # Initial velocity, in feet/second. + # Rate of updates. + period = 10 ms) + intruder = new Aircraft( - x_init = -6000.0, - y_init = 0.0, - psi_init = -0.9851107833377457, - v_init = 450.0, - period = 10 ms - ) + x_init=-6000.0, + y_init=0.0, + psi_init=-0.9851107833377457, + v_init=450.0, + period = 10 ms) controller = new ACASController(period = 1 s) diff = new Diff() - + plot = new XYPlotter() intruder.x, intruder.y -> diff.x1, diff.y1 own.x, own.y -> diff.x2, diff.y2 - + diff.x, diff.y -> controller.x, controller.y own.psi, intruder.psi -> controller.psi_own, controller.psi_int own.v, intruder.v -> controller.v_own, controller.v_int - + controller.command -> own.turn - + own.x, own.y -> plot.x1, plot.y1 intruder.x, intruder.y -> plot.x2, plot.y2 } diff --git a/Python/src/acas/ACASXu2.lf b/Python/src/acas/ACASXu2.lf index 9317bd8b..fee861cc 100644 --- a/Python/src/acas/ACASXu2.lf +++ b/Python/src/acas/ACASXu2.lf @@ -1,29 +1,28 @@ /** - * This program models two aircraft moving in a two-dimensional space - * for testing an Airborne Collision Avoidance System (ACAS). - * In this example, both aircraft are equipped with the same ACAS system. - * + * This program models two aircraft moving in a two-dimensional space for testing an Airborne + * Collision Avoidance System (ACAS). In this example, both aircraft are equipped with the same ACAS + * system. + * * It is based on the following paper: - * - * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, - * Christophe Garion, ans Claire Pagetti, "Verification of machine - * learning based cyber-physical systems: a comparative study," - * International Conference on Hybrid Systems: Computation and Control - * (HSCC), May 2022, Pages 1–16, https://doi.org/10.1145/3501710.3519540 + * + * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, Christophe Garion, ans Claire Pagetti, + * "Verification of machine learning based cyber-physical systems: a comparative study," + * International Conference on Hybrid Systems: Computation and Control (HSCC), May 2022, Pages 1–16, + * https://doi.org/10.1145/3501710.3519540 * * The original Python code on which this is based was provided by Arthur Clavière. - * - * The ML models (in code/src/systems/acasxu/nnets) - * come from https://github.com/guykatzz/ReluplexCav2017 - * + * + * The ML models (in code/src/systems/acasxu/nnets) come from + * https://github.com/guykatzz/ReluplexCav2017 + * * # Prerequisites - * * ``` * pip install wheel * pip install pandas * pip install matplotlib * ``` - * + + * * @author Arthur Clavière * @author Edward A. Lee * @author Claire Pagetti @@ -32,6 +31,7 @@ target Python { fast: true, timeout: 16 s } + import XYPlotter from "lib/XYPlotter.lf" import Aircraft from "lib/Aircraft.lf" import ACASController from "lib/ACASController.lf" @@ -45,6 +45,7 @@ reactor Diff { input y2 output x output y + reaction(x1, y1, x2, y2) -> x, y {= x.set(x1.value - x2.value) y.set(y1.value - y2.value) @@ -53,46 +54,44 @@ reactor Diff { main reactor { own = new Aircraft( - x_init = 0.0, # Initial x position in feet. - y_init = 0.0, # Initial y position in feet. - psi_init = 0.0, # Angle in radians, relative to vertical, positive counterclockwise - v_init = 248.74685927665496, # Initial velocity, in feet/second. - period = 10 ms # Rate of updates. - ) - + x_init=0.0, # Initial x position in feet. + y_init=0.0, # Initial y position in feet. + psi_init=0.0, # Angle in radians, relative to vertical, positive counterclockwise + v_init=248.74685927665496, # Initial velocity, in feet/second. + # Rate of updates. + period = 10 ms) + intruder = new Aircraft( - x_init = -6000.0, - y_init = 0.0, - psi_init = -0.9851107833377457, - v_init = 450.0, - period = 10 ms - ) + x_init=-6000.0, + y_init=0.0, + psi_init=-0.9851107833377457, + v_init=450.0, + period = 10 ms) controller1 = new ACASController(period = 1 s) diff1 = new Diff() - + controller2 = new ACASController(period = 1 s) diff2 = new Diff() - + plot = new XYPlotter() intruder.x, intruder.y -> diff1.x1, diff1.y1 own.x, own.y -> diff1.x2, diff1.y2 - + intruder.x, intruder.y -> diff2.x2, diff2.y2 own.x, own.y -> diff2.x1, diff2.y1 - + diff1.x, diff1.y -> controller1.x, controller1.y own.psi, intruder.psi -> controller1.psi_own, controller1.psi_int own.v, intruder.v -> controller1.v_own, controller1.v_int controller1.command -> own.turn - + # Reverse role of intruder and own for second aircraft. diff2.x, diff2.y -> controller2.x, controller2.y own.psi, intruder.psi -> controller2.psi_int, controller2.psi_own own.v, intruder.v -> controller2.v_int, controller2.v_own controller2.command -> intruder.turn - - + own.x, own.y -> plot.x1, plot.y1 intruder.x, intruder.y -> plot.x2, plot.y2 } diff --git a/Python/src/acas/lib/ACASController.lf b/Python/src/acas/lib/ACASController.lf index 40cf5a1e..0b282e7c 100644 --- a/Python/src/acas/lib/ACASController.lf +++ b/Python/src/acas/lib/ACASController.lf @@ -1,28 +1,27 @@ /** - * This program realizes a neural-network-based controller for an - * Airborne Collision Avoidance System (ACAS). - * + * This program realizes a neural-network-based controller for an Airborne Collision Avoidance + * System (ACAS). + * * It is based on the following paper: - * - * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, - * Christophe Garion, ans Claire Pagetti, "Verification of machine - * learning based cyber-physical systems: a comparative study," - * International Conference on Hybrid Systems: Computation and Control - * (HSCC), May 2022, Pages 1–16, https://doi.org/10.1145/3501710.3519540 + * + * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, Christophe Garion, ans Claire Pagetti, + * "Verification of machine learning based cyber-physical systems: a comparative study," + * International Conference on Hybrid Systems: Computation and Control (HSCC), May 2022, Pages 1–16, + * https://doi.org/10.1145/3501710.3519540 * * The original Python code on which this is based was provided by Arthur Clavière. - * - * The ML models (in code/src/systems/acasxu/nnets) - * come from https://github.com/guykatzz/ReluplexCav2017 - * + * + * The ML models (in code/src/systems/acasxu/nnets) come from + * https://github.com/guykatzz/ReluplexCav2017 + * * # Prerequisites - * * ``` * pip install wheel * pip install pandas * pip install matplotlib * ``` - * + + * * @author Arthur Clavière * @author Edward A. Lee * @author Claire Pagetti @@ -31,6 +30,7 @@ target Python { fast: true, timeout: 16 s } + import NN from "ACASNN.lf" preamble {= @@ -38,27 +38,24 @@ preamble {= import math =} -reactor PreProcessor( - nn_period(10 ms) -) { - input x # x position of intruder relative to own - input y # y position of intruder relative to own +reactor PreProcessor(nn_period = 10 ms) { + input x # x position of intruder relative to own + input y # y position of intruder relative to own input psi_own # angle of own trajectory input psi_int # angle of intruder trajectory input v_own # speed of own trajectory input v_int # speed of intruder trajectory output vector # [rho, theta, psi, v_own, v_int] - + timer t(0, nn_period) - + reaction(t) x, y, psi_own, psi_int, v_own, v_int -> vector {= - pi = math.pi - + # 1) compute rho rho = np.sqrt(x.value**2 + y.value**2) - + # 2) compute theta if y.value > 0: angle = -np.arctan(x.value/y.value) @@ -74,7 +71,7 @@ reactor PreProcessor( theta += 2*pi while theta > pi: theta -= 2*pi - + # 3) compute psi psi = psi_int.value - psi_own.value # wrap psi into [-pi,pi] @@ -82,55 +79,53 @@ reactor PreProcessor( psi += 2*pi while psi > pi: psi -= 2*pi - + vector.set(np.array([rho, theta, psi, v_own.value, v_int.value])) =} } # Output is produced one microstep later. reactor PostProcessor( - initial_label(0), - available_commands({=[0.0,1.5,-1.5,3.5,-3.5]=}) # Angles in degrees/second -) { + initial_label=0, + # Angles in degrees/second + available_commands = {= [0.0,1.5,-1.5,3.5,-3.5] =}) { input score output previous_label output command - + logical action next - + reaction(startup) -> previous_label, command {= previous_label.set(self.initial_label) command.set(self.available_commands[self.initial_label]) =} + reaction(next) -> previous_label, command {= previous_label.set(next.value) command.set(self.available_commands[next.value] * math.pi/180.0) =} - reaction(score) -> next {= - next.schedule(0, np.argmin(score.value)) - =} + + reaction(score) -> next {= next.schedule(0, np.argmin(score.value)) =} } -reactor ACASController( - period(1 s) -) { - input x # x position of intruder relative to own - input y # y position of intruder relative to own - input psi_own # angle of own trajectory - input psi_int # angle of intruder trajectory - input v_own # speed of own trajectory - input v_int # speed of intruder trajectory +reactor ACASController(period = 1 s) { + input x # x position of intruder relative to own + input y # y position of intruder relative to own + input psi_own # angle of own trajectory + input psi_int # angle of intruder trajectory + input v_own # speed of own trajectory + input v_int # speed of intruder trajectory - output command # Turn command for own in radians/sec + output command # Turn command for own in radians/sec - pre = new PreProcessor(nn_period = period) + pre = new PreProcessor(nn_period=period) nn = new NN() post = new PostProcessor() - + x, y -> pre.x, pre.y psi_own, psi_int -> pre.psi_own, pre.psi_int v_own, v_int -> pre.v_own, pre.v_int - + pre.vector -> nn.vector nn.score -> post.score post.command -> command diff --git a/Python/src/acas/lib/ACASNN.lf b/Python/src/acas/lib/ACASNN.lf index 3141df51..129874f0 100644 --- a/Python/src/acas/lib/ACASNN.lf +++ b/Python/src/acas/lib/ACASNN.lf @@ -1,33 +1,35 @@ /** * A bank of neural networks for an ACAS (Airborne Collision Avoidance System). - * + * * This is based on the following paper: - * - * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, - * Christophe Garion, ans Claire Pagetti, "Verification of machine - * learning based cyber-physical systems: a comparative study," - * International Conference on Hybrid Systems: Computation and Control - * (HSCC), May 2022, Pages 1–16, https://doi.org/10.1145/3501710.3519540 + * + * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, Christophe Garion, ans Claire Pagetti, + * "Verification of machine learning based cyber-physical systems: a comparative study," + * International Conference on Hybrid Systems: Computation and Control (HSCC), May 2022, Pages 1–16, + * https://doi.org/10.1145/3501710.3519540 * * The original Python code on which this is based was provided by Arthur Clavière. - * - * The ML models (in code/src/systems/acasxu/nnets) - * come from https://github.com/guykatzz/ReluplexCav2017 - * + * + * The ML models (in code/src/systems/acasxu/nnets) come from + * https://github.com/guykatzz/ReluplexCav2017 + * * # Prerequisites - * * ``` * pip install wheel * pip install pandas * pip install matplotlib * ``` - * + + * * @author Arthur Clavière * @author Edward A. Lee * @author Claire Pagetti */ target Python { - files: ["../code/src/utils.py", "../code/src/mlmodels/ffnn.py", "../code/src/systems/acasxu/nnets/"] + files: [ + "../code/src/utils.py", + "../code/src/mlmodels/ffnn.py", + "../code/src/systems/acasxu/nnets/"] } preamble {= @@ -35,18 +37,16 @@ preamble {= from utils import parse_nnet_format =} -reactor NN( - prefix_nnet_names("nnet_acas_") -) { +reactor NN(prefix_nnet_names="nnet_acas_") { input vector - input index # Index of neural network to use. - - output score # A vector with six scores. - - state nnets({=[]=}) # Empty list initially. - state norm_parameters({={}=}) # Empty map initially. - state nnets_dict({={}=}) # Empty map initially. - + input index # Index of neural network to use. + + output score # A vector with six scores. + + state nnets = {= [] =} # Empty list initially. + state norm_parameters = {= {} =} # Empty map initially. + state nnets_dict = {= {} =} # Empty map initially. + # Read and parse neural network definitions. reaction(startup) {= for i in range(1,6): @@ -58,29 +58,28 @@ reactor NN( # name the network nnet_name = self.prefix_nnet_names + str(i-1) # append the network to the nnets list - self.nnets.append((nnet_name, nnet, path_nnet)) + self.nnets.append((nnet_name, nnet, path_nnet)) # update the norm_parameters dictionary self.norm_parameters[nnet_name] = norm_params # Create a dictionary with the neural networks for (nnet_name, nnet, path_nnet) in self.nnets: self.nnets_dict[nnet_name] = nnet - =} - + =} + reaction(vector) index -> score {= - # (ii) select the neural network to be executed, depending on the index nnet_name = self.prefix_nnet_names + str(index.value) - + # Normalize the vector norm_params = self.norm_parameters[nnet_name] x_mean = norm_params[2] x_range = norm_params[3] x_norm = (vector.value - x_mean) / x_range - + # (iv) evaluate the neural network score.set(self.nnets_dict[nnet_name].compute_output(x_norm)) - + # score.set([1.0, 0.0, 1.0, 1.0, 1.0, 1.0]) =} } diff --git a/Python/src/acas/lib/Aircraft.lf b/Python/src/acas/lib/Aircraft.lf index f56df7f2..c73b806b 100644 --- a/Python/src/acas/lib/Aircraft.lf +++ b/Python/src/acas/lib/Aircraft.lf @@ -1,63 +1,60 @@ /** - * Model of a constant speed aircraft moving in two dimensions - * under control of an input turn command - * for testing an Airborne Collision Avoidance System (ACAS). - * + * Model of a constant speed aircraft moving in two dimensions under control of an input turn + * command for testing an Airborne Collision Avoidance System (ACAS). + * * It is based on the following paper: - * - * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, - * Christophe Garion, ans Claire Pagetti, "Verification of machine - * learning based cyber-physical systems: a comparative study," - * International Conference on Hybrid Systems: Computation and Control - * (HSCC), May 2022, Pages 1–16, https://doi.org/10.1145/3501710.3519540 + * + * Arthur Clavière, Laura Altieri Sambartolomé, Eric Asselin, Christophe Garion, ans Claire Pagetti, + * "Verification of machine learning based cyber-physical systems: a comparative study," + * International Conference on Hybrid Systems: Computation and Control (HSCC), May 2022, Pages 1–16, + * https://doi.org/10.1145/3501710.3519540 * * The original Python code on which this is based was provided by Arthur Clavière. - * + * * # Prerequisites - * * ``` * pip install wheel * pip install pandas * ``` - * + + * * @author Arthur Clavière * @author Edward A. Lee * @author Claire Pagetti */ target Python -preamble {= - import math -=} +preamble {= import math =} reactor Aircraft( - x_init(0.0), # Initial x position in feet. - y_init(0.0), # Initial y position in feet. - psi_init(0.0), # Angle in radians, relative to vertical, positive counterclockwise - v_init(250.0), # Initial velocity, in feet/second. - period(10 ms) # Rate of updates. -) { - input turn # Angle change, in radians/second. Leave unconnected for no turns. - + x_init=0.0, # Initial x position in feet. + y_init=0.0, # Initial y position in feet. + psi_init=0.0, # Angle in radians, relative to vertical, positive counterclockwise + v_init=250.0, # Initial velocity, in feet/second. + # Rate of updates. + period = 10 ms) { + input turn # Angle change, in radians/second. Leave unconnected for no turns. + # Outputs are used as state variables. - output x # x position - output y # y position - output psi # Angle in radians - output v # Velocity in feet/second - - state latest_turn(0.0) # Most recently received turn command. + # x position + output x + output y # y position + output psi # Angle in radians + output v # Velocity in feet/second + + state latest_turn = 0.0 # Most recently received turn command. timer t(period, period) # Offset of period to not overwrite initial state. - + reaction(startup) -> x, y, psi, v {= x.set(self.x_init) y.set(self.y_init) psi.set(self.psi_init) v.set(self.v_init) =} - reaction(turn) {= - self.latest_turn = turn.value - =} + + reaction(turn) {= self.latest_turn = turn.value =} + reaction(t) -> x, y, psi, v {= delta_t = self.period / 1e9 # Period is seconds x.set( @@ -69,6 +66,6 @@ reactor Aircraft( # Update the angle after updating the position. psi.set( psi.value + delta_t * self.latest_turn - ) + ) =} } diff --git a/Python/src/acas/lib/XYPlotter.lf b/Python/src/acas/lib/XYPlotter.lf index 1df93b43..4d44342d 100644 --- a/Python/src/acas/lib/XYPlotter.lf +++ b/Python/src/acas/lib/XYPlotter.lf @@ -1,9 +1,10 @@ /** * Plotter that accepts two x-y input sets and plots them. - * + * * @author Edward A. Lee */ target Python + preamble {= import numpy as np import matplotlib.pyplot as plt @@ -14,11 +15,11 @@ reactor XYPlotter { input y1 input x2 input y2 - - state x1_list({=[]=}) # collected x values - state y1_list({=[]=}) # collected y values - state x2_list({=[]=}) # collected x values - state y2_list({=[]=}) # collected y values + + state x1_list = {= [] =} # collected x values + state y1_list = {= [] =} # collected y values + state x2_list = {= [] =} # collected x values + state y2_list = {= [] =} # collected y values reaction(x1, y1, x2, y2) {= self.x1_list.append(x1.value) @@ -26,17 +27,18 @@ reactor XYPlotter { self.x2_list.append(x2.value) self.y2_list.append(y2.value) =} - reaction(shutdown) {= + + reaction(shutdown) {= # make data - x1 = np.array(self.x1_list) - y1 = np.array(self.y1_list) - x2 = np.array(self.x2_list) - y2 = np.array(self.y2_list) - - # plot - plt.plot(x1, y1, "b") - plt.plot(x2, y2, "r") - - plt.show() + x1 = np.array(self.x1_list) + y1 = np.array(self.y1_list) + x2 = np.array(self.x2_list) + y2 = np.array(self.y2_list) + + # plot + plt.plot(x1, y1, "b") + plt.plot(x2, y2, "r") + + plt.show() =} } diff --git a/Rust/src/CounterProgram.lf b/Rust/src/CounterProgram.lf index 67feb598..00905927 100644 --- a/Rust/src/CounterProgram.lf +++ b/Rust/src/CounterProgram.lf @@ -3,32 +3,35 @@ //! ./counter_program --main-stride 20 --main-period 500msec //! //! Author: Clément Fournier - target Rust { timeout: 3 sec, cargo-features: ["cli"] -}; +} + +reactor Counter(stride: u32 = 1, period: time = 1 sec) { + state count: u32 = 0 + state stride = stride + timer t(0, period) + output out: u32 -reactor Counter(stride: u32(1), period: time(1 sec)) { - state count: u32(0); - state stride(stride); - timer t(0, period); - output out: u32; reaction(t) -> out {= ctx.set(out, self.count); self.count += self.stride; =} } + reactor Printer { - input in: u32; + input in: u32 + reaction(in) {= if let Some(value) = ctx.get(r#in) { println!("Hello World! value={}.", value); } =} } -main reactor CounterProgram(stride: u32(10), period: time(1 sec)) { - counter = new Counter(stride=stride, period=period); - printer = new Printer(); - counter.out -> printer.in; + +main reactor CounterProgram(stride: u32 = 10, period: time = 1 sec) { + counter = new Counter(stride=stride, period=period) + printer = new Printer() + counter.out -> printer.in } diff --git a/Rust/src/Snake/KeyboardEvents.lf b/Rust/src/Snake/KeyboardEvents.lf index 192d506b..3c869adc 100644 --- a/Rust/src/Snake/KeyboardEvents.lf +++ b/Rust/src/Snake/KeyboardEvents.lf @@ -6,13 +6,14 @@ //! If the Ctrl+C combination is detected, the program requests to stop. Upon //! shutdown, the program exits the raw mode. This reactor is designed to support //! other applications that require real-time keyboard interaction, particularly -//! within terminal-based interfaces. +//! within terminal-based interfaces. //! //! Support reactor for the Snake.lf example. - target Rust { - cargo-dependencies: { termion: "1", } -}; + cargo-dependencies: { + termion: "1" + } +} /// Capture asynchronous key presses, and sends them through an output port. /// Used by the other examples. @@ -24,12 +25,11 @@ reactor KeyboardEvents { use termion::raw::{RawTerminal, IntoRawMode}; =} - /// The latest key press. - output arrow_key_pressed: Key; + output arrow_key_pressed: Key /// The latest key press. - physical action key_press: Key; + physical action key_press: Key - state raw_terminal: Option>; + state raw_terminal: Option> reaction(key_press) -> arrow_key_pressed {= ctx.set(arrow_key_pressed, ctx.get(key_press).unwrap()); @@ -40,7 +40,6 @@ reactor KeyboardEvents { =} reaction(startup) -> key_press {= - let stdin = std::io::stdin(); // enter raw mode, to get key presses one by one diff --git a/Rust/src/Snake/Snake.lf b/Rust/src/Snake/Snake.lf index c36b32af..95042599 100644 --- a/Rust/src/Snake/Snake.lf +++ b/Rust/src/Snake/Snake.lf @@ -15,29 +15,25 @@ //! //! Note: Git history of this file may be found in https://github.com/lf-lang/reactor-rust //! under the path examples/src/Snake.lf - target Rust { // LF-Rust programs integrate well with Cargo cargo-dependencies: { termcolor: "1", - termion: "1", // (this doesn't support windows) - rand: "0.8", + termion: "1", // (this doesn't support windows) + rand: "0.8" }, // This will be linked into the root of the crate as a Rust module: `pub mod snakes;` rust-include: "snakes.rs", // This is a conditional compilation flag that enables the CLI. // Without it, command-line arguments are ignored and produce a warning. - cargo-features: ["cli"], -}; + cargo-features: ["cli"] +} -// Import a shared reactor -import KeyboardEvents from "KeyboardEvents.lf"; +import KeyboardEvents from "KeyboardEvents.lf" // Import a shared reactor // main reactor parameters can be set on the CLI, eg: // ./snake --main-grid-side 48 -main reactor Snake(grid_side: usize(32), - tempo_step: time(40 msec), - food_limit: u32(2)) { +main reactor Snake(grid_side: usize = 32, tempo_step: time = 40 msec, food_limit: u32 = 2) { preamble {= use crate::snakes::*; use crate::snakes; @@ -45,33 +41,26 @@ main reactor Snake(grid_side: usize(32), use rand::prelude::*; =} - /// this thing helps capturing key presses - keyboard = new KeyboardEvents(); + keyboard = new KeyboardEvents() /// this thing helps capturing key presses - // model classes for the game. - state snake: CircularSnake ({= CircularSnake::new(grid_side) =}); - state grid: SnakeGrid ({= SnakeGrid::new(grid_side, &snake) =}); // note that this one borrows snake temporarily + state snake: CircularSnake = {= CircularSnake::new(grid_side) =} // model classes for the game. + state grid: SnakeGrid = {= SnakeGrid::new(grid_side, &snake) =} // note that this one borrows snake temporarily /// Triggers a screen refresh, not a timer because we can /// shrink the period over time to speed up the game. - logical action screen_refresh; - /// The game speed level - state tempo: u32(1); - state tempo_step(tempo_step); + logical action screen_refresh + state tempo: u32 = 1 /// The game speed level + state tempo_step = tempo_step /// Changes with arrow key presses, might be invalid. /// Only committed to snake_direction on grid update. - state pending_direction: Direction ({= Direction::RIGHT =}); - /// Whither the snake has slithered last - state snake_direction: Direction ({= Direction::RIGHT =}); - - /// manually triggered - logical action manually_add_more_food; - /// periodic - timer add_more_food(0, 5 sec); - // state vars for food - state food_on_grid: u32(0); - state max_food_on_grid(food_limit); + state pending_direction: Direction = {= Direction::RIGHT =} + state snake_direction: Direction = {= Direction::RIGHT =} /// Whither the snake has slithered last + + logical action manually_add_more_food /// manually triggered + timer add_more_food(0, 5 sec) /// periodic + state food_on_grid: u32 = 0 // state vars for food + state max_food_on_grid = food_limit // @label startup reaction(startup) -> screen_refresh {= @@ -136,8 +125,5 @@ main reactor Snake(grid_side: usize(32), } =} - // @label shutdown - reaction(shutdown) {= - println!("New high score: {}", self.snake.len()); - =} + reaction(shutdown) {= println!("New high score: {}", self.snake.len()); =} // @label shutdown } diff --git a/TypeScript/src/ChatApplication/SimpleChat.lf b/TypeScript/src/ChatApplication/SimpleChat.lf index 37199d82..d1d3a362 100644 --- a/TypeScript/src/ChatApplication/SimpleChat.lf +++ b/TypeScript/src/ChatApplication/SimpleChat.lf @@ -4,19 +4,18 @@ * @author Byeonggil Jun (junbg@hanyang.ac.kr) * @author Hokeun Kim (hokeunkim@berkeley.edu) */ - target TypeScript { - coordination-options: {advance-message-interval: 100 msec} + coordination-options: { + advance-message-interval: 100 msec + } } reactor InputHandler { - output out:string; - physical action response; - - preamble {= - import * as readline from "readline"; - =} - + output out: string + physical action response + + preamble {= import * as readline from "readline"; =} + reaction(startup, response) -> out, response {= const rl = readline.createInterface({ input: process.stdin, @@ -26,39 +25,34 @@ reactor InputHandler { if (response !== undefined) { out = response as string; } - + rl.question("Enter message to send: ", (buf) => { actions.response.schedule(0, buf as string); rl.close(); }); =} - } +} reactor Printer { - input inp:string; + input inp: string - reaction(inp) {= - console.log("Received: " + inp); - =} + reaction(inp) {= console.log("Received: " + inp); =} } reactor ChatHandler { - input receive:string; - output send:string; - u = new InputHandler(); - p = new Printer(); - - reaction(u.out) -> send {= - send = u.out; - =} - reaction(receive) -> p.inp {= - p.inp = receive; - =} + input receive: string + output send: string + u = new InputHandler() + p = new Printer() + + reaction(u.out) -> send {= send = u.out; =} + + reaction(receive) -> p.inp {= p.inp = receive; =} } federated reactor SimpleChat { - a = new ChatHandler(); - b = new ChatHandler(); - b.send -> a.receive; - a.send -> b.receive; + a = new ChatHandler() + b = new ChatHandler() + b.send -> a.receive + a.send -> b.receive } diff --git a/TypeScript/src/DistributedHelloWorld/HelloWorld.lf b/TypeScript/src/DistributedHelloWorld/HelloWorld.lf index 27bb26b1..64d56c05 100644 --- a/TypeScript/src/DistributedHelloWorld/HelloWorld.lf +++ b/TypeScript/src/DistributedHelloWorld/HelloWorld.lf @@ -1,52 +1,51 @@ /** - * Distributed LF program (in TypeScript) where a MessageGenerator creates - * a string message that is sent via the RTI (runtime infrastructure) to a - * receiver that prints the message. - * + * Distributed LF program (in TypeScript) where a MessageGenerator creates a string message that is + * sent via the RTI (runtime infrastructure) to a receiver that prints the message. + * * The code generator generates two TypeScript files: - * - * * src-gen/DistrubtedHelloWorld/HelloWorld/src/DistrubtedHelloWorld_source.ts: - * The program that produces the sequence of messages. - * * src-gen/DistrubtedHelloWorld/HelloWorld/src/DistrubtedHelloWorld_print.ts: - * The program that prints the sequence of messages received from source. * - * The code generator also creates compiled JavaScript files out of TypeScript - * files above, respectively. + * * src-gen/DistrubtedHelloWorld/HelloWorld/src/DistrubtedHelloWorld_source.ts: The program that + * produces the sequence of messages. + * * src-gen/DistrubtedHelloWorld/HelloWorld/src/DistrubtedHelloWorld_print.ts: The program that + * prints the sequence of messages received from source. + * + * The code generator also creates compiled JavaScript files out of TypeScript files above, + * respectively. * - * * src-gen/DistributedHelloWorld/HelloWorld/dist/HelloWorld_source.js - * * src-gen/DistributedHelloWorld/HelloWorld/dist/HelloWorld_print.js + * * src-gen/DistributedHelloWorld/HelloWorld/dist/HelloWorld_source.js + * * src-gen/DistributedHelloWorld/HelloWorld/dist/HelloWorld_print.js * * To run this example, first you need to start the standalone RTI program in * org.lflang/src/lib/core/federated/RTI using `build/RTI -n 2`. * - * After starting the RTI, you need to run both compiled JavaScript files using - * node as shown below (inside src-gen/DistributedHelloWorld/HelloWorld/). + * After starting the RTI, you need to run both compiled JavaScript files using node as shown below + * (inside src-gen/DistributedHelloWorld/HelloWorld/). + * + * * node dist/HelloWorld_source.js + * * node dist/HelloWorld_print.js * - * * node dist/HelloWorld_source.js - * * node dist/HelloWorld_print.js - * * @author Edward A. Lee * @author Hokeun Kim */ target TypeScript { timeout: 10 secs -}; +} /** - * Reactor that generates a sequence of messages, one per second. - * The message will be a string consisting of a root string followed - * by a count. + * Reactor that generates a sequence of messages, one per second. The message will be a string + * consisting of a root string followed by a count. * @param root The root string. * @output message The message. */ -reactor MessageGenerator(root:string("")) { +reactor MessageGenerator(root: string = "") { // Output type char* instead of string is used for dynamically // allocated character arrays (as opposed to static constant strings). - output message:string; - state count:number(1); + output message: string + state count: number = 1 // Send first message after 1 sec so that the startup reactions // do not factor into the transport time measurement on the first message. - timer t(1 sec, 1 sec); + timer t(1 sec, 1 sec) + reaction(t) -> message {= message = root + " " + count++; console.log(`At time (elapsed) logical tag ${util.getElapsedLogicalTime()}, source sends message: ${message}`); @@ -59,14 +58,15 @@ reactor MessageGenerator(root:string("")) { * @input message The message. */ reactor PrintMessage { - input message:string; + input message: string + reaction(message) {= console.log(`PrintMessage: At (elapsed) logical time ${util.getElapsedLogicalTime()}, receiver receives: ${message}`); =} } federated reactor HelloWorld { - source = new MessageGenerator(root = "Hello World"); - print = new PrintMessage(); - source.message -> print.message; + source = new MessageGenerator(root = "Hello World") + print = new PrintMessage() + source.message -> print.message } diff --git a/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf b/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf index 1743d952..44db1aed 100644 --- a/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf +++ b/TypeScript/src/HTTPSRequestReactor/HTTPSRequest.lf @@ -1,25 +1,25 @@ /** - * This Lingua Franca program is designed to retrieve content from a webpage using - * HTTPS GET request. Upon startup, it sends a GET request to the specified URL - * ("https://ptolemy.berkeley.edu/projects/icyphy/"). As it receives data from the - * server, it schedules a physical action to process that data, which is then - * printed to the console. When the server indicates that there is no more data to - * be sent, another physical action is scheduled which ends the program. This - * reactor demonstrates the capability of Lingua Franca to handle and react to - * asynchronous network events in a timely manner. + * This Lingua Franca program is designed to retrieve content from a webpage using HTTPS GET + * request. Upon startup, it sends a GET request to the specified URL + * ("https://ptolemy.berkeley.edu/projects/icyphy/"). As it receives data from the server, it + * schedules a physical action to process that data, which is then printed to the console. When the + * server indicates that there is no more data to be sent, another physical action is scheduled + * which ends the program. This reactor demonstrates the capability of Lingua Franca to handle and + * react to asynchronous network events in a timely manner. */ -target TypeScript{ - keepalive : true - //logging : "debug" -}; +target TypeScript { + keepalive: true // logging : "debug" +} + main reactor { preamble {= import * as https from "https" import * as http from "http" =} - physical action data:{= Buffer =}; - physical action done; - reaction (startup) -> data, done {= + physical action data: {= Buffer =} + physical action done + + reaction(startup) -> data, done {= https.get("https://ptolemy.berkeley.edu/projects/icyphy/", (res : http.IncomingMessage) => { console.log("statusCode:", res.statusCode); console.log("headers:", res.headers); @@ -31,13 +31,15 @@ main reactor { }); }); =} - reaction (data) {= + + reaction(data) {= let serverData = data; if (serverData) { console.log(serverData.toString()); } =} - reaction (done) {= + + reaction(done) {= console.log("No more data in response."); util.requestStop(); =} diff --git a/TypeScript/src/HTTPServerReactor/HTTPServer.lf b/TypeScript/src/HTTPServerReactor/HTTPServer.lf index f2559344..d29476b7 100644 --- a/TypeScript/src/HTTPServerReactor/HTTPServer.lf +++ b/TypeScript/src/HTTPServerReactor/HTTPServer.lf @@ -1,28 +1,28 @@ /** - * This Lingua Franca program sets up a simple HTTP server that listens on port - * 8000 and responds to incoming requests. On startup, the server is created and - * starts listening for incoming connections. When a request is received, it is - * scheduled as a physical action, which triggers a reaction that writes a response - * back to the client. This response includes a count of the number of requests - * processed so far, incremented each time a new request is received. The program - * also ensures proper cleanup on shutdown by closing the server. This reactor - * serves as a basic example of how to integrate network interactions within the - * Lingua Franca framework. + * This Lingua Franca program sets up a simple HTTP server that listens on port 8000 and responds to + * incoming requests. On startup, the server is created and starts listening for incoming + * connections. When a request is received, it is scheduled as a physical action, which triggers a + * reaction that writes a response back to the client. This response includes a count of the number + * of requests processed so far, incremented each time a new request is received. The program also + * ensures proper cleanup on shutdown by closing the server. This reactor serves as a basic example + * of how to integrate network interactions within the Lingua Franca framework. */ target TypeScript { - keepalive : true - //logging : "debug" - //timeout : 10 sec -}; + // logging : "debug" + // timeout : 10 sec + keepalive: true +} + main reactor { preamble {= import * as https from "https" import * as http from "http" =} - state count:number(0); - state server:{=http.Server | undefined=}({=undefined=}); - physical action serverRequest:{= [ http.IncomingMessage, http.ServerResponse ] =}; - reaction (startup) -> serverRequest {= + state count: number = 0 + state server: {= http.Server | undefined =} = {= undefined =} + physical action serverRequest: {= [ http.IncomingMessage, http.ServerResponse ] =} + + reaction(startup) -> serverRequest {= let options = {}; server = http.createServer(options, (req : http.IncomingMessage, res : http.ServerResponse) => { // Generally browsers make two requests, the first is for favicon.ico. @@ -32,7 +32,8 @@ main reactor { } }).listen(8000); =} - reaction (serverRequest) {= + + reaction(serverRequest) {= let requestArray = serverRequest; if (requestArray) { let req = requestArray[0]; @@ -43,9 +44,10 @@ main reactor { res.end("hello world\n"); } =} - reaction (shutdown) {= + + reaction(shutdown) {= if (server) { server.close(); } - =} + =} } diff --git a/TypeScript/src/SimpleWebserver.lf b/TypeScript/src/SimpleWebserver.lf index 07c5fe71..5e781015 100644 --- a/TypeScript/src/SimpleWebserver.lf +++ b/TypeScript/src/SimpleWebserver.lf @@ -1,23 +1,21 @@ /** - * This Lingua Franca program creates a simple web server that responds to HTTP - * requests. Upon startup, the server is initiated and begins to listen on port - * 8000. When an HTTP request is received that is not for the favicon.ico file, the - * server schedules a reaction that processes the request and sends a response with - * the message "Hello world!". This program efficiently handles incoming requests - * and performs the necessary actions in response to each request. The server stops - * and closes upon the shutdown of the program. + * This Lingua Franca program creates a simple web server that responds to HTTP requests. Upon + * startup, the server is initiated and begins to listen on port 8000. When an HTTP request is + * received that is not for the favicon.ico file, the server schedules a reaction that processes the + * request and sends a response with the message "Hello world!". This program efficiently handles + * incoming requests and performs the necessary actions in response to each request. The server + * stops and closes upon the shutdown of the program. */ target TypeScript { - keepalive : true -}; + keepalive: true +} main reactor { - preamble {= - import * as http from "http" - =} - state server:{=http.Server | undefined=}({=undefined=}); - physical action serverRequest:{= [http.IncomingMessage, http.ServerResponse] =}; - reaction (startup) -> serverRequest {= + preamble {= import * as http from "http" =} + state server: {= http.Server | undefined =} = {= undefined =} + physical action serverRequest: {= [http.IncomingMessage, http.ServerResponse] =} + + reaction(startup) -> serverRequest {= let options = {}; server = http.createServer(options, (req : http.IncomingMessage, res : http.ServerResponse) => { // Generally, browsers make two requests; the first is for favicon.ico. @@ -28,7 +26,8 @@ main reactor { }).listen(8000); console.log("Started web server at http://localhost:8000/") =} - reaction (serverRequest) {= + + reaction(serverRequest) {= let requestArray = serverRequest; if (requestArray) { let req = requestArray[0]; @@ -37,9 +36,10 @@ main reactor { res.end("Hello world!\n"); } =} - reaction (shutdown) {= + + reaction(shutdown) {= if (server) { server.close(); } - =} + =} }