Statecharts Assignment 

Submission

  • Due Date: Sunday 12 December 2021, before 23:55.
  • Team Size: 2 (pair design/programming)!
    Note that as of the 2017-2018 Academic Year, each International student should team up with "local" (i.e., whose Bachelor degree was obtained at the University of Antwerp).
  • Checklist:
    • Transformed the requirements into black-box test scenarios
    • Implemented the requirements in statechart
    • The statechart passes the black-box test, and YAKINDU reports 100% coverage
    • Generated Python code from the statechart, and the generated code works with the GUI
    • Wrote report
  • Submission Information: Only one member of each team submits a full solution. This must be a compressed archive (ZIP, RAR, TAR.GZ...) that includes your report and all the files needed to run your solution, including the files from the "starting point" that you are not allowed to edit, and your generated Python code. The report must be called index.html. Make sure to mention the names and student IDs of all the team members. The other team member must submit a single HTML file containing only the coordinates of both team members. This will allow us to put in grades for both team members in BlackBoard.
  • Submission Medium: BlackBoard. Beware that BlackBoard's clock may differ slightly from yours. If BlackBoard is not reachable due to an (unpredicted) maintenance, you submit your solution via e-mail to the TA. Make sure all group members are in CC.
  • Contact / TA: Joeri Exelmans.

Goals

The main goal of this assignment is to familiarize yourself with Statechart modelling, simulation, testing and code synthesis. All this is to be done with YAKINDU Statechart Tools.

Requirements

You will use the Statecharts formalism to model the behavior of a (single) Personal Rapid Transport trolley. This trolley drives laps on a one-way track, repeatedly passing by 4 different stations:

Requesting a stop

At any point in time, a stop at a station can be requested. For every station, the trolley has either scheduled at stop, or not: Requesting the same stop multiple times before the train has stopped there, has no extra effect.

Input events
  • requestStop: integer

    Raised when a stop has been requested. The integer parameter is the station of the requested stop, and is 0, 1, 2 or 3.

Interface variables
  • stopsAt: integer

    A bitmap of the currently scheduled stops. If bit i is 1, a stop at station i is scheduled. The trolley's statechart should read and write this variable for its internal logic.

    TIP: To work with a bitmap, use bitwise operations in YAKINDU action code:
    Set bit i
    stopsAt |= (1 << i)
    Unset bit i
    stopsAt &= ~(1 << i)
    Test if bit i is set:
    stopsAt & (1 << i) != 0

    Additionally, the GUI reads this variable to display the currently scheduled stops. The buttons of scheduled stops are greyed-out:

Normal operation

During normal operation, at any point in time, the trolley's behavior alternates between these modes:

  1. Arrival at a station
  2. Departure to a station

We'll now explain the behavior of each of these modes.

Arrival at a station

If the trolley has just arrived at a station, the following steps must happen:

  1. We wait for 1 second
  2. Then, the doors are opened.
  3. Then, only if the both of the following conditions hold, will the mode change to "Departure to a station":
    1. The doors must have been open for a duration of at least 2 seconds.
    2. A stop at another station has been requested.

Additionally, between steps 2 and 3, requesting a stop for the station the trolley is currently stopped at, has no effect.

Departure to a station

This mode consists of the following steps:

  1. The trolley runs the "closing doors" procedure (see section "Closing the doors", below) until completion (i.e. until the doors are closed).
  2. We wait for 1 second.
  3. Then, the trolley accelerates to "full speed", which is 24 m/s.
  4. Only after the trolley has reached its full speed, should it respond to an incoming approachingStation-event (indicating that we are nearing a station). The approachingStation-event contains an integer parameter, indicating the ID of the station the trolley is approaching.
    • If a stop has been scheduled at this station, should the following steps happen:
      1. Brake until standstill.
      2. Change the mode to "Arrival at a station" (described above).
    • Otherwise, the event is ignored.

    NOTE: The reason we require the trolley to first accelerate to full speed before allowing it to stop at a station, is because the approachingStation-event always occurs at a fixed distance before the respective station. This fixed distance is approximately the braking distance of the trolley at full speed. It was simply easier to develop the framework this way :)
    It is therefore possible for the trolley to drive past a station at which a stop was scheduled, if the trolley has not yet reached full speed in time. The trolley will stop at that station during its next lap.
Output events
  • setTargetSpeed: real

    Sets the target speed of the trolley. If the target speed differs from the current speed, the trolley will accelerate or decelerate accordingly.

Input events
  • atTargetSpeed

    Raised when the trolley has reached the target speed that was set earlier.

    You may assume that the control over the electric motor is perfect, and after adjusting the target speed, this event is raised once, at which point, the target speed is perfectly maintained (acceleration becomes 0).

  • approachingStation: integer

    Raised when the trolley is at a certain (fixed) distance from the given station, such that, if the current speed is 24 m/s, this distance is approximately equal to the current braking distance.

