Dezyne Introductory Tutorial

Dezyne Introductory Tutorial
Version 16
16 December 2016
Dezyne Version 2.1.1, 2016-09-19 09:10
Contents
Introduction .................................................................................................................................................. 2
Intended Audience .................................................................................................................................... 2
The Tutorial ............................................................................................................................................... 2
Dezyne Hello World: Turning an LED On and Off ......................................................................................... 2
Simplest Usable Dezyne System Model ........................................................................................................ 8
Defining and Managing Component States ................................................................................................ 11
Handling States Event-by-Event: “Event-Leading” ................................................................................. 11
Interface States versus Component States ............................................................................................. 12
Handling Events State-by-State: “State-Leading” ................................................................................... 13
Polling for State....................................................................................................................................... 16
States and Illegal Events: Asserts ............................................................................................................ 19
Time and Timers: Making an LED Blink ....................................................................................................... 20
Possible Events: Multiplexing Sensor Inputs .............................................................................................. 26
Inevitable Events versus Optional Events ............................................................................................... 27
The Multiplexer Component ................................................................................................................... 27
The Alarm System ....................................................................................................................................... 32
Requirements.......................................................................................................................................... 32
The Controller ......................................................................................................................................... 33
Adding the LED ........................................................................................................................................ 34
Adding the Sensors ................................................................................................................................. 36
Adding the Siren and Timer .................................................................................................................... 37
The Completed Alarm System ................................................................................................................ 40
Dezyne – Introductory Tutorial
v16
16 December 2016
1
Introduction
Intended Audience
This tutorial assumes that you have developed control software in C, C++, C# and/or Java. Most likely
you have worked hands-on with finite state machines in some form or other, maybe with Harel state
machines or UML statecharts. You’ve seen that deeply nested if-then-else control logic leads to
debugging nightmares – every fix seems to break something else. You’ve experienced unhandled events,
race conditions, deadlocks and live-locks, some of which are not reproducible. You know about state
machine determinism and non-overlapping guards.
The tutorial also assumes that you already generally understand the benefits Dezyne brings to
developing control software – see the next paragraph – and that you now want to create something
realistic with it, gaining fluency with its domain-specific language and its techniques.
Benefits summary: Dezyne enables you to methodically and efficiently divide complex control problems
into verified problem-free components and interfaces that you re-compose into reliable, trustworthy
systems. Verified Dezyne models have no unhandled events, problematic race conditions, deadlocks or
live-locks in their core state-management and communications “skeleton” logic. Source code that
Dezyne auto-generates from models will be free of these same problems. For more information about
Dezyne’s benefits, see the materials on the Verum’s Dezyne website or contact Verum.
Dezyne runs on all common platforms as an Eclipse™ or Visual Studio™ plug-in, or via command line.
Models are expressed in a simple, immediately familiar C/Java-like language.
The Tutorial
The tutorial guides you step-by-small-step, with explanations, through building and verifying a model of
a single-threaded burglar-alarm system, in Eclipse. The model is built with hierarchical components that
communicate through events. You should already understand these concepts, and you need to be able
to run the Dezyne Eclipse or Visual Studio user interface; for information about the Eclipse plug-in user
interface see https://www.verum.com/support/help/.
Tutorial goals in decreasing order of priority are 1) efficiency of learning, 2) rigor, and 3) completeness.
You should go through the tutorial from beginning to end: later sections depend on earlier ones.
We will build the system from the bottom up, first as the simplest possible useful system, ultimately as a
realistic system with complex behaviours. Along the way, we will always ensure our work-in-progress
components and subsystems have no inherent logical errors in their states and communications.
Also along the way, we will automatically generate C++ source code that implements the verified logic,
and at the end we’ll automatically generate audit-ready state tables, state charts, sequence diagrams
and architectural system diagrams. Then we’ll look back and consider the obstacles that could arise
when we need to extend or refactor the system – not many, it turns out.
Dezyne Hello World: Turning an LED On and Off
Language keywords: behaviour component import in interface on provides void
Dezyne – Introductory Tutorial
v16
16 December 2016
2
In this section we build a near-minimal Dezyne model in which one component presents one interface.
The simplest possible valid Dezyne model consists of one component that presents no interface to its
outside environment.
Step 1. Create a new Dezyne model by entering or copy-pasting the below two lines of model code
into a new file in the Dezyne editor (in Eclipse: File / New / Dezyne Model File).
component LED {
}
You can copy and paste any code sample in this tutorial directly into the Dezyne editor. Most Dezyne
language keywords are displayed like this in the Dezyne editor and in the tutorial code samples; a
few special keywords including “enum” are colored blue. You can position curly-brackets exactly as in
C/C++/Java: at the end of the line, on the next line, and so forth.
Step 2. Verify this model (in Eclipse: the double-arrow green toolbar icon
).
Dezyne will respond that no component in the model has behaviour, therefore there’s nothing yet to
verify.
Step 3. Add a minimum behaviour block as below. In each code sample, added or changed lines of
code from the previous sample will be highlighted like this.
component LED {
behaviour {
}
}
In Eclipse a small red symbol appears, indicating invalid syntax and/or semantics; moving the cursor over
the symbol brings up the below information.
A well-formed, verifiable Dezyne component presents (provides) at least one defined interface to its
environment, and at least one of its presented/provided interfaces has to define one or more trigger
events accessible from outside as callable functions.
Step 4. Remove the behaviour {} block from the component for now. We will add it back later.
Step 5. Enter or copy-paste the new code in the below model into the Dezyne editor. This code
defines a simple interface through which a caller can turn on and turn off an LED through
Dezyne – Introductory Tutorial
v16
16 December 2016
3
synchronous void function calls to the providing component. The component provides
(implements) this interface. Note that commenting in Dezyne works exactly as in C/C++/C#/Java.
interface ILED {
in void turnOn();
in void turnOff();
behaviour {
on turnOn: {}
on turnOff: {}
}
}
component LED {
provides ILED iLed;
}
// removed the component's behaviour block for now
Step 6. Verify your copy of the above model code (
) and study the following paragraphs.
The LED component is now well-formed: it has instanced a defined interface, ILED, and we have verified
that it and its interface have no bugs or other problems.
An instanced interface is known as a “port” in Dezyne; each port must have a unique name (here “iLed”)
within the providing component. The term “port” is not a keyword in the Dezyne modeling language.
You may have noticed that keywords and user-defined names in Dezyne are case-sensitive. You will
need to select your own conventions for naming things; in this tutorial we prefix each interface name
with a capital “I” and each instance of an interface with a lower-case “i”. Dezyne requires that userdefined names contain only letters, digits and the underscore character ‘_’, and that the leading
character in a name is not a digit.
In the above, by not defining any behaviour inside the component, we are asserting that the outside
world needs to know only that the component supports void functions named “turnOn” and “turnOff”,
and that the component will quietly and correctly do its work whenever triggered by an input event, i.e.
whenever one of the functions is called. This behaviourless feature turns out to be important in
practice: you can use it for placeholder model components for which you will write all C/C++ source
code manually, and for which you will not use automatically-generated C/C++ source coming out of the
Dezyne model. For example, you might write hardware or communications abstraction components
manually. In Dezyne, a component with no defined behaviour is sometimes called “hand-written”.
To “verify” in Dezyne means to trace every possible execution sequence through a component model,
and to stop and report the first occurrence of a problem – either a mismatch between a providing
component’s actual functional behaviour versus its contracted functional behaviour, or a nondeterministic behaviour such as an overlapping guard or unmanaged race condition, or an unhandled
event, or some other issue. If no problem is found, then the model has been verified. In the Eclipse
editor, if a verification problem was found, then it is reported as the entire sequence of trace steps that
led to it.
Dezyne – Introductory Tutorial
v16
16 December 2016
4
Note that, by definition, a verified interface’s externally visible functional behaviour exactly matches a
verified providing component’s externally visible functional behaviour for that interface. Verification
proceeds through exhaustive stimulus-response of a component’s provided functions, using the
applicable interface definition to decide if the providing component’s responses are complete and
correct. A providing component, however, can also optionally contain additional non-visible internal
behaviour that extends and refines its responses to events. And a given interface can have any number
of providing components in a Dezyne project. We will revisit these matters later.
Step 7. Create a new file named “ILED.dzn”, move the ILED interface code into it, and delete the
ILED interface code from the model. Add a new import line to your original file as follows:
import ILED.dzn;
component LED {
provides ILED iLed;
}
// removed the component's behaviour block for now
You can import any number of files in any order into a Dezyne file. All imports must precede other noncomment/non-whitespace contents in the file.
Step 8. Enter or copy-paste the component behaviour as shown in the code sample below. Observe
the parentheses () in the component’s added code, which differ from the parallel declarations in the
interface definition, and remember that if a component defines behaviour for a called function, that
behaviour must exactly match the interface’s defined behaviour as seen through function calls and
only in that sense. In practice, you would replicate the exact interface behaviour in the component
only if you plan to refine and extend the component’s own internal management in response to
events. Notice also that the component’s function names are qualified with the port name, i.e. the
interface instance’s name.
import ILED.dzn;
component LED {
provides ILED iLed;
/* "provides" means implements ILED functions */
behaviour {
on iLed.turnOn(): {} // qualify funcs w/interface *instance* (port) name
on iLed.turnOff(): {}
}
}
Step 9. Verify the model. Then, simulate interface ILED and component LED via the Eclipse toolbar
icon
, which initiates traces with events. Be sure the Eclipse Sequence View is on (menu Window
/ Show View / Sequence View) as well as the Trace View and Watch View.
Step 10. Experiment with the Sequence Diagram view: select Next Events (“eligible events”) one by
one to build up a trace, then click on transitions in the diagram and see how the corresponding code
is automatically highlighted. In the below Eclipse screen shot, the turnOff call was clicked in the
diagram, highlighting its source to the left:
Dezyne – Introductory Tutorial
v16
16 December 2016
5
The above screen shot shows the Dezyne console output. Every verification and simulation step
executes through the console command-line interface to access the Dezyne server. Enter “dzn -h” at the
command prompt at the bottom for a summary of the commands.
Look also at the Verify Results tab contents, shown below, to see the categories of model verification.
You can search the Dezyne help at https://www.verum.com/support/help/ for details about these.
Notice the differences between verifying versus simulating. Simulation enables interactive exploration
and explanation of the detailed interface protocol – a powerful way of communicating to other people
about how an interface and its implementing component(s) function.
Step 11. You can generate this component’s C or C++ source code via Eclipse’s toolbar icon
and
view it. Future tutorial sections will discuss generated source code and how to integrate with it.
The generated source as seen in the sandbox comprises 100 to 200 lines of compilable code not
counting whitespace, comments, or lines consisting of only a curly-bracket. Most of the source for this
small example is generic to any Dezyne-generated code. In practice, you would rarely change
Dezyne – Introductory Tutorial
v16
16 December 2016
6
automatically generated code; instead, you would modify only code that supports it or interacts with it.
In this way you guarantee not introducing subtle bugs into your system, and you greatly simplify matters
when refactoring or adding new features later.
That’s all! We’ve created and verified our first system model. Well, not quite, only our first verifiable and
verified component model and its source code. Next we’ll build our first system.
Before moving on, consider the following. In the model so far, we could have used just one function that
toggles the light on and off. Why didn’t we? In this case, because we want users/callers of the LED
controller to know what the light’s state is – on or off – after sending a control command to the light. If
the only control was a toggle, then even in a single-threaded system a caller would have to manage
initialization and perhaps poll to know the light’s current state for sure. In a multi-threaded system the
light could receive a toggle from anyone any time, and polling would likely be essential. In later sections
we’ll return to these and other related matters.
The Efficiency of Code-Like Dezyne Models for States and Communications Logic
In Dezyne, you define components and their interfaces through a familiar and compact C/Java-like
language, then automatically verify the completeness and syntactical correctness of your logic, and
then exercise sequence diagrams to check that the coded behaviour is what you intended.
Contrast this with going in the opposite direction: you’d whiteboard a sequence diagram or draw it
in a graphics editor, converge on the behaviour you seek, write the code, compile and link it, and
then try to test the code to ensure it’s working properly. With Dezyne, you direct the machine to
test your logic and generate interactive visualizations of the behaviour.
The Dezyne approach, once mastered, is dramatically more efficient, and that efficiency translates
to dramatic savings across entire projects – and with no subtle logical bugs, such as race conditions
or deadlocks or livelocks, in states-and-communications logic
Dezyne – Introductory Tutorial
v16
16 December 2016
7
Simplest Usable Dezyne System Model
Language keywords: requires system
Operator: <=>
We first extend the LED model by adding a switch component that switches the LED on and off. The
switch requires the ILED interface in order to call the LED component and change the light’s state.
Again, commenting and whitespace in Dezyne work the same as in C/C++/C#/Java.
Step 1. Enter or copy-paste the new Switch component into the complete ILED-LED model, as shown
in the below code sample. You can either import the ILED interface .dzn file or include the ILED code
as here, but if you import then the import statement must precede all other statements.
component Switch {
requires ILED iLed;
}
// "requires" means calls ILED “in” functions
interface ILED {
in void turnOn();
in void turnOff();
behaviour {
on turnOn: {}
on turnOff: {}
}
}
component LED {
provides ILED iLed;
/* "provides" means implements ILED functions */
behaviour {
on iLed.turnOn(): {}
on iLed.turnOff(): {}
}
}
You can declare components and interfaces in any order in a Dezyne model. In the above, the Switch
component requires the ILED interface but the Switch component definition precedes the interface
definition.
Step 2. Verify the above code. Notice that the Switch component does not show up in the list of
verifiable items – because it defines no behaviour. Nothing has really changed in this Dezyne model;
we’ve only added a hand-written Switch component that we intend to connect to the LED.
Step 3. “Wire” the two components into a system as shown in the below diagram and model code. A
Dezyne “system” is a component that instances other components and connects their interfaces.
Wiring amounts to binding each requires interface instance (port) to a corresponding provides
instance (port) for the same interface, via the Dezyne <=> operator.
Dezyne – Introductory Tutorial
v16
16 December 2016
8
component LEDSwitchSystem {
system {
LED led;
Switch switch;
switch.iLed <=> led.iLed;
}
}
component Switch {
requires ILED iLed;
}
// "requires" means calls ILED functions
interface ILED {
in void turnOn();
in void turnOff();
behaviour {
on turnOn: {}
on turnOff: {}
}
}
component LED {
provides ILED iLed;
/* "provides" means implements ILED functions */
behaviour {
on iLed.turnOn(): {}
on iLed.turnOff(): {}
}
}
Step 4. Study the following important points one by one. They are essential to your understanding.

