Skip to content

VFSM Library Programmers Guide

VFSM library Programmer’s Guide

The specification of state machines in the StateWORKS Studio is done in a virtual environment. For a programmer who develops an RTDB based application this issue is transparent and in principle irrelevant. The RTDB realizes the interface between the world of real signals and the virtual environment. Thus, a programmer has no points of contact with the VFSM Executor and concentrates on I/O Handlers and output functions triggered by OFUN object.

In contrast, the VFSM library contains only the FSM Executor that can be built into the application using a limited number of access methods, essentially a few variants of the the Execute() function. In such a development environment the important task would be the conversion of real input signals into virtual inputs (Input Names) and triggering appropriate functions in response to virtual outputs (Output Names). This task is very error prone as changes in the specification can change the virtual values. To lower the burden of these real/virtual and virtual/real conversions the StateWORKS Studio generates a file xxx_out.h that contains initialized C++ constants that hold those conversions.

Those constants are only relevant to specifications that do not use complement values. For a specification with a complement values a programmer has to realize the input real/virtual conversions by hand. For that purpose he should use the methods of the class CSet provided in the VFSM library package. Of course, a mixed approach is possible, where state machines without complement use the constants provided. Frankly speaking we are not too enthusiastic about that way. This task is complex and doubly error prone and we suggest a switch to the RTDB for any such applications.

A development system that uses ready-made libraries must have tools that allow a (logical) debugging as we are not able to step into the code of VFSM Executor functions. RTDB base applications have very comfortable monitors to supervise all objects in the real time data base. When building an application with the VFSM library we do not have these possibilities that are based on the TCP/IP Server functionality of the RTDB. The trace feature built into the VFSM Executor compensates for the lack of TCP/IP. A trace file contains state and virtual input changes of all state machines in the system. This is a truly powerful method of debugging state machines when using the VFSM library.

1.1 An application with a single state machine

Section titled “1.1 An application with a single state machine”

Using the VFSM library in an application is a relatively simple task. On the one side we have to create the state machine and call the state machine by any input change. On the other side we have to write output functions triggered by the state machine actions.

We illustrate the use of VFSM library functions with an example which is a coded version of the state machine OnOff from our Sample directory (see the User’s Guide). The program is a simple console application which only aim is to show the use of VFSM library functions.

The state machine OnOff is created in the code line:

CVfsm OnOff( "onoff",
stConfigPath,
&aOnOff_Action[0],
ONOFFNUMBFUNC,
(vector<pSet>*)&ONOI );

where:

  • "onoff" is a string, the name of the state machine
  • stConfigPath is the path of the directory Conf containing the specification files
  • &aOnOff_Action[0] is the address of the table of pointers to output functions.
  • ONOFFNUMBFUNC is the number of output functions
  • (vector<pSet>*)&ONOI is the address of the set of pointers to sets of input names

The 3 last parameters are declared in the file onoff_out.h (to be found in the directory Conf).

When the state machine is created the code has to contain the function GetSpecInfo() which load the files onoff.str defining the behavior of the state machine and onoff.iod that contains names used in the specification. If any of those files cannot be opened the program should terminate. This is the purpose of the line:

if ( !OnOff.GetSpecInfo( ) )

At this moment we may also decide to switch on tracing, which happens in the line:

bool bTrace = OnOff.Trace( true );

In that case we have accepted the default name ("MyTrace.txt") of the trace file. Tracing can be disabled or enabled at any time by calling the function Trace with parameter: false or true. We may also create at any time a new trace file giving the file a new name but at any time we should have opened only one trace file; therefore before creating a new trace file the old one has to be closed.

The state machine should be called if an input changes. We assume that inputs may have only those values used in the specification. The state machine is then called using the function Execute(). The example OnOff uses 3 input types: a command (CMD), a timer (TI) and a digital input (DI). On receiving an input of a given type we call correspondingly:

OnOff.Execute( aONOI_MyCmd[iInput] );
OnOff.Execute( aONOI_Di[0] );

where the parameter is the virtual input taken from the conversion array defined in the onoff_out file.

Alternatively, we might have used:

OnOff.Execute( aONOI_MyCmd[iInput], ONOI_MyCmd );
OnOff.Execute( aONOI_MyCmd[iInput], ONOI_Di );

but this is in principle redundant as the function Execute will find the proper remove sets. It makes sense if the time considerations play a role as this variant of the function Execute is faster. The alternative form of the function Execute must be used if the specification uses the complement values as the Studio does not provide correct conversion arrays in such a case.

