VFSM Library Programmers Guide
VFSM library Programmer’s Guide
Table of Contents
Section titled “Table of Contents”- Introduction
- 1. Writing an Application
- 2. The VFSM Library
- 2.1 Class
Cvfsm- 2.1.1 Public member functions
- 2.1.2
CVfsm::CVfsm - 2.1.3
CVfsm::~CVfsm - 2.1.4
Cvfsm::GetSpecinfo - 2.1.5
Cvfsm::Execute - 2.1.6
Cvfsm::Execute - 2.1.7
Cvfsm::Execute - 2.1.8
Cvfsm::DisplayState - 2.1.9
Cvfsm::DisplayVI - 2.1.10
Cvfsm::Trace
- 2.2 Class
CSet- 2.2.1 Public member functions
- 2.2.2
CSet::CSet - 2.2.3
CSet::Copy - 2.2.4
CSet::Insert - 2.2.5
CSet::Remove - 2.2.6
CSet::Clear - 2.2.7
CSet::Unions
- 2.1 Class
- Appendix
Introduction
Section titled “Introduction”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. Writing an Application
Section titled “1. Writing an Application”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.
1.1.1 Creating the state machine
Section titled “1.1.1 Creating the state machine”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 machinestConfigPathis the path of the directoryConfcontaining the specification files&aOnOff_Action[0]is the address of the table of pointers to output functions.ONOFFNUMBFUNCis the number of output functions(vector<pSet>*)&ONOIis 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.
1.1.2 Calling the state machine
Section titled “1.1.2 Calling the state machine”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].
1.1.3 Implementing the output functions
Section titled “1.1.3 Implementing the output functions”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.
2. The VFSM Library
Section titled “2. The VFSM Library”2.1 Class Cvfsm
Section titled “2.1 Class Cvfsm”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.
2.1.1 Public member functions
Section titled “2.1.1 Public member functions”CVfsm | Constructor, creates the state machine |
~CVfsm | Destructor, destroys the state machine |
GetSpecInfo | Reads *.str and *.iod files of a state machine |
Execute | Performs state actions and transitions |
DisplayState | Returns a string, the state name |
DisplayVI | Returns a string, the virtual input (VI) content |
Trace | Switches tracing off or on |
2.1.2 CVfsm::CVfsm
Section titled “2.1.2 CVfsm::CVfsm”void CVfsm( const string stVfsmName, const string stConfPath, const pFunc* aActions, unsigned short iActNum, vector<pSet>* InputObjectName)| Parameters | stVfsmName | The name of the state machine. |
stConfPath | The full path to the Conf directory containing the specification files. | |
aActions | The address of the array of output function (action) pointers. | |
iActNum | The number of output functions. | |
InputObjectName | The 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.
2.1.3 CVfsm::~CVfsm
Section titled “2.1.3 CVfsm::~CVfsm”void ~CVfsm( void)2.1.4 Cvfsm::GetSpecinfo
Section titled “2.1.4 Cvfsm::GetSpecinfo”bool GetSpecInfo( )Return value
true if config files (*.str, *_out.h) found, else false.
Remarks
The application should terminate if this function fails.
2.1.5 Cvfsm::Execute
Section titled “2.1.5 Cvfsm::Execute”bool Execute( const unsigned short& iEvent )bool Execute( const unsigned short& iEvent, const CSet& Remove )| Parameters | iEvent | Input Name (number) as defined in the specification. |
Remove | A 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).
2.1.6 Cvfsm::Execute
Section titled “2.1.6 Cvfsm::Execute”bool Execute( CSet& Events )bool Execute( CSet& Events, const CSet& Remove )| Parameters | iEvent | A set of Input Names as defined in the specification. |
Remove | A 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).
2.1.7 Cvfsm::Execute
Section titled “2.1.7 Cvfsm::Execute”bool Execute( const unsigned short* piEvent )| Parameters | piEvent | A 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.
2.1.8 Cvfsm::DisplayState
Section titled “2.1.8 Cvfsm::DisplayState”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.
2.1.9 Cvfsm::DisplayVI
Section titled “2.1.9 Cvfsm::DisplayVI”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.
2.1.10 Cvfsm::Trace
Section titled “2.1.10 Cvfsm::Trace”bool Trace( const bool bTraceEnable, string stFileName = "MyTrace.txt" )| Parameters | bTraceEnable | A true value opens the trace file and starts tracing. A false value closes the trace file. |
stFileName | A 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.
2.2 Class CSet
Section titled “2.2 Class CSet”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.
2.2.1 Public member functions
Section titled “2.2.1 Public member functions”CSet | Constructor, creates a set |
Copy | Copies a set to a destination set |
Insert | Inserts an element or a set of elements into a set, before inserting it may remove a set of elements |
Remove | Removes an element from a set |
Clear | Removes all elements from a set |
Unions | Merges two sets while inserting into a destination set all elements of the source set that are not existent in the destination set. |
2.2.2 CSet::CSet
Section titled “2.2.2 CSet::CSet”CSet()CSet( SBTYPE bits )CSet( SBTYPE bits, SET arr, SBTYPE size )| Parameters | bits | Number of elements in the set. |
arr | Array of elements (unsigned short) to be inserted into the set. | |
size | Size 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.
2.2.3 CSet::Copy
Section titled “2.2.3 CSet::Copy”void CSet( CSet *set )| Parameters | set | A 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.
2.2.4 CSet::Insert
Section titled “2.2.4 CSet::Insert”bool Insert( SBTYPE n )bool Insert( CSet *remset, SBTYPE n )bool Insert( CSet *remset, CSet *uniset )bool Insert( SET arr, SBTYPE size )| Parameters | n | The element (unsigned short) to be inserted. |
| Parameters | remset | A pointer to the set of elements to be removed before insertion. |
| Parameters | uniset | A pointer to the set of elements to be inserted. |
| Parameters | arr | An pointer to the array of elements to be inserted. |
| Parameters | size | The size of the array to be inserted. |
Return value
true if the function has been successful, otherwise false.
2.2.5 CSet::Remove
Section titled “2.2.5 CSet::Remove”bool Remove( SBTYPE n )| Parameters | n | The element (unsigned short) to be inserted. |
Return value
none
Remarks
true if the element has been removed, otherwise false.
2.2.6 CSet::Clear
Section titled “2.2.6 CSet::Clear”void Clear()Return value
none
Remarks
After the operation the set is empty.
2.2.7 CSet::Unions
Section titled “2.2.7 CSet::Unions”void Unions( CSet *set )| Parameters | set | The 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.
Appendix
Section titled “Appendix”A.01 OnOff example
Section titled “A.01 OnOff example”Main.cpp : the application
Section titled “Main.cpp : the application”// 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 : the output functions
Section titled “onoff_out.cpp : the output functions”/**************************************************************/// 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”/**************************************************************/// 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