The LEDSwitchSystem we defined above can also serve as a component if it is outfitted with
at least one provides or requires interface to communicate with its environment. Any
higher-level component can instance and use any other component as long as 1) no namespace conflicts result and 2) all verifications succeed, which means in particular that there is
no recursion.

The order in which instances “led” and “switch” appear in the system block does not matter,
nor do the order in which mapping instances appear in the same block.
Dezyne – Introductory Tutorial
v16
16 December 2016
9

Dezyne uses the term “port” to mean an instance of an interface, but “port” is not a
keyword in the language. In each port binding, by convention you probably should map
requires on the left to provides on the right, though Dezyne accepts either order. The last
line of the system block above binds the switch’s required port to the LED’s provided port,
establishing the link between the caller and the called function.

This tutorial by convention uses the same user-defined port local name for the provided
interface as for the required interface in another component, as a simple visual consistency
check when binding at the system level. You do not have to use this convention.

When you verify, the choices of what can be verified have not changed from having added
the Switch component or the LEDSwitchSystem component. Only the LED component and
its ILED interface can be verified. Also, systems-level components cannot be verified or
simulated, only interfaces and components that have explicit behaviour can be. Future
versions of Dezyne will support system verification and simulation.
Step 5. Bring up the System View (in Eclipse, menu Window / Show View / System View) to display
the wired system as shown below. Clicking the top-left button next to the string “LEDSwitchSystem”
displays or suppresses the system internal structure, as shown below. Clicking the “wire” between
the Switch and LED highlights the line of model source code that binds these two ports together.
In Dezyne-generated system/subsystem diagrams like the above, the “wires” – i.e. the requires-provides
connections – are drawn top down, as the blue/black arrowheads indicate. In this simple system, the
LED provides the interface for the Switch to set the light state to on or off. Later we will build composite
subsystems in which “wires” are actually “buses” in the electronics sense, carrying multiple connections
going top-down, or equivalently outside-in, among system components. Ultimately every chain of
arrows terminates in a leaf component that provides an interface.
Step 6. View the component’s State Chart, shown below (Window / Show View / State Chart View).
Though you have not yet defined any state behaviour, Dezyne still tracks state as it perceives it. This
component never leaves its initial state. The next section discusses state management.
Dezyne – Introductory Tutorial
v16
16 December 2016
10
Dezyne Diagrams and Traces Both Validate and Document a System
Consider the value of the system diagram, the sequence diagram and the event trace. Together they
validate the core system as modeled. They can serve as specifications and documentation for
whoever will integrate and test the Dezyne-generated source code in the whole hardware and
software system.
Defining and Managing Component States
Language keywords and constructs: enum illegal reply
Operators and constructs: = == [].
Handling States Event-by-Event: “Event-Leading”
In this section we start tracking the LED component’s on/off state. This anticipates extending or refining
state behaviour inside the component, while keeping the component’s externally visible behaviour
exactly the same as its provided interface or interfaces.
Step 1. Enter or copy-paste the following new state-management code in the Dezyne editor. You
can continue with the system design from the previous section, or restart with only the below code.
import ILED.dzn;
component LED {
provides ILED iLed;
behaviour {
enum State { Off, On };
State state = State.Off;
on iLed.turnOn(): { state = State.On; }
on iLed.turnOff(): { state = State.Off; }
}
}
In the above component’s code, the enum line declares an enumeration type as in C/C++/Java. “Off” and
“On” are unique enum values for this particular enumeration type, not strings. “State” is a case-sensitive
user-defined name for the enumeration type.
The next line after the enum declaration defines a variable named “state” of type State and initializes the
setting to “Off”. By convention in this tutorial we will always list the default state first when declaring an
enum. Two important but non-obvious points:
 You are required to initialize an enumeration-type variable in its declaration.
 You cannot split this declaration-assignment line into the two lines, e.g.“State state; state =
