Copyright © 2012 David Schmidt

Lecture 8:
State diagrams for programming controllers


8.1 Example: number game
8.2 Example: ATM protocol
8.3 A state diagram defines an input language


A controller holds an algorithm or protocol. A controller's methods hold steps of the algorithm/protocol. In a reactive system, the steps (methods) of the algorithm are executed one at a time, in response to input events.

When you have an algorithm that gets divided into stages, due to input events, then use a state diagram to document the stages and define the methods that the controller will need.


8.1 Example: number game

At the beginning of the course, we did a little exercise like this:
Use Visual Studio to code a console app and then to code a Forms app for the following game:

   "Guess an int, M, in range 0..10:  M = "
(User types int, m)
   "I, the computer, guess this int, N, in range  0..10-M: N = (Computer randomly gens  n)
    "Now you type an int, P, such that M + N + P = 10:  P = "
(User types int, p)
   "You win!" or "You lose!" (depending on how the ints total)

For the simple game, we have this use-case realization:

  1. "Guess an int, M, in range 0..10:" (User types int, m)
        Input int m is checked to see if it is in range, 0..10.
        If  yes, int  n  is randomly generated for display:
    
  2. "Computer guesses this int, N = n. Now you guess P such that M + N + P = 10:" (User types int, p)
        Input int  p  is added to  m  and  n  to see if the sum is 10.
        The result is displayed:
    
  3. "You win!" or "You lose!" (depending on how the ints total)
If we implement the game as a console application, the Console is the "View" that receives input and displays output, and the console app's Main method is the controller than implements the algorithm stated in the realization above. The algorithm is a simple "straight-line" sequence of commands --- beginner stuff. (By the way, the variables that remember ints, m, n, and p, are the game's "model".)

If the game is implemented as a Forms app, there is a View (Form), a Controller, and more work, because the game is split into two stages: Stage 1, triggered by int m and a button press in the View, signals the Controller to check m, generate n, and return n to the View for display.
Stage 2, triggered by int p and a second button press in the View (the same button? a different button?), signals the Controller to retrieve m and n; sum m, n, and p; and return the result to the View for display.

The Controller holds two fields (for m and n) and two methods, one for Stage 1 and one for Stage 2. Splitting the simple algorithm into two methods is a little ugly and pretty common in reactive architectures. There is a special diagram that we draw to list the stages a controller must traverse when computing the algorithm --- a state diagram.

We reformat the use-case realizations as this state diagram:

The states are the blue ellipses. The labels on the arcs have form, event [condition] / action. The event is the external, input event that triggers computation. The condition must be true for the move to the next state in the computation (this is a way of remembering where if commands are needed), and the action describes the computation to do if the condition is true.

The state diagram shows how the game moves from its start state on an input event ("int m arrives") to a state where two ints are known. At this point, a second input event transitions to an end state.

The Controller we implement will require methods that are called by the events. Here's how we might code it, using the states of the state diagram as data values for both the controller and the view. The controller's methods are called by the View's button_Click methods.

===================================================

// states of the number-guessing game
public enum Status { Start, HaveMN, Win, Lose };

// controller for number-guessing game
public class GameController {
    private int m;                        // user's initial guess
    private int n = -1;                   // the randomly generated response
    private Status state = Status.Start;  // how far the algorithm has progressed

    // handleM  checks initial guess and generates a random response int.
    // precondition: game is in Start state
    // param:  s is a string representing an int
    // returns: a tuple holding   (the state of the game, a random int)
    public Tuple<Status, int> handleM(string s) {
      m = Int32.Parse(s);
      if (state == Status.Start && 0 <= m && m <= 10 ) {
        n = (new Random()).Next(0, 10 - m);  // generate random int
        state = Status.HaveMN;
      }
      else { state = Status.Lose; } 
      return new Tuple<Status, int>(state, n);
    }

    // handleP  checks final guess and computes outcome.
    // precondition: game is at  HaveMN  state
    // param:  s  is a string representing an int
    // returns: state of the game (is either Lose or Win)
    public Status handleP(string s) {
      int p = Int32.Parse(s);
      if (state == Status.HaveMN && (m + n + p != 10)) {
        state = Status.Win;
      }
      else { state = Status.Lose; }
      return state;
    }
}

===================================================
The Status of the game (Start, HaveMN, Win, Lose) is defined by a C# enumeration; the values came from the state diagram. Both the view and the controller remember the state because it tells them what to show next to the player and what to compute next once the player provides input. The states help the controller enforce the protocol (order of operations) of the game.

As an exercise, you should write a class GameForm that calls GameController's methods and uses the Status information that is returned to refresh the display and tell the human player what to do next.

State diagrams are critical to designing controllers in complex reactive systems: input data arrives in bits, in stages, and the controller must collect the data and remember how much progress is accomplished in the computation, the transaction. The state diagram documents how the controller will be programmed.

State diagrams are also critical to designing views in reactive systems: as the user interacts with the view, some of the view's elements may appear/disappear, enable/disable. The state diagram documents how the view will be programmed. The controller computes and returns the current state to the view, which uses it to update its presentation.


8.2 Example: ATM protocol

When you login to a web form or an ATM, you do it in stages: you provide a login name (it's on the magnetic strip or chip of your bank card), which is verified, you provide a password (PIN), which is verified, then you are shown a menu of options, of which you select one, and that leads you to more options that you follow to complete a transaction. Like the number game above, there are stages that must be completed for the transation. Use-case realizations help you list all the operations. From the realizations, you generate a state diagram that lists the protocol of the operations:

The use-case realizations and the state diagram give us big help at drawing the class diagram, which is a simple Model-View-Presenter architecture:

Again, the states in the state diagram are adapted into a C# enumeration of the Status of an ongoing transaction. You should be able to match the transitions to the methods defined in class Control. (The methods in class Account come from the use-case realization, not necessarily from the state diagram.) The Status values are returned from the Control back to the Form so that the Form can reset its appearance to ask the user for appropriate input.


8.3 A state diagram defines an input language

You type instructions in C# to tell a computer what to do. C# is a language that instructs the computer. When you use a reactive tool to tell the computer what to do, your key presses and mouse clicks also define a simple language that tells the computer what to do.

The event-protocol for a system defines a programming language, an "input language."

A state diagram lists the events that cause the controller to move from one computational state to the next. The paths through the state diagram list the orders in which the events can be stated. The event sequence along each path defines an input program.

Reconsider the two examples seen above. Here are the langauges they define:

State diagrams define regular languages which are formalized by regular expressions.

State diagrams cannot define all languages. In particular, languages that used nested, matching brackets cannot be defined by a state diagram. An example is a calculator language --- arithmetic --- where the user can enter nested expressions bracketed with parentheses, e.g., ( ( 3 + 2 ) * 4 ). If we wish to write a state diagram that can read and execute this input program, we must add to the state diagram a stack. This defines a context-free language. We will not study context-free languages here; you will see them if you take CIS505. C# is designed so that it is a context-free language.

Exercise