Adding a state machine to your class

The SMC-generated code is designed to be loosely coupled with your application software. The only changes that you need to make to your code is to:

  1. Include the SMC class definitions into your application (stored in smc/lib by programming language name):
    • C: Have lib/C/statemap.h in the include path.
    • C++: Have lib/C++/statemap.h in the include path.
    • C#: Have lib/DotNet/statemap.dll included in the Visual Studio studio.
    • Groovy: Have lib/Groovy/statemap.jar in the classpath.
    • Java: Have lib/Java/statemap.jar in the classpath.
    • JavaScript: Have lib/JavaScript/statemap.js accessible to <script> tag.
    • Lua: Have the lib/Lua/statemap.lua module on your Lua package.path (initialized by the environment variable LUA_PATH).
    • Objective-C: Have lib/ObjC/statemap.h in the include path.
    • Perl: Have StateMachine::Statemap module on your Perl library path @INC.
    • PHP: Have StateMachine/statemap.php in your PHP include_path.
    • Python: Have the statemap module on your import source path sys.path.
    • Ruby: Have the statemap module on your Ruby library path $LOAD_PATH.
    • Scala: Have lib/Scala/statemap.jar in the classpath.
    • Tcl: Have the lib/Tcl/statemap1.0 package on your path.
    • VB.net: Have lib/DotNet/statemap.dll included in the Visual Studio studio.
  2. Include the state machine's source file:

    • C: #include "AppClass_sm.h"
    • C++: #include "AppClass_sm.h"
    • C#: put the AppClass_sm.vb file in your Visual Studio solution.
    • Groovy: If AppClassContext class is in the same package as AppClass, no importation is needed.
    • Java: If AppClassContext class is in the same package as AppClass, no importation is needed.
    • JavaScript: Have AppClass_sm.js file accessible to <script> tag.
    • Lua: require 'AppClass_sm'
    • Objective-C: #import "AppClass_sm.h"
    • Perl: use AppClass_sm;
    • PHP: require_once 'AppClass_sm';
    • Python: import AppClass_sm
    • Ruby: require 'AppClass_sm'
    • Scala: If AppClassContext class is in the same package as AppClass, no importation is needed.
    • Tcl: source ./AppClass_sm.tcl
    • VB.net: put the AppClass_sm.vb file in your Visual Studio solution.
    where AppClass is your application class with an associated SMC state machine.
  3. Instantiate the state machine's context object.
  4. If you want to execute the start state's entry actions call the state machine context's enterStartState method. This is not needed to set the start state as that is done when the state machine context is instantiated. enterStartState only executes the start state's entry actions (if any exist).

That's all you need to do. Whenever you want to issue a transition, call the context's object transition method.

SMC does not change your code or require you to change your code's logic. SMC does not require that your class inherit or implement any SMC class. SMC does not use state transition arrays or switch statements. SMC state machines are easy to add with minimal impact to your existing code.

CAUTION: THIS BEARS REPEATING. Do NOT issue a transition from within an action - it will cause the state machine to throw an exception since actions are not allowed to issue a transitions.

For an explanation of why this is, see the SMC FAQ question: Why can't an action issue a transition?

If you do need to issue a transition from an action, see the section 7, "Queuing Up" explaining how to use a "transition queue".


C


C language is not Object Oriented. But this behavior could be emulated in order to follow the SMC State pattern.

Assumptions:

Changes to NetworkIF.h

  1. As explained in section 1 , you have added the lines
    %class NetworkIF %header NetworkIF.h %start MainMap::Start

    at the appropriate location in NetworkIF.sm.

  2. Add the following #include to NetworkIF.h:

    #include "NetworkIF_sm.h"

  3. Add the member data to class NetworkIF:

    struct NetworkIFContext _fsm;

    (I used "_fsm" but you can use another name).

Changes to NetworkIF.c

  1. In NetworkIF initializer NetworkIF_Init, add the line:
    void NetworkIF_Init(struct NetworkIF *network) { NetworkIFContext_Init(&this->_fsm, network); }
    Again, the member data does not have to be named _fsm.
    Add the following line, if Push transitions are used.
    FSM_STACK(&network->_fsm, AppStack);
    where AppStack is a memory location.
  2. If the start state has entry actions and these actions need to be executed before transitions are issued, then add the following code:
    NetworkIFContext_EnterStartState(&network->_fsm);
  3. Wherever you want to issue a state machine transition, add the following line of code:
    NetworkIFContext_<transition>(&network->_fsm, ...);
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    NetworkIFContext_Connect(&network->_fsm, "192.168.3.100", 80);