State.Off” – this equals sign is a special initialization operator, not a generic assignment (try it).
Each handler line (on event) in the component’s behaviour block is followed either by one statement
that ends in a semicolon, or by curly braces {} that contain zero (no-op) or one or more statements.
Dezyne – Introductory Tutorial
v16
16 December 2016
11
Step 2. View the component’s State Chart, shown below (you might have to refresh/update views
first using the
icon in Eclipse, next to the verify
icon)
Interface States versus Component States
You can track states in an interface exactly as you can in a component. When defined in an interface,
state declarations differ from the component’s only by 1) having no interface-instance prefix, and 2)
having no parentheses. Here are the same states as above, but defined instead in the interface:
interface ILED {
in void turnOn();
in void turnOff();
behaviour {
enum State { Off, On };
State state = State.Off;
on turnOn: { state = State.On; }
on turnOff: { state = State.Off; }
}
}
It is important to understand that there are at least two occasions when one might choose to declare
and manage state-related enumerations in an interface:

When the providing component or components are hand-written, i.e. have no behaviour blocks
and therefore no declared state behaviour, yet enumerated state behaviour is of interest to
requiring (calling) components.

When an interface function will synchronously return enumerated results that inform the caller
of a new state arrived at (at least momentarily) as a consequence of the function call.
If you define states in both an interface and in a providing component for that interface, Dezyne
verification will ignore the interface’s declared states and only compare the component’s function
return values with the interface’s specified function return values.
Dezyne – Introductory Tutorial
v16
16 December 2016
12
There is no direct connection between states defined in an interface’s behavioural specification and
states defined in a providing component’s implemented behaviour. All that matters is that every stimulus
applied to a component-implemented interface function returns a response type that matches the
interface’s declared response type, i.e. that every trace through the interface matches every trace
through the implementing component for events related to just this interface. For example, we will see
later that a model function can be defined to return an integer within a fixed range; verification will find
any case of a return outside this range, and will display, in the Trace View window, the exact sequence
of steps that led to the mismatch.
Handling Events State-by-State: “State-Leading”
The component behaviour as written so far handles state changes event-by-event. It unconditionally
turns the light off when directed, whether the light was on or not, and turns it on when directed.
You can invert behavioural logic to handle events state-by-state. The Dezyne language supports if-thenelse but not at the first logical level within a state’s behaviour block – we’ll see this in a later section.
Top-level logic inside a behaviour block can only be one of these three different kinds:
 Declaration plus initialization of an enum or a variable.
 An on-event block.
 A [boolean-test] “guard” block as used below.
