Skip to content

Building RTDB based Application

Building an RTDB based application

This description is an introduction to the art of developing StateWORKS run-time systems. As such systems are RTDB-based programs a programmer has to have a good knowledge of RTDB methods described in detail in the RTDB manual. The discussed Example shows some principles which have to be followed, but it is not a final code for an RTDB application. The Example project has been compiled and built using Visual Studio, version 7 (VS 2003), version 8 (VS 2005) and Version 9 (VS 2008). Other platforms need some adaptation due to the usual idiosyncrasies of development environments.

The heart of a StateWORKS application is the RTDB library which is available in several forms depending on the operating system (Windows, Linux, etc.), the application size (LE, BASIC, PRO) and the source of the specification files (hard disk, flash memory). The RTDB library is a kind of data base, storing and processing objects defined by the system specification. In addition, it contains a VFSM Executor which executes the state machines defined while specifying the system behavior.

While developing a disk based system the C++ project requires a path to the \Conf file of the project specified with StateWORKS Studio. The C++ project uses h-files. Other files (*.swd, *.iod and *.str) are then read on starting the application. All files used by a StateWORKS application are generated by Visual Studio.

While developing an embedded system without a hard disk the C++ project requires only a cpp-file generated by Studio. The cpp-file is then compiled with the application.

The example SWExecStandard is a simple run-time system showing components that have to be developed for an application. Any StateWORKS run-time system requires the following modules: the main file, a few standard libraries (among others the RTDB lib, all are in the \Rtdb\Lib directory) and the Ofun library. Such a system will work whereas input and output objects in the RTDB can be accessed via TCP/IP channels using for instance StateWORKS monitors. Normally, an application also requires access to input and output objects using hardware specific I/O handlers. For this additionally a user written I/O library is required.

SWExecStandard can be used “as is” as an RTDB based application. The TCP/IP Server of the RTDB allows any number of Clients to access all RTDB objects. Thus, it is imaginable to use the TCP/IP channels not only for User Interface but also as a link for inputs and outputs. In such a case the I/O handler is obsolete and may be removed from the system.

The main module has to realize some basic functions required on starting the application, like:

  • creating the RTDB
  • starting the TCP/IP server
  • defining the password
  • defining the application clock

In addition, it should display messages informing the user about a start success or problems (the details are contained in the file SULOG.txt). The form (exe or service) of the run-time system and its user interface depends on the application, environment and is not the topic of that document. We have to mention only that as a rule the main module does not contain the application user interface allowing an access and manipulation of RTDB objects. The RTDB library contains a TCP/IP server for communication with RTDB objects and we recommend the use of a separate program (a TCP/IP client) for a user interface. Examples of such interfaces are the various StateWORKS monitors.

In our example the main module is the file RTDBApp.cpp. The RTDB is represented by the global variable

CVfsmSystem g_VfsmSystem

The class CVfsmSystem is declared in the file vswin.h where we find definitions of all methods needed in the main program. The most interesting line in the code is the call of the Create() method

int iConfigResult = g_VfsmSystem.Create( stVFSMTypePath,
stDataFilePath,
stConfigName,
DONOTSTART_TCPIP );

The Create() method builds the RTDB data base using the content of the configuration file prepared with the help of StateWORKS Studio. The method requires three parameters defining paths to specification file directories (normally both ...\Conf) and the name of the configuration file (*.swd). Actually, the stVFSMTypePath defines the directory containing the *.swd, *.iod and *.str files and the stConfigFilePath specifies the directory where the log files (SULOG.txt, TRACE.txt and AlarmLog.txt) will be written. In most cases all files are in the Config directory.

The value of the fourth boolean parameter specifies whether the TCPIP server is to start ( true ) or not ( false ). If we set the last parameter to true, the TCP/IP server would start with a default port number 59091. In the example we use for that purpose the method StartTCPIPCommunication in the line

if ( g_VfsmSystem.StartTCPIPCommunication( lPortNumber, stPassword ) )

where the method requires two parameters. The first one is the required port number and the other one a password. If we specify password clients trying to connect to RTDB must use that password. Programming the main we may use other methods defined in the vswin.h. For instance, in the example we have used

GetTotalItemNumb()

to determine the number of RTDB objects for displaying it at the start up. Similarly, we use

RemoveAll()

to terminate the application properly.

To have a chance for terminating, the program checks in a while-loop at the end of the main the keyboard entries.

To get the RTDB working we have to supply it a constant clock tick which we generate in a separate RunThread() calling there the TimeBaseTick() method

g_VfsmSystem.TimeBaseTick();

Note that the frequency of that call decides about several programs features among others it determines the timers’ clock in the RTDB. Therefore, it must be chosen properly: on the Windows platform the 100 msec delay in the loop is O.K. Note also that the solution used does not guarantee a high accuracy of the clock.