The call of the function Execute if the timer elapses is done with a direct value (ONOI_Timer_OVER) taken from the file onoff.h (see the ThreadTimer). It results from the implementation of the timer done in this example in the TimerThread. If we like to make everything as in RTDB we should define a true Input corresponding to the control value OVER and translate it into the virtual value using the conversion array aONOI_Timer[iInput].

The output functions triggered by the Executor are declared in the file onoff_out.h. Our task is to implement them and we have shown it in the file onoff_out.cpp. The implementation is primitive; the communication between the output functions and the TimerThread is done via global variables. The aim of this example is not to teach how to realize a multitasking system but to demonstrate the use of the VFSM library functions. The functions do not do anything useful: in principle they write on the console that they have been called.

1.2 An application with a system of state machines

Section titled “1.2 An application with a system of state machines”

If the application contains several (cooperating) state machines the task becomes more complex. The programming task is a similar one: we have to create the state machines and call the Execute function if an input changes. Typically, we would create a separate thread for each state machine and exchange messages among the threads. The programming task becomes essentially more complex than for a single state machine.

The class CVfsm contains the VFSM Executor. The Executor is to be used when programming applications that use state machine specifications generated by the StateWORKS Studio. The actual VFSM Executor methods are of no use for an application programmer. Their private access specifier makes them irrelevant. Therefore, we confine ourself to discuss in this guide only all public methods of the CVfsm class, i.e. methods that could be used in writing the application.

CVfsmConstructor, creates the state machine
~CVfsmDestructor, destroys the state machine
GetSpecInfoReads *.str and *.iod files of a state machine
ExecutePerforms state actions and transitions
DisplayStateReturns a string, the state name
DisplayVIReturns a string, the virtual input (VI) content
TraceSwitches tracing off or on
void CVfsm( const string stVfsmName, const string stConfPath,
const pFunc* aActions, unsigned short iActNum,
vector<pSet>* InputObjectName)
ParametersstVfsmNameThe name of the state machine.
stConfPathThe full path to the Conf directory containing the specification files.
aActionsThe address of the array of output function (action) pointers.
iActNumThe number of output functions.
InputObjectNameThe address of the set of pointers to sets of input names.

Return value
none

Remarks
The 3 last parameters are declared in the file xxx_out.h.

void ~CVfsm( void)
bool GetSpecInfo( )

Return value
true if config files (*.str, *_out.h) found, else false.

Remarks
The application should terminate if this function fails.

bool Execute( const unsigned short& iEvent )
bool Execute( const unsigned short& iEvent, const CSet& Remove )
ParametersiEventInput Name (number) as defined in the specification.
RemoveA set of Input Names to be removed from the VI.

Return value
false if the VI could not be actualized correctly and consequently the executor has been not called, otherwise true.

Remarks
The iEvent value should be taken from the file xxx_out.h.

The second variant of the function should be used only if the file xxx_out.h does not contain an appropriate remove set (happens only by specification that use complement values).

bool Execute( CSet& Events )
bool Execute( CSet& Events, const CSet& Remove )
ParametersiEventA set of Input Names as defined in the specification.
RemoveA set of Input Names to be removed from the VI.

Return value
false if the VI could not be actualized correctly and consequently the executor has been not called, otherwise true.

Remarks
The Event value should be taken from the file xxx_out.h.

The second variant of the function should be used only if the file xxx_out.h does not contain an appropriate remove set (happens only by specification that use complement values).

bool Execute( const unsigned short* piEvent )
ParameterspiEventA pointer to the constant that converts the Slave’s states into the virtual Input Names as defined in the specification.

Return value
false if the VI could not be actualized correctly and consequently the executor has been not called, otherwise true.

Remarks
The piEvent value should be taken from the file xxx_out.h.

string DisplayState( )

Return value
A string, the state name in the form illustrated by the example:
OffBusy (3)
where the state name is followed by a corresponding state number in brackets.

Remarks
Used in the trace file.

string DisplayVI( )

Return value
A string, the content of the VI in the form illustrated by the example:
{ always (1), CmdOn (3), Timer_OVER (4) }
where each entry is displayed as a name followed by a corresponding input name number (as used in the specification) in brackets.

Remarks
Used in the trace file.

bool Trace( const bool bTraceEnable, string stFileName = "MyTrace.txt" )
ParametersbTraceEnableA true value opens the trace file and starts tracing. A false value closes the trace file.
stFileNameA string – the name of the trace file with a default name "MyTrace". The file is created in the Conf directory.

Return value
false if the trace file could not be opened, otherwise true.

Remarks
The trace function can be used at any time in the application.

The class CSet is needed for the class CVfsm. For an application programmer the class CSet is less interesting as in principle he does not need it. Anyway, in some cases he might construct sets and in that case he needs some member functions of that class. The class CSet is relatively large and we limit our discussion to few methods that may be useful in application programming.