Dezyne – Introductory Tutorial
v16
16 December 2016
13
Step 1. Enter or copy-paste the below code into your ILED-LED design and verify it.
import ILED.dzn;
component LED {
provides ILED iLed;
behaviour {
enum State { Off, On };
State state = State.Off;
[state == State.Off] {
on iLed.turnOn(): { state = State.On; }
on iLed.turnOff(): {}
}
[state == State.On] {
on iLed.turnOn(): {}
on iLed.turnOff(): { state = State.Off; }
}
}
}
Important rule: The Dezyne verification “compliance” check tests that a component’s behaviour with
respect to a given interface handles all possible events associated with that interface. If you handle
events purely state-by-state as in the above example, then for component compliance and for zero
ambiguity you must explicitly handle every on-event inside each state-oriented guard block (there is one
exception to this rule involving “illegal” situations, which is explained later.).
Step 2. Comment out line “on iLed.turnOff(): {}” in the above code and re-verify. You will get a
compliance error even though you’ve seemingly changed nothing in the component’s visible
behaviour. This error can be reported fully only via the Trace and Sequence Views:
Dezyne – Introductory Tutorial
v16
16 December 2016
14
The user interface’s Console output for the above verification is shown below; the series of events that
arrived at the compliance error precede the Exit Code line.
dzn$ dzn --session=22 -v verify --model=LED Intro\LED-State-Leading.dzn
..
verify: ILED: check: completeness: ok
verify: ILED: check: deadlock: ok
verify: ILED: check: livelock: ok
verify: LED: check: deterministic: ok
verify: LED: check: illegal: ok
verify: LED: check: deadlock: ok
verify: LED: check: livelock: ok
Intro/LED-State-Leading.dzn:14:5:i19: iLed.turnOn not handled
verify: LED: check: compliance: fail
iLed.turnOn
iLed.return
iLed.turnOn
Exit Code: 1
dzn$
The Dezyne “==” comparison operator works as in C/C++/C#/Java. Each [] guard must be followed by
either a single statement or a curly-bracket block {} with or without statements in it; you can always
remove curly brackets where they contain exactly one statement. You can optionally further abbreviate
the guard equality test for states whenever there would be no ambiguity, as shown below. We will
abbreviate these ways from here on wherever possible.
Step 3. Modify and verify your design using shorthand guard statements and event handler logic as
shown below.
import ILED.dzn;
component LED {
provides ILED iLed;
behaviour {
enum State { Off, On };
State state = State.Off;
[state.Off] {
on iLed.turnOn(): state = State.On;
on iLed.turnOff(): {}
}
[state.On] {
on iLed.turnOn(): {}
on iLed.turnOff(): state = State.Off;
}
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
15
From this point on we will leave out the Step-by-Step cues and instead sometimes suggest Exercises to
try out on your own before copy-pasting code into the editor. No matter how you proceed, you must
always run verification on your edits!
Polling for State
Sometimes a component that sets another component’s state needs to poll (query) for the current
state. The below shows how to enable polling through a “getter” function.
In order to support and verify the getState reply below, we have to declare the state enumeration
publicly in the interface definition, reference the interface enumeration in the component (not declare
it), and replicate at least some of the component’s state behaviour in the interface behaviour.
The interface code below uses the event-first pattern to define its internal behaviour. The rule is, as
before, that all states or guards must be handled by the defined behaviour. The getState handler below
does the same thing in every state, thus it does not use [] boolean checks.
Dezyne – Introductory Tutorial
v16
16 December 2016
16
Exercise: simplify the below interface’s code so that none of the on-event handlers tests the state,
instead each on-event handler does the same thing in all states. Answers are at the end of this section.
interface ILED {
enum State { Off, On };
in void turnOn();
in void turnOff();
in State getState();
behaviour {
State state = State.Off;
on turnOn: {
[state.Off] state = State.On;
[state.On] {}
}
on turnOff: {
[state.Off] {}
[state.On] state = State.Off;
}
on getState: { reply(state); }
}
}
component LED {
provides ILED iLed;
behaviour {
ILED.State state = ILED.State.Off;
[state.Off] {
on iLed.turnOn(): state = ILED.State.On;
on iLed.turnOff(): {}
on iLed.getState(): reply(state);
}
[state.On] {
on iLed.turnOn(): {}
on iLed.turnOff(): state = ILED.State.Off;
on iLed.getState(): reply(state);
}
}
}
As noted earlier and shown here again, for state-leading-only logic, every input event (“in” function)
must be handled either explicitly or implicitly inside every [state] block, else your model will have
unhandled-event verification errors (compliance).
Dezyne – Introductory Tutorial
v16
16 December 2016
17
Exercise answers – the first one mixes event-leading and state-leading logic:
component LED {
provides ILED iLed;
behaviour {
ILED.State state = ILED.State.Off;
// mix event-leading and state-leading
on iLed.getState(): reply(state);
[state.Off] {
on iLed.turnOn(): state = ILED.State.On;
on iLed.turnOff(): {}
}
[state.On] {
on iLed.turnOn(): {}
on iLed.turnOff(): state = ILED.State.Off;
}
}
}
Exercise second answer, with minimal logic:
component LED {
provides ILED iLed;
behaviour {
ILED.State state = ILED.State.Off;
//
on
on
on
minimal model of equivalent logic
iLed.turnOn(): state = ILED.State.On;
iLed.turnOff(): state = ILED.State.Off;
iLed.getState(): reply(state);
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
18
States and Illegal Events: Asserts
Suppose that the LED component insists that callers track its state and never call function turnOn() when
the light is already on. Our simple example wouldn’t suffer from such a call, but a wrong call in a lifecritical medical device could be devastating. Dezyne’s illegal keyword expresses the concept of, “if
execution reaches this line then there is a crucial defect somewhere in your logic”. An interface and its
providing component must express exactly the same legal and illegal behaviour as seen by a caller.
Dezyne-generated source code resolves each illegal as an assert() statement or equivalent.
interface ILED {
in void turnOn();
in void turnOff();
behaviour {
enum State { Off, On };
State state = State.Off;
on turnOn: {
[state.Off] state = State.On;
[state.On] illegal;
}
on turnOff: {
[state.On] state = State.Off;
[state.Off] illegal;
}
}
}
component LED {
provides ILED iLed;
behaviour {
enum State { Off, On };
State state = State.Off;
[state.Off] {
on iLed.turnOn():
on iLed.turnOff():
}
[state.On] {
on iLed.turnOn():
on iLed.turnOff():
}
state = State.On;
illegal;
illegal;
state = State.Off;
}
}
In your model code, mismatches between an interface and its provider that involve illegal will show
up as verification errors, but only the Trace and Sequence views can point out the exact problem. Once
any such mismatches are resolved, simulation in the Eclipse editor will not allow an illegal call to be
initiated as an event – such a call will not be “eligible” in Dezyne’s terminology.
As mentioned earlier, when using state-leading logic, there is one exception to the rule that each event
must be declared and handled explicitly in each state in a providing component’s behaviour
Dezyne – Introductory Tutorial
v16
16 December 2016
19
implementation. If a particular event is illegal in a particular state, you are allowed to omit that
declaration in the component’s behaviour; Dezyne will assume that this is an illegal and will check the
interface specification for confirmation. The generated code will have an assert in the same spot where
the explicit declaration would have placed it. To see this exception in action, comment out one of the
component’s illegals as shown below and re-verify:
component LED {
provides ILED iLed;
behaviour {
enum State { Off, On };
State state = State.Off;
[state.Off] {
on iLed.turnOn(): state = State.On;
// on iLed.turnOff(): illegal;
}
[state.On] {
on iLed.turnOn(): illegal;
on iLed.turnOff(): state = State.Off;
}
}
}
Uncluttering code by making use of implicit illegals has the downside of hiding logic from a code
reviewer.
Time and Timers: Making an LED Blink
Language keywords: bool extern false inevitable out true
Operator: !
Dezyne’s logical models have no inherent concept of time; instead, Dezyne only knows sequences of
events and responses to events. Model verification proceeds by examining, directly or indirectly, every
possible eligible-event-initiated trace path through the component and/or interface(s) being verified.
Each step in an underway verification trace represents zero elapsed time in the modeled system (and
takes very little computation time during verification). Any verification trace stops immediately upon
finding an error, thus running to completion signals that no logic-related errors were found in the
model.
Dezyne – Introductory Tutorial
v16
16 December 2016
20
Dezyne can model and verify repeating behaviour. We’ll build a timer interface that makes our LED blink
on and off at constant time intervals, and that works as-is in auto-generated C++ source code. The
entirely new – but nearly all familiar – code below defines a simple timer interface and a hand-written
component that will implement the interface. An out function names an event that a requiring (calling)
component has to handle in its behaviour.
interface ITimer {
enum State { Idle, Running };
in void start();
in void cancel();
out void timeout();
behaviour {
State state = State.Idle;
[state.Idle] {
on start: state = State.Running;
on cancel: {}
}
[state.Running] {
on start: illegal;
on cancel: state = State.Idle;
on inevitable: {
// ALWAYS happens eventually unless cancelled first
state = State.Idle;
timeout;
}
}
}
}
component Timer { provides ITimer iTimer; }
Dezyne’s inevitable keyword indicates that the timeout event will always eventually happen during
model verification, to test all possible execution paths, including the possibility of a timeout just before a
cancel call arrives. The inevitable keyword has a companion we’ll discuss later, optional, which
means “usually happens but might never happen”; both these keywords are used only in interfaces, to
inform verification about certain special conditions it must check.
Note in the above the asymmetry between the Idle state and the Running state. The interface says it’s
OK to cancel when the timer is idle, but it’s illegal to try to start when the timer is running. We assume
the caller will manage its own state with respect to use of the timer.
Dezyne – Introductory Tutorial
v16
16 December 2016
21
The next version of the timer shows how to pass a parameter of a particular type to a model method.
interface ITimer {
extern long_integer $long$;
// source code data type will be long
in void start(long_integer milliseconds);
in void cancel();
out void timeout();
behaviour {
enum State { Idle, Running };
State state = State.Idle;
[state.Idle] {
on start: state = State.Running;
on cancel: {}
}
[state.Running] {
on start: illegal;
on cancel: state = State.Idle;
on inevitable: {
// ALWAYS happens eventually unless cancelled first
state = State.Idle;
timeout;
}
}
}
}
component Timer { provides ITimer iTimer; }
The extern keyword above maps a model data type to an indicated data type in Dezyne-generated
source, via string substitution. For example, the above sample leads to this line in generated C++ code:
std::function<void (long milliseconds)> start;
Now we integrate the timer into the LED controller. The interface definition below is complete; it uses
the events-leading pattern.
interface
in void
in void
in void
ILED {
shine();
darken();
blink();
behaviour {
enum State { Dark, Shining, Blinking };
State state = State.Dark;
on shine: { state = State.Shining; }
on blink: { state = State.Blinking; }
on darken: { state = State.Dark; }
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
22
This interface allows a caller to reset the LED to the same state it’s already in. Below is a partial
implementation of the blinkable LED component, sketching in a states-leading approach. You can create
this exact component in Dezyne (though it might need a different name) and verify that it conforms to
both the ILED interface and the ITimer interface, even though the interface expresses its logic in eventleading format. Also, in this component we will track the LED’s lit/dark state as part of managing the
blinking state; as with state variables we have to initialize any boolean variable in its declaration.
component LED {
provides ILED iLed;
requires ITimer iTimer;
behaviour {
enum State { Dark, Shining, Blinking };
State state = State.Dark;
bool lit = false;
[state.Dark] {
on iLed.shine(): {}
on iLed.darken(): {}
on iLed.blink(): {}
on iTimer.timeout(): {}
}
[state.Shining] {
on iLed.shine(): {}
on iLed.darken(): {}
on iLed.blink(): {}
on iTimer.timeout(): {}
}
[state.Blinking] {
on iLed.shine(): {}
on iLed.darken(): {}
on iLed.blink(): {}
on iTimer.timeout(): {}
}
}
}
In the above, we have added a new bool variable named “lit” to track the shining-darkened state
changes when the LED is blinking. The light is off by default, hence variable lit is initialized to false.
Booleans work in Dezyne as they do in C/C++/Java, including the negation operator !.
Dezyne – Introductory Tutorial
v16
16 December 2016
23
Filling in the event handlers for the initially dark LED:
[state.Dark] {
on iLed.shine(): { // -> Shining, lit
state = State.Shining;
lit = true;
}
on iLed.darken(): {} // ignore
on iLed.blink():
{ // -> Blinking, half-second timer
lit = false;
state = State.Blinking;
iTimer.start($500$);
}
on iTimer.timeout(): {} // shouldn’t occur, just ignore
}
The Shining state event handlers are essentially the same as for Dark. Blinking state handlers are below.
When Blinking, on each timer timeout the light switches between off and on and restarts the timer.
[state.Blinking] {
on iLed.shine(): {
iTimer.cancel();
state = State.Shining;
}
on iLed.darken(): {
iTimer.cancel();
state = State.Dark;
}
on iLed.blink(): {}
on iTimer.timeout(): {
lit = !lit;
iTimer.start($500$);
}
}
You can use if else instead of the negation operator when handling the timeout, as below. In Dezyne,
the if-else construct can appear only within on-event behaviour.
[state.Blinking] {
on iLed.shine(): {
iTimer.cancel();
state = State.Shining;
}
on iLed.darken(): {
iTimer.cancel();
state = State.Dark;
}
on iLed.blink(): {}
on iTimer.timeout(): {
if (lit) { lit = false; } else { lit = true; }
iTimer.start($500$);
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
24
As an exercise you should complete the [state.Shining] event handlers, then verify and simulate.
Here is a screen shot showing some of the source and all of the Trace, Sequence and State Chart views
for the completed, verified and simulated blink-able LED (the Shining event handlers are visible in it).
Challenge: The above model verifies completely and it simulates with no logic errors (by definition). But
it has a functional bug related to exiting the Blinking state. Can you spot it?
Hint: observe the state variable “lit” in the Watch View window when you simulate. The answer is below
the sidebar.
Answer to the challenge: When exiting the Blinking state you must set the “lit” variable to the indicated
new state, else the state variable might not match the actual on/off state of the LED. Which brings up an
essential point: The first functional test of generated code from this model using an actual LED would
probably reveal the state-tracking bug within minutes. You could then go directly to the Blinking state
code in the model, insert the new assignments, regenerate the source, compile, and link – and the bug
would be gone. Dezyne verifies your logic and helps you quickly validate your implemented system.
Dezyne – Introductory Tutorial
v16
16 December 2016
25
Possible Events: Multiplexing Sensor Inputs
Language keyword: optional
Earlier we worked with a timer, which generates an event after a specified duration unless cancelled
first. Another kind of time-related event is sensor activation. An enabled sensor might signal almost
immediately, or it might never signal at all. Some control systems that use sensors have to account for
events that might never occur; for example, a passcode-entry component should eventually time out
and reset if a user starts keying in a passcode but never finishes and never cancels.
The alarm system we’ll build later uses multiple kinds of passive sensors, i.e. sensors that do not poll but
instead only respond to stimuli. In this section we address optional events – ones that might never
happen – and how to multiplex identical or similar components that provide the same defined interface.
Below is a sensor interface. When the sensor is on (enabled, sensing), at some point in the future it
might trigger an event, or it might never trigger an event. The interface expects a caller to be aware of
the sensor state and not call wrongly (illegal). The new keyword optional is highlighted. Note that
keywords inevitable and optional occur only in interface behaviour, never in component behaviour,
and that the illegal declarations are all necessary, this being an interface and not a component.
interface ISensor {
in void turnOn();
in void turnOff();
out void triggered();
behaviour {
enum State { Off, Sensing, Triggered };
State state = State.Off;
[state.Off] {
on turnOn: state = State.Sensing;
on turnOff: illegal;
// tests caller's logic
}
[state.Sensing] {
on turnOn: illegal;
on turnOff: state = State.Off;
on optional: {
// could trigger soon but might never
triggered;
state = State.Triggered;
}
}
[state.Triggered] {
on turnOn: illegal;
on turnOff: state = State.Off;
}
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
26
These two components provide the same iSensor interface.
component HallEffectSensor {
provides ISensor iSensor;
}
component VibrationSensor {
provides ISensor iSensor;
}
Inevitable Events versus Optional Events
The Dezyne interface keyword inevitable means that a specified event (i.e., out function) will always
eventually happen asynchronously, unless something in your logic always happens to prevent the
specified event – for example a timer that never gets set during execution, or that always gets canceled
before it can time out. The keyword optional means that the event might or might not ever happen
asynchronously. In the practical world, nothing asynchronous always happens in a system’s lifetime: not
every burglar alarm gets tripped, not every email gets eventually delivered, not every transaction
eventually succeeds, not every reptile species is eventually wiped out by a meteor or asteroid strike.
Thus “optional” perhaps more often models the real situation than does “inevitable”. So when and why
would we ever use “inevitable” in a model?
In sum, if you care about the system’s behaviour when an event goes unheeded essentially forever, or
when an awaited event still hasn’t occurred long after you expected it, then use “optional”, for example
for a half-entered passcode. Otherwise, “inevitable” is simpler and will usually test the system’s logical
completeness and correctness well enough.
When you use “optional”, a rule of thumb might be to push down optional behaviour as low as possible
in the system, or equivalently as close as possible to the hardware on which the system runs or depends.
The Multiplexer Component
First let’s specify requirements for a multiplexer regardless of how many sensors it controls:
1. A multiplexer can control any number of sensors, including zero.
2. A multiplexer’s default state is Off.
3. Turning on a multiplexer turns on all of its sensors, unless the multiplexer’s state is already
Sensing or Triggered, in which case turning on is illegal (should never occur).
4. Turning off a multiplexer turns off all of its sensors, unless the multiplexer’s state is already Off,
in which case turning off is illegal.
5. A trigger event from any of a multiplexer’s sensors sets the multiplexer’s state to Triggered and
sends a triggered event to the multiplexer’s caller.
6. If a multiplexer has triggered, it will not trigger again until its sensors have been turned off and
turned back on.
Dezyne – Introductory Tutorial
v16
16 December 2016
27
The below component with zero sensors establishes the framework for multiplexing. Its behaviour is just
a replica of the interface behaviour minus the optional trigger; hence, you can copy the interface’s
whole behaviour block, remove the optional sub-block, reset State references to the local declaration of
State, reset event (function) names to iSensor.<name>, and add parentheses to event (function) names.
The Triggered state will remain dead code until the multiplexer is hooked up to at least one sensor that
can be triggered (note that Dezyne verification does NOT notify you about dead code).
component SensorMultiplexer {
provides ISensor iSensor;
behaviour {
enum State { Off, Sensing, Triggered };
State state = State.Off;
[state.Off] {
on iSensor.turnOn(): state = State.Sensing;
on iSensor.turnOff(): illegal;
// tests caller's logic in model
}
[state.Sensing] {
on iSensor.turnOn(): illegal;
on iSensor.turnOff(): state = State.Off;
}
[state.Triggered] {
on iSensor.turnOn(): illegal;
on iSensor.turnOff(): state = State.Off;
}
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
28
In the below code sample we have added one sensor and highlighted all added source code.
Exercise: Will this component verify? Make a decision, then verify in Dezyne. The answer is below the
code sample.
component SensorMultiplexer {
provides ISensor iSensor;
requires ISensor iSensor1;
behaviour {
enum State { Off, Sensing, Triggered };
State state = State.Off;
[state.Off] {
on iSensor.turnOn(): {
iSensor1.turnOn();
state = State.Sensing;
}
on iSensor.turnOff(): illegal;
}
[state.Sensing] {
on iSensor.turnOn(): illegal;
on iSensor.turnOff(): {
iSensor1.turnOff();
state = State.Off;
}
on iSensor1.triggered(): {
iSensor.triggered();
iSensor1.turnOff();
state = State.Triggered;
}
}
[state.Triggered] {
on iSensor.turnOn(): illegal;
on iSensor.turnOff(): state = State.Off;
on iSensor1.triggered(): illegal;
}
}
}
The above component does verify, in fact. One state-event case is not handled explicitly, “on
iSensor1.triggered():” in [state.Off], but this is an allowed implicit illegal declared in the interface.
The below screen shot shows the simulated SensorMultiplexer’s Trace, Sequence and State Chart views.
Notice that its State Chart is exactly the same as a plain sensor. Aggregated sensor behaviour and single
sensor behaviour are identical from the caller’s point of view, provided that it doesn’t matter what kind
of alarm happened – motion, vibration, magnetic effect, or other.
Dezyne – Introductory Tutorial
v16
16 December 2016
29
In the below code we have added a second multiplexed sensor, with new code highlighted. But this code
has a logic error. Exercise: Can you spot it? If you’re impatient, just verify in Dezyne to get a hint. The
multiplexing logic is highly regular and really quite simple, yet you still might find the error hard to spot
(you would not be alone by any means). Hint: count the instances of “1” versus instances of “2”. The
answer is below the code sample.
Dezyne – Introductory Tutorial
v16
16 December 2016
30
component SensorMultiplexer {
provides ISensor iSensor;
requires ISensor iSensor1;
requires ISensor iSensor2;
behaviour {
enum State { Off, Sensing, Triggered };
State state = State.Off;
[state.Off] {
on iSensor.turnOn(): {
iSensor1.turnOn(); iSensor2.turnOn();
state = State.Sensing;
}
on iSensor.turnOff(): illegal;
on iSensor1.triggered(), iSensor2.triggered(): illegal;
}
[state.Sensing] {
on iSensor.turnOn(): illegal;
on iSensor.turnOff(): {
iSensor1.turnOff();
state = State.Off;
}
on iSensor1.triggered(), iSensor2.triggered(): {
iSensor.triggered();
iSensor1.turnOff(); iSensor2.turnOff();
state = State.Triggered;
}
}
[state.Triggered] {
on iSensor.turnOn(): illegal;
on iSensor.turnOff(): state = State.Off;
on iSensor1.triggered(), iSensor2.triggered(): illegal;
}
}
}
The defect is in [state.Sensing], on turnOff(), which turns off the first sensor but not the second.
Exercise: Repair the defect, verify and simulate, then optionally generate the C++ source and examine it.
Having gone through most Dezyne basics, we’re ready now to design, build, verify and simulate a
somewhat less simple Burglar Alarm system.
Dezyne – Introductory Tutorial
v16
16 December 2016
31
The Alarm System
Requirements
Our next alarm system has a pincode keypad, a multi-color LED, a siren, and sensors. The keypad silently
discards invalid pincodes. The system has two basic sequences of events:
On power up the system is unarmed, the LED is green, and sensors are disabled.
1. Green: unarmed.
2. Yellow: Valid pincode entry arms the system, turns the LED yellow and enables sensors.
3. Red: A sensor trigger disables the sensors and turns the LED red.
4. Either Yellow: Valid pincode entry soon enough cancels the timer and returns the system to
the armed state with yellow LED and enabled sensors.
5. Or Red with siren: Timer timeout turns on the siren.
6. Yellow: Valid pincode entry turns off the siren and returns to armed state.
Alternate sequence with no sensor trigger event:
1. Green: Unarmed
2. Yellow: Valid pincode entry arms the system, turns the LED yellow, and enables sensors.
3. Green: Valid pincode entry disarms the system, turns the LED green, and disables sensors.
One might white-board the initial conceptual system like this:
After power-up, a valid pincode event can arrive any time at the Controller, whether appropriate or not.
Once the Controller enables the sensors, they can trigger events at any moment but possibly never. The
LED is configured to be a solid-color green on power-up, yellow when armed, and red whenever there is
a sensor-generated alarm active. The Controller starts the timer when a sensor triggers and starts the
siren upon timeout. A user must enter a valid pincode to exit the alarm state and to silence the siren if it
has begun sounding.
Dezyne – Introductory Tutorial
v16
16 December 2016
32
The Controller
Usually we start designing by sketching interfaces, since we can always reasonably assume that a “handwritten” component will correctly implement any given interface. The below interface captures the two
main kinds of incoming events for the controller: user-entered pincodes, and sensor trigger events.
Timer timeout events are different since the controller has to initiate them.
interface IController {
in void validPincode();
in void sensorTriggered();
behaviour {
on validPincode: {}
on sensorTriggered: {}
}
}
The above minimal interface declares implicitly that any and every incoming event is “legal”, i.e. the
caller does not have to track the controller’s state and avoid inappropriate calls. Said another way, the
implemented interface will not hit an assertion via an illegal event.
A minimal Controller component would start up Unarmed; go from there to Armed upon a valid
pincode; go from Armed to Alarming upon getting a sensorTriggered event; return to Armed state upon
valid pincode; and return to Unarmed after another valid pincode. As an exercise you might want to
build the minimal controller yourself before reading the below code.
component Controller {
provides IController iController;
behaviour {
enum State { Unarmed, Armed, Alarming };
State state = State.Unarmed;
[state.Unarmed] {
on iController.validPincode(): state =
on iController.sensorTriggered(): {}
}
[state.Armed] {
on iController.validPincode(): state =
on iController.sensorTriggered():state
}
[state.Alarming] {
on iController.validPincode(): state =
on iController.sensorTriggered(): {}
}
State.Armed;
State.Unarmed;
= State.Alarming;
State.Armed;
}
}
The above honors its interface contract by always silently accepting and ignoring a sensorTriggered
event when it is not in the Armed state.
Dezyne – Introductory Tutorial
v16
16 December 2016
33
Adding the LED
Next we add simple control for an LED.
interface ILED {
// assumes green automatically on power-up
in void setGreen();
in void setYellow();
in void setRed();
behaviour { on setGreen, setYellow, setRed: {} }
}
component LED { provides ILED iLed; }
Integrating the LED would make a good exercise.
Note that the below integrated code imports the LED interface as opposed to declaring it.
Dezyne – Introductory Tutorial
v16
16 December 2016
34
import LED.dzn;
interface IController {
in void validPincode();
in void sensorTriggered();
behaviour {
on validPincode: {}
on sensorTriggered: {}
}
}
component Controller {
provides IController iController;
requires ILED iLed;
behaviour {
enum State { Unarmed, Armed, Alarming };
State state = State.Unarmed;
[state.Unarmed] {
on iController.validPincode(): {
state = State.Armed;
iLed.setYellow();
}
on iController.sensorTriggered(): {}
}
[state.Armed] {
on iController.validPincode(): {
state = State.Unarmed;
iLed.setGreen();
}
on iController.sensorTriggered(): {
state = State.Alarming;
iLed.setRed();
}
}
[state.Alarming] {
on iController.validPincode(): {
state = State.Armed;
iLed.setYellow();
}
on iController.sensorTriggered(): {}
}
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
35
Adding the Sensors
Now we add sensors, removing the sensorTriggered event from the Controller and importing the sensor
we constructed earlier.
import LED.dzn;
import Sensor.dzn;
interface IController {
in void validPincode();
behaviour { on validPincode: {} }
}
component Controller {
provides IController iController;
requires ILED iLed;
requires ISensor iSensor;
behaviour {
enum State { Unarmed, Armed, Alarming };
State state = State.Unarmed;
[state.Unarmed] {
on iController.validPincode(): {
iSensor.turnOn();
state = State.Armed;
iLed.setYellow();
}
on iSensor.triggered(): {}
}
[state.Armed] {
on iController.validPincode(): {
iSensor.turnOff();
state = State.Unarmed;
iLed.setGreen();
}
on iSensor.triggered(): {
iSensor.turnOff();
state = State.Alarming;
iLed.setRed();
}
}
[state.Alarming] {
on iController.validPincode(): {
iSensor.turnOn();
state = State.Armed;
iLed.setYellow();
}
on iSensor.triggered(): illegal;
}
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
36
Adding the Siren and Timer
Next we add a siren to be sounded only when a user takes too long to enter the pincode after a sensor
trigger makes the LED go red. The siren interface and component are simple:
interface ISiren {
in void turnOn();
in void turnOff();
behaviour { on turnOn, turnOff: {}
}
}
component Siren { provides ISiren iSiren; }
We’ll put the siren in its own file Siren.dzn and include that file in the Controller. In the next step we
turn on the siren whenever Alarming, and turn it off whenever returning to the Armed state.
Dezyne – Introductory Tutorial
v16
16 December 2016
37
import LED.dzn;
import Sensor.dzn;
import Siren.dzn;
interface IController {
in void validPincode();
behaviour { on validPincode: {} }
}
component Controller {
provides IController iController;
requires ILED iLed;
requires ISensor iSensor;
requires ISiren iSiren;
behaviour {
enum State { Unarmed, Armed, Alarming };
State state = State.Unarmed;
[state.Unarmed] {
on iController.validPincode(): {
iSensor.turnOn();
state = State.Armed;
iLed.setYellow();
}
on iSensor.triggered(): {}
}
[state.Armed] {
on iController.validPincode(): {
iSensor.turnOff();
state = State.Unarmed;
iLed.setGreen();
}
on iSensor.triggered(): {
iSensor.turnOff();
state = State.Alarming;
iLed.setRed();
iSiren.turnOn();
}
}
[state.Alarming] {
on iController.validPincode(): {
iSensor.turnOn();
state = State.Armed;
iLed.setYellow();
iSiren.turnOff();
}
on iSensor.triggered(): illegal;
}
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
38
As an exercise, modify the above model to turn on the siren only on a 30-second timeout after the
system has been triggered into the Alarming state. You will need to import the Timer component we
created earlier and configure it correctly. The fragment of model code below shows only the key
changes needed to the Controller event handling; if you copy and paste this partial solution into Dezyne
you will still have to debug and finish it.
on iSensor.triggered(): {
iSensor.turnOff();
state = State.Alarming;
iLed.setRed();
iTimer.start($30000$);
}
on iTimer.timeout(): illegal;
}
[state.Alarming] {
on iTimer.timeout(): {
iSiren.turnOn();
}
on iController.validPincode(): {
iTimer.cancel();
iSensor.turnOn();
state = State.Armed;
iLed.setYellow();
iSiren.turnOff();
}
Dezyne – Introductory Tutorial
v16
16 December 2016
39
The Completed Alarm System
Below is the Dezyne-generated sequence diagram, and event trace listing, from simulating a complete
round-trip exercise of the Controller component. Events included sounding the siren when the user did
not enter a valid pincode soon enough after an alarm was triggered, and returning at the end to the
disarmed state. (Screen shots were edited for better readability.)
Dezyne – Introductory Tutorial
v16
16 December 2016
40
We finish the alarm system by creating a top-level system component that shows how the other
components interconnect through interfaces. This system component presents the iController interface
to the yet larger software system into which it might be integrated. For this tutorial, the system
component’s purpose is only to auto-generate the diagram shown below the code.
component AlarmSystem {
provides IController iController;
system {
Controller controller;
LED led;
Sensor sensor;
Siren siren;
Timer timer;
iController<=>controller.iController;
controller.iLed<=>led.iLed;
controller.iSensor<=>sensor.iSensor;
controller.iSiren<=>siren.iSiren;
controller.iTimer<=>timer.iTimer;
}
}
Dezyne – Introductory Tutorial
v16
16 December 2016
41
Dezyne – Introductory Tutorial
v16
16 December 2016
42