Example: Coffee Machine

This example is inspired by the Coffee Machine example provided by the documentation about the TAU UML tool. Here is a sketch of the user interface:

user interface

Different system architectures (model structures)

(1) simple The system to be described is the Coffee Machine. This is the simplest architecture: only one interface.
(2) two interfaces As above, but two separate interfaces are considered for input and output. It is assumed that two separate entities in the environment look after input and output, respectively.
(3) with components As (1) above, but some internal system structure is shown. The user interacts directly with one of the system components.

System behavior modeled as an LTS

LTS

Possible interaction sequences:

  1. coin-10; tea; return-5; cupOfTea; coin-x; return-coin; coin-5;
  2. coin-5; tea; cupOfTea; coin-10; coffee; cupOfCoffee;
  3. . . . etc.

Because of the rendez-vous interactions, at the end of sequence (1), no coin can be entered (the Coffee Machine has no coin transition in its current state (which is paid 5). Similarly, no cupOfCoffee interaction is possible in the initial state.

Notation of the LTSA tool

In the LTSA notation, this state machine can be defined by the following text:

IDLE = (coin[5] -> PAIDFIVE | coin[10] -> PAIDTEN | coinElse -> returnChange -> IDLE),
PAIDFIVE = (tea -> MAKETEA | coffee -> returnChange -> IDLE),
PAIDTEN = (tea -> returnCoin[5] -> MAKETEA | coffee -> MAKECOFFEE),
MAKETEA = (cupOfTea -> IDLE),
MAKECOFFEE = (cupOfCoffee -> IDLE).

The following diagram is produced by LTSA; it is equivalent to the state machine diagram above. Question: To which states of the informal model above correspond the numbered states of the LTSA diagram ?

This example is contained in the LTSA-Example "coffee machine - simple". That example also has the check of two properties:

System behavior modeled as an IOA

IOA user

The states and transitions of this model correspond directly to the states and transition of the LTS model above.

Here a distinction is made between input and output interactions, indicated by ? and !, respectively.

The main difference with LTS modeling is that interactions are not rendez-vous. For instance, an input coin-5 would be possible in the state Paid 10, but the fact that the diagram contains no coin-5 transition from this state means that the hypothesis is made about the behavior of the system environment that the environment will not produce this input when the Coffee Machine is in the Paid 10 state. If this happens nevertheless, we say that this input is a non-specified input.

Note that for an IOA model, the corresponding behavior of the environment (the user in our case) can be obtained by the system behavior by interchanging inputs and outputs. See the user behavior on the right side above.

System behavior modeled as an FSM

FSM

This model is similar to the IOA model, but in the FSM formalism, a transition contains input and output interactions, therefore this model has less states and transitions. For instance, the tea transition from the Paid 10 state contains the tea input event, plus two output interactions, return-5 and cupOfTea. This transition also includes (implicitely) two states that were shown explicitly in the IOA diagram above. Note: Obtaining the user behavior is more difficult for FSM behaviors than explained for IOA behaviors above.

Notation of the Umple tool

This FSM model can be coded in Umple as a state machine object in the following form (only two transitions are included in the following Umple specification):

 class CoffeeMachine {
  User user;
  state {
    idle {
      coin(int value) [value == 10] -> paidTen; }
    paidTen {
       coffee / {user.cupOfCoffee(new Drink());}  -> idle; }
  }
 }

Note that Umple uses the procedure call semantics input events: Each transition is realized as a method that is called when the input event occurs. For instance, the coffee transition from the Paid 10 state (mentioned already above) is translated into the following Java code:

public boolean coffee() {
      boolean wasEventProcessed = false;
      State aState = state;
      switch (aState) {
            case paidTen:
                  user.cupOfCoffee(new Drink());
                  setState(State.idle);
                  wasEventProcessed = true;
                  break;
            }
      return wasEventProcessed;
}

A problem with the basic procedure-call paradigm of Umple

There is a problem in this example with the generated code. During the call of the coffee transition by the user object, the method cupOfCoffee is called on the user object - that is, there are two processes executing in the user object simultaneously, the waiting for the return of the coffee method, and the executions of the cupOfCoffee method. This kind of concurrency should be avoided because it is very difficult to understand, in general. The simple system below is another example for this kind of difficulty: What output to the environment would have been produced, and in which state would the machine A be, after an input event a has been processed ? - An implementation of this system will be provided in Lab-3.

difficuty

Using separate interfaces and agents for input and output as shown in Architecture (2) above avoids this problem. Here is a sketch of the corresponding Umple model. The "Input interface" of the Architecture diagram (2) is represented by the User object, and the "Output interface" is represented by the Delivery object. (Note: only two transitions are included in the behavior of the CoffeeMachine):

// This is the CoffeeMachine-2

public class Main {
      public static Delivery del = new Delivery();
      public static CoffeeMachine cm =
            new CoffeeMachine(del);
      public static User user = new User(cm, del);
      public static void main ( String[] args ) {
            new Thread(user).start(); }
}

public class Delivery {
      public Drink coffee = null;
      public int returnedCoins = 0;
   // added for Umple model
      public void cupOfCoffee(Drink d) {
            coffee = d; }
}

public class User implements Runnable {
      private CoffeeMachine myCM; Delivery d;
      public User(CoffeeMachine m, Delivery del) {
            myCM = m; d = del; }
      public void run(){
            while (true) {
                  myCM.coin(10);
                  myCM.coffee();
                  try {Thread.sleep(2000);}
                        catch(InterruptedException ie){}
                  if (d.coffee != null){
                        // drink coffee
                        d.coffee = null;
                        System.out.println("got a coffee ");
                  } else {System.out.println("there is no drink ");}

            }
      }
}

// this is Umple syntax

class CoffeeMachine {
  Delivery del;
  state {
    idle {
      coin(int value) [value == 10] -> paidTen;
    }
    paidTen {
       coffee / {del.cupOfCoffee(new Drink());}
         -> idle;
     }
  }
}

Modeling internal system structure

In the architectural model (3), the component Controller interacts with the user (as shown in the above behavior models) and also with the Hardware component which produces the drinks. The LTS model of the Hardware component is shown in the LTS notation below. Each command given to the Hardware is followed by a corresponding response rendez-vous interaction.

HARDWARE = (fillWater -> waterOK -> HARDWARE | heatWater -> warm -> HARDWARE | fillCoffee -> PREPARECOFFEE),
PREPARECOFFEE = (coffeeOK -> HARDWARE).

hardware

Using the LTS modeling approach with rendez-vous interactions, the functionality of the Hardware may be used by the Controller component when it is in the Make Coffee state of the LTS behavior model above (we assume that this diagram also models the Controller) - however, in the state Make Coffee, it interacts with the Hardware as shown by the diagram below. Note that we use here the hierarchical state notation of UML for LTS diagrams. A similar diagram defines how tea is made in the Make Tea state of the Controller.

makeCoffee

The following is a model of the Controller in LTSA notation. Note: the first three lines of the CONTROLLER definition are the same as above.

IDLE = (coin[5] -> PAIDFIVE | coin[10] -> PAIDTEN | coinElse -> returnChange -> IDLE),
PAIDFIVE = (tea -> MAKETEA | coffee -> returnChange -> IDLE),
PAIDTEN = (tea -> returnCoin[5] -> MAKETEA | coffee -> MAKECOFFEE),
MAKETEA = (fillWater -> waterOK -> heatWater -> warm -> cupOfTea -> IDLE),
MAKECOFFEE = (fillWater -> waterOK -> fillCoffee -> coffeeOK -> heatWater -> warm -> cupOfCoffee -> IDLE).

Note: The intermediate states during the Make Coffee operations are not mentioned in the LTSA notation, only the transition labels are defined.

If we consider that each of these interactions actually has a "direction" (for the Controller, is either input or output), we may represent the sequence of interactions involved in the use case buying coffee by this sequence diagram.

Model of the Controller using the UML-SDL notation (distinction of input and output and message queuing)

Here is a state diagram of the dynamic behavior of the Controller component in the UML-SDL notation taken from the Telelogic documentation.

Adaptation to the procedure-call paradigm

The model of the Hardware component given above shows clearly that this entity can receive three commands fillWater, fillCoffee and heatWater which are followed by the respective responses when the requested operation is completed. The commands can be given in any order (assuming that the environment waits that the corresponding response was received). In the LTS modeling approach, it may therefore be more natural to model each command as a single rendez-vous interaction between the Controller and the Hardware - for instance, a signle fillWater interaction, instead of fillWater followed by WaterOK. This can be simply transformed into the object-oriented modeling approach, where the Hardware can be simply modelled as an instance of a class that accepts three methods fillWater, fillCoffee and heatWater.

Using this approach to model the Hardware component, the Controller component may be modeled in Umple as follows (again using two external interfaces for the communication with the user, as in architecture (2)). Note, however, that in Umple, the executed transition methods cannot return any results (only a boolean value indicating whether the transition was executed) - this is OK for this example where no explicit result is returned (only control flow).

class Controller {
      Delivery del; Hardware h;
      state { idle { coin(int value) [value == 10] / -> paidTen; }
               paidTen { coffee() / { h.fillWater(); h.fillCoffee(); h.heatWater();
                                             del.cupOfCoffee(new Drink()); } -> idle; }
            }
}

Created: December 17, 2008; last revision: January 26, 2015