Changes to Makefile

Add NetworkIF_sm.c to the source file list and link NetworkIF_sm.o into your application.

#includes in NetworkIF.sm

If you need to include header files in your .sm file, use the %include keyword:

%include <util/logger.h>

Cautions

The following class and file names are generated by SMC. Be careful not to use them yourself.

where:


SMC and C++


Assumptions:

Changes to NetworkIF.h

  1. Add the following #include to NetworkIF.h:
    #include "NetworkIF_sm.h"
  2. Add the member data to class NetworkIF:
    NetworkIFContext _fsm;

    This member data can be either public, protected or private and can have any name (I used "_fsm" but you can use another name).

  3. All state machine actions must be implemented as NetworkIF public methods. For an explanation as to why these methods must be public, see the answer to the FAQ question: Why do actions have to be declared as public?

Changes to NetworkIF.cpp

  1. In all NetworkIF constructors, add the following to the initialization list:
    _fsm(*this)
    Again, the member data does not have to be named _fsm.
  2. If the start state has entry actions which must be executed before any transitions are issued add the following code:
    _fsm.enterStartState();
  3. Wherever you want to issue a state machine transition, add the following line of code:
    _fsm.<transition>();
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    _fsm.Connect("192.168.3.100", 80);

Changes to Makefile

Add NetworkIF_sm.cpp to the source file list and link NetworkIF_sm.o into your application.

"using namespace" in NetworkIF.sm

If you need to place the SMC-generated classes into a C++ namespace, then see Section 8.

#includes in NetworkIF.sm

If you need to include header files in your .sm file, use the %include keyword:

%include <util/logger.h>

Cautions

The following class and file names are generated by SMC. Be careful not to use them yourself.

where:


SMC and C++ using -crtp (Curiously Recurring Template Pattern)


Assumptions:

Same as C++ assumptions.

Changes to NetworkIF.h

  1. Add the following #include to NetworkIF.h:
    #include "NetworkIF_sm.h"
  2. Inherit the context template:
    class NetworkIF : public NetworkIFContext<NetworkIF>

    This must be a public inheritance otherwise the SMC-generated code will not compile.

  3. All state machine actions must be implemented as NetworkIF public methods. For an explanation as to why these methods must be public, see the answer to the FAQ question: Why do actions have to be declared as public?

Changes to NetworkIF.cpp

  1. If the start state has entry actions which must be executed before any transitions are issued add the following code:
    enterStartState();
  2. Wherever you want to issue a state machine transition, add the following line of code:
    <transition>();
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    Connect("192.168.3.100", 80);

Changes to Makefile

Add NetworkIF_sm.cpp to the source file list and link NetworkIF_sm.o into your application.


[Objective-C]


Assumptions:

Changes to NetworkIF.h

  1. Forward declare the FSM class by adding this line to NetworkIF.h:
    @class NetworkIFContext;
  2. Add the member data to the class NetworkIF:
    NetworkIFContext *_fsm;

Changes to NetworkIF.m

  1. Import the FSM header:
    #import "NetworkIF_sm.h"
  2. When allocating the FSM, pass in the NetworkIF instance reference:
    _fsm = [[NetworkIF alloc] initWithOwner:self];
  3. If the start state has entry actions which must be executed prior to issuing transitions add the following code:
    TODO PUT Objective-C code here.
  4. When you want to issue a state machine transition, add the following line of code:
    [_fsm <transition>];
    Where "<transition>" is the transition name.

SMC and Java


Assumptions

Adding the state machine to NetworkIF requires the following changes to NetworkIF.java:

  1. Add the following member data to class NetworkIF:
    private NetworkIFContext _fsm;
    The data member does not have to be private but may be public or protected. Also, the variable name does not have to be "_fsm".
  2. In all NetworkIF constructors, add the line:
    _fsm = new NetworkIFContext(this);
  3. If the start state has entry actions which must be executed prior to issuing transitions then add the following code outside the NetworkIF constructors:
    _fsm.enterStartState();
  4. Whenever you want to issue a transition, all you need to do is:
    _fsm.<transition>();
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    _fsm.Connect("192.168.3.100", 80);
  5. All state machine actions may be implemented in NetworkIF as either public or package methods so that the SMC generated code can access them.

