ColdFrame: Event modelling

State modelling

Restrictions

The vocabulary available for statechart modelling in UML is extensive. In fact, it's so extensive that to support all of it would be difficult, while for modellers to use all of it would be confusing for their audience if not for themselves.

ColdFrame only supports statecharts where

These restrictions may seem draconian, and indeed models produced with them will tend to have more states. However, they do allow you to make complete state models. The basic idea is that it's better to have a small language that you understand completely rather than a bigger but vaguer language.

Example model

This is a statechart for a Lamp. I assume that it's OK to turn on the lamp if it's already on and to turn it off if it's already off; we'd need some more intermediate states if not.

The Lamp is the kind that you find on shared stairwells; it stays on for a period of time after being switched on by a pushbutton, and you can extend the period by pushing again while it's still on.

  1. The state model starts at the Initial state (the black spot). Since the transition from the Initial state requires no event to trigger it (it's a "completion transition"), the model immediately transitions to the Idle state.
  2. On entry to the Idle state, the entry action Turn_Off is executed.
  3. The state model remains in the Idle state until a Lamp.Button_Push event occurs. It then transitions to the Lit state.
  4. On entry to the Lit state, the entry actions Turn_On and Set_Timeout are executed, in the order shown in the model. It would be possible for the timeout period to be controlled by a Lamp instance variable, if required.
  5. If no further user action occurs, the timeout set at step 4 occurs, and the state model transitions back to the Idle state (step 2), where the lamp is turned off.
  6. If on the other hand the user pushes the button again (a Lamp.Button_Push event), the state model transitions to the current (Lit) state, by indivisibly

Timed events

The Lamp statechart involves a timeout, which is a special case of a delayed event.

Given an event, there are six things you can do with it:

  1. Post it, to be handled as soon as possible (in order),
  2. Post it, to be handled at a specific time in the future,
  3. Post it, to be handled after at least a specified interval,
  4. Set it on a Timer, to be handled at a specific time in the future,
  5. Set it on a Timer, to be handled after at least a specified interval,
  6. handle it immediately.

There are two variants of the first operation:

plain Post
This is the normal way of posting events. The queuing is strictly first in, first out.
Post_To_Self
Sometimes you have to send an event to the current instance as a way of moving the state machine on (perhaps you've come across an error condition). If this happens, you need the event to take precedence over any other events in the queue (otherwise, your event will happen after all the instance events already in the queue).
To achieve this, use Post_To_Self; any event posted in this way takes priority over normally-posted events.
"Events to self" are also queued first in, first out.

The difference between Posting to be handled in the future and Setting on a Timer is that events that are posted to be handled in the future are "fire and forget", whereas Set events can be retracted. Set events are the kind to use for timeouts (indeed, in state machines in general, with perhaps odd exceptions).

Note that ColdFrame requires you to cancel (Unset) unfired timers before setting them again, and will also insist that a timer is in fact Set before you can Unset it. To make up for this, it guarantees that you can Unset a timer which has in fact fired at any point before the corresponding event has actually been handled, and the event won't be delivered.

Because you need to be quite sure about whether a timer needs to be Unset before being Set again, it's a very good idea never to manipulate a timer except in a state machine action.

Event modelling

In ColdFrame, events are always directed to a specific class. They may be Instance Events (used in state machines), which are directed to individual instances of the class, or Class Events (typically used for domain isolation).

However, in UML 1.4, and therefore in ArgoUML, events belong to the package in which they're declared rather than to a class. One way of handling this would be to put each class that has events associated with it in its own child package; this seems rather clumsy, so instead, prefix each event's name with the class it belongs to, as in the Lamp statechart. When generating code, ColdFrame discards all but the final name component (for example, Lamp.Button_Push becomes Button_Push when it's declared inside the Lamp package).

It's possible to use ArgoUML's Properties to set up entry and transition actions. However, because the Properties dialogs are so strongly related to the (complex) UML metamodel, it's much much easier to set them up by directly editing the statechart. In the case of an entry action, double-click in the body of the state and type entry / Action_1; Action_2 (the action part is a semicolon-separated list of actions); in the case of a transition, draw the transition, double-click in the label and type Class.Event or, if there are actions, Class.Event / Action_1; Action_2.

Class events

Instance events don't need to have any action associated with them, if all they do is cause transitions in the statechart. However, class events always need to have an action.

In the ArgoUML-supporting version of ColdFrame, this is organised using a StateMachine stereotyped «class», containing a dummy state and the required event/action association as a transition:

ArgoUML alows you to have as many StateMachines on a Class as you like (quite what that would mean isn't clear). ColdFrame allows you to have at most one unstereotyped StateMachine, and at most one stereotyped «class».

Because you can have both, it may be best (to avoid confusing the reader) if the events include .Class in the name, as in Button.Class.Button_Pushed above. As before, ColdFrame discards all but the last component of the name, so the translation (in the Button package) will be Button_Pushed.

In any case, ColdFrame requires that class events be stereotyped «class», as in

(note, this demonstrates the point above, that in UML events belong to the package).

Payloads

Class Events will always carry a payload (though it might be an empty record); Instance Events need not.

To model an instance event without any payload, it's only necessary to show it on a statechart.

To model a payload, enter it in the Parameters section of the Properties tab for the event:

There can be no more than one parameter. ColdFrame will disregard its name, but it might as well be Payload!

It's best if the payload doesn't contain allocated data, because that may lead to memory leaks.

Transitions

Instance events cause transitions. It's illegal for an event to occur in a state if there's no corresponding transition from that state (typically, a Can't Happen exception will be raised).

If you want an event to be ignored when it occurs in a particular state, provide a transition-to-current-state for that event and mark it «ignore». This means that the event is completely ignored, and in particular any entry actions don't get called. Don't do this with a completion transition (one with no triggering event).

At present, ArgoUML doesn't display the «ignore» stereotype on the statechart.

Note that you need to reserve Can't Happen for when it's logically impossible for the event to happen in the state, not just when it's undesirable!

Class modelling

Instance events and actions

The statechart for the Lamp class needs supporting in the class model.

The instance operations Turn_On, Turn_Off, Set_Timeout and Clear_Timeout correspond to the entry actions on the statechart.

Instance entry and transition actions can have a single parameter (aside from the implicit This): if you specify it, it must be of the type of the payload of the event that causes entry to the state concerned, and the actual payload will be passed at run time. This means that if you use this feature, all the events that cause entry to the state must carry the same payload type.

You can use the implicitly-generated Delete operation as an entry action (ColdFrame doesn't check that it's in a final state). Of course, it needs to be the last action.

If you need to delete the instance in any other action operation, you must mark the operation «final». A «final» operation must delete the instance.

The instance attribute Timeout, of the ColdFrame-special type Timer, is used to hold timeouts. The concept of operation for timeouts is:

  1. you create an event
  2. you set it on the timer
  3. the timer fires, and the event is posted on the event queue
  4. the event reaches the head of the event queue, and is handled.

The timer can be unset between the time when it's set and the time when the event is handled; if you do this, the event won't be delivered.

If you need to have more than one concurrent timeout, allocate a Timer variable for each.

Class events and handlers

As shown above, class events are associated with a handler or action, in this case Button.Pushed. The action must take a parameter of the same type as the event's payload, and must be a class operation (ownerScope checked in the modifiers section of the Properties tab).

Class events can be posted to run later or Set on Timers; however, mixing class timers with state machines may be a symptom of a poor state model. You'll probably want to make the Timer concerned a «class» attribute.


See also:


Simon Wright