Opening the doors

Opening the doors of the trolley is straightforward, and happens instantaneously. While the doors are open, passengers can board and unboard the trolley.

Passenger (un)boarding

The trolley can never carry more than 6 passengers, or less than 0 (duh-huh).

Additionally, the boarding or unboarding of a passenger causes the door to be "obstructed" for the duration of 1 second. While the door is obstructed, boarding or unboarding is impossible.

Closing the doors

Closing the doors always happens as the following sequence of steps:

  1. The "closing doors" signal is activated. Passengers can still (un)board at this point. The signal is merely supposed to put pressure on the passengers to hurry up.
  2. Then, as soon as the door has not been obstructed for 1 second (counting since the start of the "closing doors" signal), the "closing doors" signal is deactivated, and the doors are closed.
Example: Doors and departure to another station

The following example summarizes the doors and (un)boarding behavior in the context of arrival at a station and departure to another station:

We assume that the trolley has just made a stop at a station, and another stop was already scheduled. The 'closing the doors'-sequence starts after 2 seconds of opening the doors. This causes the "closing doors signal" to start. Meanwhile, a passenger unboards and another passenger boards. The door is obstructed twice, each time for a duration of 1 second. Finally, while the doors-signal is active and the door has been unobstructed for 1 second, the doors can close.

TIP! You can literally write this scenario as a black-box test for your solution.
Output events
  • openDoors

    Opens the doors (instantaneously)

  • closeDoors

    Closes the doors (instantaneously)

  • startDoorsSignal

    Starts the "closing doors" signal

  • stopDoorsSignal

    Stops the "closing doors" signal

Input events
  • board

    Raised when a passenger attempts to board the trolley.

  • unboard

    Raised when a passenger attempts to unboard the trolley.

Interface variables

These variables are displayed in the GUI:

  • numPassengers: integer

    Number of passengers currently on the trolley.

  • remainingCapacity: integer

    Remaining passenger capacity. Must always be equal to (6 - numPassengers).

Exceptional operation: Emergency stop

Until now we have described "normal operation". Normal operation can temporarily be interrupted by an emergency stop. An emergency stop can always happen, even if the trolley is currently stopped at a station.

Starting an emergency stop

An emergency stop executes the following sequence of steps:

  1. Start braking until standstill. This step is skipped if the emergency stop occurs while the trolley is stopped at a station.
  2. Then, the doors open, without delay.

The doors remain open and the trolley cannot move for as long as the emergency stop lasts. Passengers can board and unboard while the doors are opened, as usual.

Stops can still be requested/scheduled, and will be executed after the emergency stop is over.

Ending an emergency stop

An emergency stop cannot be ended before the trolley has come to a full stop, i.e. it cannot be "canceled" early. If an emergency stop is ended, the following must happen:

  • If the trolley was driving to a station, it re-starts the "Departure to a station"-sequence (defined above) again, from the beginning (closing the doors, accelerating, etc.).
  • If the trolley was stopped at a station, it re-starts the "Arrival at a station"-sequence, but at step 3 (meaning: the doors will remain open for at least another 2 seconds before the trolley can depart). The doors are not closed and re-opened; they simply remain open.
Input events
  • startEmergency

    Initiate an emergency stop.

  • endEmergency

    Attempt to end an emergency stop.

Initial state

Initially, it is as if we have just completed step 2 of "Arrival at a station":

  • The trolley is stopped (at station 0, in the GUI)
  • The doors are open
  • The doors signal is off

Furthermore:

  • No stops are scheduled
  • There are 0 passengers on board

GUI

It is important to know that the GUI will only read and display the current values of the interface variables when a refreshUI-event is raised by the statechart. This event should be raised every time one of the interface variables are changed.

Output events
  • refreshUI

    Causes the UI to read the current values of all interface variables (stopsAt, numPassengers and remainingCapacity) and update them in the view.

Getting started

Download YAKINDU Statechart Tools 4 (the latest version at the time of writing) and register for a free academic (Professional) license, with your university e-mail address. You will be automatically e-mailed a license key that will remain valid for 3 months.

NOTE: You can only register with your university e-mail once. If your earlier academic license has expired, you will have to send proof of enrollment to YAKINDU (you can obtain such a proof through SiSA), which will be manually reviewed.
NOTE: You can also register for a free Standard license, for non-commercial use. You will be required to send scans of your ID card to YAKINDU, which will be manually reviewed.

Download the assignment zip file. Import this archive as a project into your YAKINDU workspace. It contains the following files, which you must edit:

  • Trolley.ysc: The starting point for the Trolley statechart model. The statechart interface has already been defined. Do no alter the interface! You can (and probably should) add internal variables, and/or internal events.
  • TrolleyTest.sctunit: The starting point for writing a black-box unit test for the Trolley statechart.