Thus we have now all components required for the simple RTDB based application. Note that by linking we have to add an Ofun library. The installation package contains a standard Ofun library which can be used for that purpose. Later, we may replace it with own Ofun library containing output functions required by our application. On starting the application as programmed in the RTDBApp.cpp file we get the following messages on the monitor:

SWExecStandard.exe
The program should be called with 0-4 arguments:
- -cName: full name (with path) of the configuration file.
If missing the path will be taken from the file .RTDB_Conf.par in the exe directory.
If the RTDB_Conf.par is also missing the application will ask for the path.
- -sService: 0 -> the application does not run as a service
1 -> the application will run as a service
- -tPort: the tcp/ip port number
If missing the default value 59091 will be used.
- -pPassword: a password string.
If missing it is assumed the RTDB can be accessed without password.
Started Configuration file = C:\...\Conf\Standard.swd
Application created with 28 objects
RTDB Server (port 59091) started
TimeBaseTick thread created
Created standardunit Unit1
Exit program by pressing the key 'q' and Enter.

assuming that we have entered the configuration file path: C:\...\Conf\Standard.swd and have acknowledged the next two questions with ENTER accepting the default value of the TCP/IP port number and starting the application without a password. We may connect StateWORKS monitors to the application and watch or otherwise access RTDB objects.

The full StateWORKS run-time system requires an attachment to the input/output signals of the controlled hardware (devices, network, etc). Hence, if we have already programmed the I/O-handler we can link it with the application. We store the pointers to IO-handlers in a global array

CIO_Handler* g_apIOHandler[NUMBIOU];

The class CIO_Handler is defined in the file vsiou.h which contains all (virtual) methods needed for creation and interfacing with the RTDB. Programming the I/O handler (see the corresponding section below) we replace the virtual methods adequately to our needs. In the example the methods used are declared in the standardio.h file where we declare the class CIO_StandardHandler derived from CIO_Handler. The activation of the I/O-handler requires a few calls in the main. They are placed in two local functions: CreateIOH() and DeleteIOH().

Development of an I/O handler is based on a Unit specified in the StateWORKS Studio. The Unit contains a declaration of RTDB object which are to be accessed from the I/O handler. The function CreateIOH() checks which Units are available in the system configuration using the methods:

pItem = g_VfsmSystem.FirstItem(IT_UNIT, pos);
pItem = g_VfsmSystem.NextItem(IT_UNIT, pos);

which deliver a pointer to the handler. Having the pointer we acquire the access to methods declared in the vsiou.h or standardio.h file. Depending on the Unit name

stType = pUnit->GetUnitTypeName();

we create the handler, for instance of a type CIO_StandardHandler

g_apIOHandler[g_iNumbIOUnit] = new CIO_StandardHandler;
bOk = g_apIOHandler[g_iNumbIOUnit]->Create(pUnit);

Eventually

g_apIOHandler[i]->Connect();

assures that the handlers are “connected” to RTDB events.

While terminating the application the function DeleteIOH() deletes properly all created handlers using

bOk = g_apIOHandler[i]->`Destroy()`;

On starting the main with I/O handlers integrated the messages displayed are completed by information about the found (and created) I/O handlers.

Note 1: if the application does not find the SWTerm dll-file on your computer you will see the alarm message in the monitor: Cannot find DLL: SWTermDll.dll.

Note 2: you may start any configuration file and use the application to test the project by changing inputs and watching outputs in the monitor (for instance SWMon), exactly as you test the application using SWLab. Of course SWLab allows you to simulate inputs / outputs on its graphic user interface (if you provide in the project the corresponding units: DI8, DO8, NI4 and NO4) and using SWExec you have to access them via monitor but from the testing point of view it is more or less the same.

2.1 Starting application as a pseudo-service

Section titled “2.1 Starting application as a pseudo-service”

The program may be called with up to 4 parameters.

  • -cName: full name (with path) of the configuration file. If the parameter is missing the path will be taken from the file.RTDB_Conf.par stored in the exe directory. If the file .RTDB_Conf.par is also missing the application will ask for the path in the console window.
  • -sService: where the Service has to be a value 0 or 1. The value 0 means that the application will run as a normal console program; the value 1 means that the console window will hide after the startup. If the parameter is missing the program does not hide.
  • -tPort: the tcp/ip port number. If the parameter is missing the default value 59091 will be used.
  • -pPassword: a password string. If the parameter is missing the RTDB will be accessed without parameter.

For instance, the program started with the following parameters:
-cC:\...\Conf\all.swd -t59091 -p1234 -s
loads the configuration file C:\...\Conf\all.swd, uses the TCP/IP port 59091, sets the string “1234” as a RTDB password and runs as a visible console application.

Using the parameter Service with value equal 1 allows the application to be started in a service-like mode. To realize it we use a free available windows service XYNTService. When installed the XYNTService is able to control (start / stop) other programs. The activity of the XYNTService is determined by a file XYNTService.ini. The content of the ini-file to start the application swexecstandard.exe with a standard TCP/IP port, without password as a service-like mode is then something like:

[Settings]
ServiceName=Manage_SWExecStandard
CheckProcessSeconds = 30
[Process0]
CommandLine = "full_path"\swexecstandard.exe -s
WorkingDir= "full_path"
PauseStart= 1000
PauseEnd= 1000
UserInterface = Yes
Restart = No
UserName =
Domain =
Password =

The sense and usage of the entries in the ini file can be taken for instance from the description on codeproject.com (archive). For a start notice:

  • To install the service run at the command prompt
    XYNTService -i
  • To un-install the service at the command prompt
    XYNTService -u
  • The ServiceName defines the name of the service as seen in the Services option of Administrative tools, in the example: Manage_SWExecStandard.
  • To start/stop service from a command line run at the command prompt
    net start/stop Manage_SWExecStandard
  • To open service manager from the command line run
    services.msc

Note also that the file .RTDB_Conf.par is in:

  • the directory “exe” if application is started in Visual Studio
  • the directory “Documents and Settings/User” if application is started from the command line
  • the “Working directory” (see service ini file) if application is started as a service.

The output functions used by the application must be declared in the ofu.h file. In the example we use the standard Ofu library which contains

int CalcLimit (CItem* pOwner, int nVO);
int PerformBreak (CItem* pOwner, int nVO);

Actually, the PerformBreak() function has a general character and is intended to be used when a state machine requires a break. The CalcLimit() function is provided for the state machine Pressure used in our demo and tutorials examples. These functions demonstrate the rules which have to be followed by in writing any specific output functions.

The binding to the application is defined in the StateWORKS Studio while specifying the system configuration: the OFUN object defines two properties: the name of the function used and the Unit required by the function.

The output function (e.g. with the name CalcLimit) must have the form

int CalcLimit (CItem* pOwner, int nVO)

where:

  • pOwner is the pointer of the output function owner. It will be provided internally by calling the output function (effectively it is determined by the Unit Name propriety of the OFUN object: see specification of RTDB objects in the StateWORKS Studio).
  • nVO is the value (Virtual Output Name) used by calling the function during execution.

While programming we are not involved in calling the output function: we have only to provide the function in an Ofun library, the rest is done automatically if the system configuration specifies correctly the above mentioned OFUN object properties.

On programming of the output function we gain access to the required RTDB objects acquiring their pointers using the GetAssItem() method, like in the example

C_PAR* pPar = (C_PAR*)(pOwner->GetAssItem(LIMB_Par_Value));

With those pointers we have access to objects methods needed by programming the output function.

On terminating the output function should return a value that represents the result of calculations and may be used as a control value (Input Name) by a state machine specification.

Programming an I/O handler depends on the hardware drivers and may have very different forms. But there are a few common points. First of all the handler class has to define all methods used by the main for creating and deleting the handler, namely: Create(), Destroy() and Connect(). In addition, the handler class has to implement the SetOutput() methods used by the RTDB to set output values and the ChangedOn() method triggered by RTDB object changes.

The Standard handler is written for a SWTerm dll that does not generate events. Hence, the dll has to be polled and the solution of the Standard handler uses a Thread for that purpose. The Thread processes all accesses to the SWTerm dll; polling the inputs and setting the outputs. To avoid collisions of outputs coming from the RTDB which calls SetOutput() and ChangedOn() methods they are serialized in a Queue which is guarded by Critical Section; the Queue is then served in the Thread.

The Thread contains a while loop which checks periodically the content of the Queue. If the Queue is not empty the due output message is taken from the Queue and sent to the SWTerm dll using the PerformOutput() method. Otherwise, polling of SWTerm inputs is performed using the PerformPollStep() method.

The SWTerm functions are declared in the standarddll.h file which corresponds to the file SWTermDll.h In fact any dll that uses the file standarddll.h can be accessed by the Standard handler (see the project SWTerm). The name of the dll can be specified in StateWORKS Studio by the Unit specification.

The Create() method gets and stores pointers to all RTDB objects used in the handler functions. In situations where the number of inputs and outputs may vary other solutions might be better. Other functions performed in the Create() method are implied by the dll used:

  • The dll is opened.
  • The Queue receiver is created and Queue sender is connected to it.
  • The Critical Section is initialized.
  • The polling time for the Thread is defined.

The connecting functions for the output objects of type CMD, DO and NO in the Connect() method assure that changes of those objects values call the OnChanged() method. In addition, a polling Thread is created in the Connect() method.

The Destroy() method terminates gently the polling Thread and closes dll.

4.5 The ChangedOn() and SetOutput() methods

Section titled “4.5 The ChangedOn() and SetOutput() methods”

The ChangedOn() is being triggered by changes of RTDB object values. The SetOutput() method is called while executing the virtual outputs. Both methods pack the received output values into a message and put the message into the Queue.