CSetConstructor, creates a set
CopyCopies a set to a destination set
InsertInserts an element or a set of elements into a set, before inserting it may remove a set of elements
RemoveRemoves an element from a set
ClearRemoves all elements from a set
UnionsMerges two sets while inserting into a destination set all elements of the source set that are not existent in the destination set.
CSet()
CSet( SBTYPE bits )
CSet( SBTYPE bits, SET arr, SBTYPE size )
ParametersbitsNumber of elements in the set.
arrArray of elements (unsigned short) to be inserted into the set.
sizeSize of the array.

Return value
none

Remarks
The first constructor creates an empty set with a default size equal to 16 (a size of unsigned short).

The second constructor creates a set with a size defined by the bits parameter.

The third constructor creates a set with a size defined by the bits parameter and initialized the set to elements contained in the array.

void CSet( CSet *set )
ParameterssetA pointer to the set to be copied.

Return value
none

Remarks
If the capacity of the destination set is less than the copied set only the elements that “fit” into the destination set are copied.

bool Insert( SBTYPE n )
bool Insert( CSet *remset, SBTYPE n )
bool Insert( CSet *remset, CSet *uniset )
bool Insert( SET arr, SBTYPE size )
ParametersnThe element (unsigned short) to be inserted.
ParametersremsetA pointer to the set of elements to be removed before insertion.
ParametersunisetA pointer to the set of elements to be inserted.
ParametersarrAn pointer to the array of elements to be inserted.
ParameterssizeThe size of the array to be inserted.

Return value
true if the function has been successful, otherwise false.

bool Remove( SBTYPE n )
ParametersnThe element (unsigned short) to be inserted.

Return value
none

Remarks
true if the element has been removed, otherwise false.

void Clear()

Return value
none

Remarks
After the operation the set is empty.

void Unions( CSet *set )
ParameterssetThe set to be merged with a destination set.

Return value
none

Remarks
Effectively, it inserts the set into the destination set. The elements of the set that do not “fit” into the destination set are ignored.

// Main file for a test application
// This application tests the fsm executor concept
#include <iostream>
#include <windows.h>
#include "pthread.h"
#include "vfsm.h"
#include "onoff_out.h"
bool g_bRunning = true;
bool g_bStarted = false;
//--------------------------------------------------------------
void* TimerThread ( void* p )
//--------------------------------------------------------------
{
CVfsm* Vfsm = (CVfsm*)p;
unsigned int iTimeout = 3000;
unsigned short iInput = ONOI_Timer_OVER;
while( g_bRunning )
{
if ( g_bStarted )
{
iTimeout = 3000;
Sleep( iTimeout );
if ( g_bStarted )
{
Vfsm->Execute( iInput );
g_bStarted = false;
}
}
Sleep( 1 );
}
return NULL;
};
main( int argc, char* argv[] )
{
char ac[4];
// get config path
string stConfigPath( "" );
if ( argc > 1 )
stConfigPath = argv[1];
CVfsm OnOff( "onoff", stConfigPath, &aOnOff_Action[0],
ONOFFNUMBFUNC, (vector<pSet>*)&ONOI );
pthread_t pTimerThread;
// Create the Timer thread
if ( ( pthread_create( &pTimerThread,
NULL,
TimerThread,
(void*)&OnOff ) ) == 0 )
cout << "Timer thread created" << endl;
else
cout << "Cannot create Timer thread" << endl;
if ( !OnOff.GetSpecInfo( ) )
{
cout << "Terminate with Enter" << endl;
cin.getline(ac, 4);
return 1;
}
bool bTrace = OnOff.Trace( true );
int iInput;
do
{
if ( bTrace )
cout << endl << "State = " << OnOff.DisplayState() << endl;
cout << "Enter Cmd or DevDi (1: CmdOff, 2: CmdOn, 3: DeviceOff (0), 4: DeviceOn (1)" << endl;
cout << "7: Trace off, 8 Trace on, 9: end)" << endl;
cin.getline(ac, 4);
iInput = atoi( ac );
switch ( iInput )
{
case 1: // Command Stop
case 2: // Command Start
OnOff.Execute( aONOI_MyCmd[iInput] );
break;
case 3: // DeviceDi Off (0)
OnOff.Execute( aONOI_Di[0] );
break;
case 4: // DeviceDi On (1)
OnOff.Execute( aONOI_Di[1] );
break;
case 7:
bTrace = OnOff.Trace( false ); // trace off
break;
case 8:
bTrace = OnOff.Trace( true ); // trace on
break;
default:
break;
}
} while ( iInput < 9 );
cout << endl << "Terminate with Enter" << endl;
cin.getline(ac, 4);
return 0;
}
onoff_out.cpp
/**************************************************************/
// User written output (action) functions
// Definition of functions declared in ..._out.h
/**************************************************************/
// The action functions (declared in ..._out.h file) returns
// typically 0.
// The functions may return a control (virtual) value taken from
// constants declared int ..._out.h ( typically for OFUN
// objects)
// If the return value (taken from constants declared int
// ..._out.h)
// is negative this value will be removed from the virtual input
// (use for the action Cmd_Clear).
//
#include <iostream>
#include "onoff_out.h"
extern bool g_bStarted;
bool bAlarm = false;
bool bOfu = false;
//--------------------------------------------------------------
int OnOff_Dummy( )
{
return 0;
};
//--------------------------------------------------------------
int OnOff_Timer_ResetStart( )
{
g_bStarted = true;
return 0;
};
//--------------------------------------------------------------
int OnOff_Timer_Stop( )
{
g_bStarted = false;
return 0;
};
//--------------------------------------------------------------
int OnOff_Alarm_Coming( )
{
bAlarm = true;
cout << "\tDevice switching too long - Coming\n";
return 0;
};
//--------------------------------------------------------------
int OnOff_Alarm_Going( )
{
if ( bAlarm )
{
cout << "\tDevice switching too long - Going\n";
bAlarm = false;
}
return 0;
};
//--------------------------------------------------------------
int OnOff_SwitchOff( )
{
cout << "\tSwitch off the device" << endl;
return 0;
};
//--------------------------------------------------------------
int OnOff_SwitchOn( )
{
cout << "\tSwitch on the device" << endl;
return 0;
};