The archive also contains the following files, which you must not edit:

  • MotorControl.ysc: A statechart that sets the acceleration of the motor, based on a target speed (received from the trolley control) and the current speed (received from the "physics"). It notifies the trolley component when the selected target speed has been reached.
  • Physics.ysc: A discrete, fixed-time-step (every 10 ms) approximation of the trolley's position as an integral of the trolley's speed, which again is an integral of the trolley's acceleration.
  • System.ysc: A statechart instantiating the Trolley, MotorControl and Physics statecharts, and connecting their interfaces.
  • Statechart.sgen: This file describes how YAKINDU should generate Python code from the *.ysc files. To manually generate Python code, right-click on Statechart.sgen, and select "Generate code artifacts". Usually however, YAKINDU automatically does this every time you save your model.
  • main.py: After you have generated Python code from your statechart, you can run this script to see your model in action! To run the script, you need Python 3 (The assignment was developed with Python 3.9), and the TkInter Python library (included with most Python distributions).

For the curious student, here is an overview of the system's architecture:

Testing your solution

There are various ways you can "test" if your work is correct:

  1. You can write a unit test in YAKINDU. There are 2 "dimensions" here:
    • Isolated vs. system-as-a-whole
      • Isolated: You write a unit test for the Trolley statechart (Trolley.ysc).
      • System-as-a-whole: You write a unit test for the entire system (System.ysc). We advice against this, because some parts of the system contain floating point arithmetic, which can be a bit unpredictable.
    • Black-box vs. white-box
      • Black-box: A black-box test only reads and writes parts of the statechart that are part of its interface, that is:
        • Raise an input event
        • Assert that an output event is (not) raised
        • Read the values of interface variables
      • White-box: A white-box test can additionally:
        • Check which states are active
        • Read the values of internal variables.
      Black-box testing has 2 very important advantages:
      1. Enables test-driven development: When implementing a feature, you can non-ambiguously describe the desired behavior as a test before you actually implement the feature. This way, you keep your mind focused on the "what", whithout prematurely getting lost in the "how".
      2. Black-box tests are portable between different implementations, as long as they have the same interface. If you alter your implementation, you do not have to alter your test.

    Included with the assignment is the starting point for a black-box unit test TrolleyTest.sctunit for the Trolley statechart. You are required to further extend this black-box test with additional scenarios, derived from the requirements, until YAKINDU reports 100% coverage of the transitions in your statechart.

    If you think it is useful for you, you are allowed (but not required) to write an additional white-box unit test as well, or even write a (white/black-box) test for the system-as-a-whole. Please put any additional tests in a separate files, and include them in your submission.

  2. You can interactively debug the statechart within YAKINDU. This is a white-box approach, because you can observe which states are active at any point in time, and which transitions are firing. You have 2 options here:
    • Isolated: You can debug the Trolley statechart in isolation. This has the advantage that the debugging environment is not "polluted" with events from e.g. the physics simulation, which occur very frequently (every 10 ms).
    • System-as-a-whole: You can also debug the system-as-a-whole (System.ysc). This can be useful if the system behaves differently from what you expected, and debugging the Trolley statechart in isolation does not explain the behavior.
  3. You can generate Python code from your statechart and run it with the Python (GUI) application. The Python app has the 4 stations hardcoded in it, and with a completed solution, you should observe the trolley making its scheduled stops. A downside of this approach, is that you cannot see the internals of your statechart (e.g. what states are active?).

Tips

We recommend you to work in teams of 2, practicing "pair programming": One student is at "the controls", the other student observes, comments, spots mistakes.

We recommend you to work in the following iterative fashion, repeating the following steps:

  1. Write a black-box test scenario for a new feature
  2. Implement the feature in the statechart
  3. Verify if the feature is correctly implemented (and verify if there are any regressions wrt. earlier-written tests)

The assignment has been designed specifically to encourage use of as many Statechart features as possible: composite states, orthogonal states, history, internal events, internal variables, timed transitions, enter/exit actions, guard conditions and action code. Make sure you understand these features, and use them, where you think they are appropriate.

YAKINDU has excellent documentation.

If some part of the requirements is not clear enough, or if you have spotted an ambiguity, please e-mail me.

Report

You are also required to write a small (HTML) report.

At the beginning, you list the following things:

  • The names and student IDs of the team members
  • The amount of time spent working on the project: Please be honest, this helps us estimate the workload for future assignments.
Then, in a structure you are free to choose, you describe your workflow (How did you work together? (pair programming / divided the work / ...) In what order did you implement different features? etc.), and give an overview of your solution (but please do not describe every tiny detail). Include screenshots of (components of) your statechart, where appropriate.

The report should probably not contain more than 1000 words.

Additional material

Maintained by Hans Vangheluwe. Last Modified: 2022/08/29 00:29:58.