package in NetworkIF.sm

If you need to place the SMC-generated classes into a Java package, then see Section 8.

import in NetworkIF.sm

If you need to use import statements in your .sm file, use the %import keyword at the top of the .sm file:

%import java.net.InetAddress %import java.util.Calendar

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and Tcl


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.tcl:

  1. At the top of the file, add
    source NetworkIF_sm.tcl
  2. Add the following data member declaration:
    private variable _fsm
    This data member may be either public, protected or private and can have any name (I used "_fsm" but you can use another name).
  3. In the NetworkIF constructor, create the state machine object and place the name into your previously declared data member:
    set _fsm [NetworkIFContext #auto $this];
    Again, you don't have to use "#auto" to name the object but you do have to pass in the NetworkIF object's name to the NetworkIFContext's constructor.
  4. If the start state has entry actions which must be exectued prior to issuing transitions add this code outside the NetworkIF constructors:
    $_fsm enterStartState
  5. Whenever you want to issue a transition, all you need to do is:
    $_fsm <transition>
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments pass them in the transition call:
    $_fsm Connect "192.168.3.100" 80;
  6. All state ma actions must be implemented as NetworkIF public methods. For an explanation as to why these methods must be public, see the answer to the FAQ question: Why do actions have to be declared as public?

"namespace eval" in NetworkIF.sm

If you need to place the SMC-generated classes into a Tcl namespace, then see Section 8.

"package require" in NetworkIF.sm

If you need to use package require or source statements in your .sm file, place these statements inside the verbatim code section at the top of the .sm file:

%{ package require NetworkUtil; %}

Cautions

The following class and file names are generated by SMC. Be careful not to use them yourself.

where:


VB.net


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.vb:

  1. Add the following data member to class NetworkIF:
    Private _fsm As NetworkIFContext
    You can use any variable name you want. I use _fsm.
  2. In the New() constructors add the line:
    _fsm = New NetworkIFContext(Me)
  3. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    _fsm.EnterStartState()
  4. Whenever you want to issue a transition all you need to do is:
    _fsm.<transition>()
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    _fsm.Connect("192.168.3.100", 80)
  5. All state machine actions must be implemented in NetworkIF as Public methods so that the SMC generated code can access them.

Imports in NetworkIF.sm

If you need to use Imports in your .sm file, use the %import keyword in the top of your .sm file:

%import System.Drawing

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


C#


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.cs:

  1. Add the following data member to class NetworkIF:
    private NetworkIFContext _fsm;
    You can use any variable name you want. I use _fsm.
  2. In the NetworkIF constructors add the line:
    _fsm = new NetworkIFContext(this)
  3. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    _fsm.enterStartState()
  4. Whenever you want to issue a transition all you need to do is:
    _fsm.<transition>()
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    _fsm.Connect("192.168.3.100", 80)
  5. All state machine actions must be implemented in NetworkIF as public- or package-level methods so that the SMC generated code can access them.

Imports in NetworkIF.sm

If you need to use import in your .sm file, use the %import keyword in the top of your .sm file:

%import System.Drawing

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and Groovy


Assumptions

Adding the state machine to NetworkIF requires the following changes to NetworkIF.groovy:

  1. Add the following member data to class NetworkIF:
    private def _fsm
    The data member does not have to be private but may be public or protected. Also, the variable name does not have to be "_fsm".
  2. In all NetworkIF constructors, add the line:
    _fsm = new NetworkIFContext(this)
  3. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put Groovy example code here.
  4. Whenever you want to issue a transition, all you need to do is:
    _fsm.<transition>()
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    _fsm.Connect('192.168.3.100', 80)
  5. All state machine actions may be implemented in NetworkIF as either public or package methods so that the SMC generated code can access them.

package in NetworkIF.sm

If you need to place the SMC-generated classes into a Groovy package, then see Section 8.

import in NetworkIF.sm

If you need to use import statements in your .sm file, use the %import keyword at the top of the .sm file:

%import groovy.util %import java.net.InetAddress %import java.util.Calendar

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and Lua


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.lua:

  1. In the NetworkIF new methods add the line:
    o._fsm = NetworkIF_sm.sm:new({_owner = o})
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put Lua example code here.
  3. Whenever you want to issue a transition all you need to do is:
    self._fsm:<transition>()
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    self._fsm:Connect("192.168.3.100", 80)

Imports in NetworkIF.sm

If you need to use require in your .sm file, use the %import keyword in the top of your .sm file:

%import System.Drawing

Cautions

The following classes or objects are generated by SMC. Be careful not to use them yourself.

where:


SMC and Python


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.py:

  1. In the NetworkIF __init__ methods add the line:
    self._fsm = NetworkIF_sm.NetworkIF_sm(self)
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put Python example code here.
  3. Whenever you want to issue a transition all you need to do is:
    self._fsm.<transition>()
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    self._fsm.Connect("192.168.3.100", 80)

Imports in NetworkIF.sm

If you need to use import in your .sm file, use the %import keyword in the top of your .sm file:

%import System.Drawing

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and PHP


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.php:

  1. In the NetworkIF __construct methods add the line:
    $this->_fsm = new NetworkIF_sm($this);
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put PHP example code here.
  3. Whenever you want to issue a transition all you need to do is:
    $this->_fsm-><transition>();
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    $this->_fsm->Connect("192.168.3.100", 80);

Imports in NetworkIF.sm

If you need to use require_once in your .sm file, use the %import keyword in the top of your .sm file:

%import Event/Dispatcher.php

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and Perl


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.pm:

  1. In the NetworkIF new methods add the line:
    $self->{_fsm} = new NetworkIF_sm($self);
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put Perl example code here.
  3. Whenever you want to issue a transition all you need to do is:
    $self->{_fsm}-><transition>();
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    $self->{_fsm}->Connect("192.168.3.100", 80);

Imports in NetworkIF.sm

If you need to use use in your .sm file, use the %import keyword in the top of your .sm file:

%import System.Drawing

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and Ruby


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.rb:

  1. In the NetworkIF initialize methods add the line:
    @_fsm = NetworkIF_sm::new(self)
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put Ruby example code here.
  3. Whenever you want to issue a transition all you need to do is:
    @_fsm.<transition>()
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    @_fsm.Connect("192.168.3.100", 80)

Imports in NetworkIF.sm

If you need to use require in your .sm file, use the %import keyword in the top of your .sm file:

%import System.Drawing

Cautions

The following classes are generated by SMC. Be careful not to use them yourself.

where:


SMC and Scala


Assumptions

Adding the state machine to NetworkIF requires the following changes to NetworkIF.scala:

  1. Add the following member data to class NetworkIF:
    private val _fsm = new NetworkIFContext(this)
    The data member does not have to be private but may be public or protected. Also, the variable name does not have to be "_fsm".
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    TODO Put Scala example code here.
  3. Whenever you want to issue a transition, all you need to do is:
    _fsm.<transition>()
    Where "<transition>" is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    _fsm.Connect("192.168.3.100", 80)
  4. All state machine actions may be implemented in NetworkIF as either public or package methods so that the SMC generated code can access them.

package in NetworkIF.sm

If you need to place the SMC-generated classes into a Scala a package, then see Section 8.

import in NetworkIF.sm

If you need to use import statements in your .sm file, use the %import keyword at the top of the .sm file:

%import scala.concurrent %import java.net.InetAddress %import java.util.Calendar

Cautions

The following classes or objects are generated by SMC. Be careful not to use them yourself.

where:


JavaScript


Assumptions:

Adding the state machine to NetworkIF requires the following changes to NetworkIF.js:

  1. In the NetworkIF constructor add the line:
    this._fsm = new NetworkIF_sm(this);
  2. If the start state has entry actions which must be executed prior to issuing any transitions add the following code outside the NetworkIF constructors:
    this._fsm.enterStartState();
  3. Whenever you want to issue a transition all you need to do is:
    this._fsm.<transition>();
    where <transition> is the name of the transition you want to take. If the transition takes arguments then pass them in the transition call:
    this._fsm.Connect("192.168.3.100", 80);