onoff_out.h : the declaration of output functions, conversation arrays and sets

Section titled “onoff_out.h : the declaration of output functions, conversation arrays and sets”
G:\StateWORKS\Projects\...\OnOff\Conf\OnOff_out.h
/**************************************************************/
// Declarations of output functions (actions) and
// definitions of sets of input names.
// The output functions have to be implemented while writing
// the applications.
// The set of input names is used by the vfsm library while
// carrying out the function Execute().
//
// The file is generated by SWStudio: must not be changed!
//
#ifndef ONOFF_OUT_H
#include "vfsm.h"
#include "onoff.h"
// output functions (actions)
int OnOff_Dummy( );
int OnOff_Timer_ResetStart( );
int OnOff_Timer_Stop( );
int OnOff_Alarm_Coming( );
int OnOff_Alarm_Going( );
int OnOff_SwitchOff( );
int OnOff_SwitchOn( );
const pFunc aOnOff_Action[] =
{
OnOff_Dummy,
OnOff_Timer_ResetStart,
OnOff_Timer_Stop,
OnOff_Alarm_Coming,
OnOff_Alarm_Going,
OnOff_SwitchOff,
OnOff_SwitchOn,
};
#define ONOFFNUMBFUNC
(sizeof(aOnOff_Action)/sizeof(aOnOff_Action[0]))
// Array of input names used for translation of real signals
// into virtual ones.
// Set of input names used as remove sets while actualizing
// the virtual input.
const unsigned short aONOI_MyCmd[] = {
/*0*/ 0,
/*1*/ ONOI_CmdOff,
/*2*/ ONOI_CmdOn,
};
const CSet ONOI_MyCmd( ONOI_DeviceOn+1, (SET)aONOI_MyCmd,
sizeof(aONOI_MyCmd)/sizeof(aONOI_MyCmd[0]) );
const unsigned short aONOI_Timer[] = {
/*0*/ 0,
/*1*/ 0,
/*2*/ 0,
/*3*/ 0,
/*4*/ ONOI_Timer_OVER,
};
const CSet ONOI_Timer( ONOI_DeviceOn+1, (SET)aONOI_Timer,
sizeof(aONOI_Timer)/sizeof(aONOI_Timer[0]) );
const unsigned short aONOI_Di[] = {
/*0*/ ONOI_DeviceOff,
/*1*/ ONOI_DeviceOn,
};
const CSet ONOI_Di( ONOI_DeviceOn+1, (SET)aONOI_Di,
sizeof(aONOI_Di)/sizeof(aONOI_Di[0]) );
// Array of pointers to sets of input names.
// Set of pointers to sets of input names used while actualizing
// the virtual input.
const pSet aONOI[] = {
&ONOI_MyCmd,
&ONOI_Timer,
&ONOI_Di,
};
const vector<pSet> ONOI( aONOI,
aONOI+sizeof(aONOI)/sizeof(aONOI[0]));
#define ONOFF_OUT_H 1
#endif