Building RTDB Based Application
Building an RTDB-Based Application
Table of Contents
Section titled “Table of Contents”- 1. Introduction
- 1.1 The RTDB library
- 1.2 Disk-based system
- 1.3 Embedded system
- 1.4 Example
- 1.5 SWExecStandard as the application
- 2. Main
- 3. Ofun
- 4. Ioh
1. Introduction
Section titled “1. Introduction”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 that have to be followed, but it is not final code for an RTDB application.
1.1 The RTDB library
Section titled “1.1 The RTDB library”The heart of a StateWORKS application is the RTDB library, which is available in several forms depending on the operating system (Windows, Linux, etc.), and the source of the specification files (hard disk, flash memory). The RTDB library is a kind of database, storing and processing objects defined by the system specification. In addition, it contains a VFSM Executor that executes the state machines defined while specifying the system behavior.
1.2 Disk-based system
Section titled “1.2 Disk-based system”While developing a disk-based system, the C++ project requires a path to the /Conf folder of the project specified with StateWORKS Studio. The C++ project uses h-files. Other files (*.swd, *.iod, and *.str) are then read when starting the application. All files used by a StateWORKS application are generated by Visual Studio.
1.3 Embedded system
Section titled “1.3 Embedded system”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.
1.4 Example
Section titled “1.4 Example”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 via hardware-specific I/O handlers. For this, an additional user-written I/O library is required.
1.5 SWExecStandard as the application
Section titled “1.5 SWExecStandard as the application”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 the 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.
2. Main
Section titled “2. Main”The main module has to realize some basic functions required when starting the application, such as:
- 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 startup 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 and environment and is not the topic of this document. We have to mention only that, as a rule, the main module does not contain the application user interface allowing access to 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 this global variable:
CVfsmSystem g_VfsmSystemThe 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 database 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, stVFSMTypePath defines the directory containing the *.swd, *.iod, and *.str files, and 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 TCP/IP 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 of 59091. In the example, we use the method StartTCPIPCommunication for that purpose. The method requires two parameters:
if ( g_VfsmSystem.StartTCPIPCommunication( lPortNumber, stPassword ) )The first one is the required port number and the other one is a password. If we specify a password, clients trying to connect to the RTDB must use that password. When programming the main, we may use other methods defined in vswin.h. For instance, in the example, we have used the following method to determine the number of RTDB objects for displaying it at startup:
GetTotalItemNumb()Similarly, we use this method to terminate the application properly:
RemoveAll()To have a chance to terminate, the program checks keyboard entries in a while loop at the end of the main.
To get the RTDB working, we have to supply it with a constant clock tick, which we generate in a separate RunThread() by calling the TimeBaseTick() method:
g_VfsmSystem.TimeBaseTick();Note that the frequency of that call determines several program 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 high clock accuracy.
Thus, we now have all components required for a simple RTDB-based application. Note that when linking, we have to add an Ofun library. The installation package contains a standard Ofun library that can be used for that purpose. Later, we may replace it with our own Ofun library containing output functions required by our application. When starting the application as programmed in the RTDBApp.cpp file, we get the following messages on the monitor:
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.swdApplication created with 28 objectsRTDB Server (port 59091) startedTimeBaseTick thread createdCreated standardunit Unit1Exit program by pressing the key 'q' and Enter.Assuming that we have entered the configuration file path .../Conf/Standard.swd and acknowledged the next two questions by pressing “Enter” to accept the default TCP/IP port number and start the application without a password, we can connect StateWORKS monitors to the application and 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 I/O 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. When programming the I/O handler (see the corresponding section below), we replace the virtual methods as needed. 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 StateWORKS Studio. The Unit contains declarations of RTDB objects that 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);This returns a pointer to the handler, which provides access to methods declared in vsiou.h or standardio.h.
Depending on the Unit name, a handler is created, for instance, of type CIO_StandardHandler:
stType = pUnit->GetUnitTypeName();
g_apIOHandler[g_iNumbIOUnit] = new CIO_StandardHandler;bOk = g_apIOHandler[g_iNumbIOUnit]->Create(pUnit);Eventually, the following assures that the handlers are “connected” to RTDB events:
g_apIOHandler[i]->Connect();While terminating the application, the function DeleteIOH() properly deletes 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.
2.1 Starting the application as a pseudo-service
Section titled “2.1 Starting the 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.parstored in the exe directory. If the file.RTDB_Conf.paris also missing, the application will ask for the path in the console window.-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 a password.-sSERVICE: where Service has to be a value of 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 be hidden after startup. If the parameter is missing, the program does not hide the console window.
For instance, the program started with the following parameters:
-c.../Conf/all.swd -t59091 -p1234 -sloads the configuration file .../Conf/all.swd, uses TCP/IP port 59091, sets the string 1234 as an RTDB password, and runs as a visible console application.
Using the Service parameter with a value equal to 1 allows the application to be started in a service-like mode. To realize this, we use a freely available Windows service called XYNTService. When installed, XYNTService is able to control (start/stop) other programs. The activity of XYNTService is determined by a file called XYNTService.ini. The content of the ini-file used to start the application swexecstandard.exe with a standard TCP/IP port, without a password, in service-like mode is something like:
[Settings]ServiceName=Manage_SWExecStandardCheckProcessSeconds = 30[Process0]CommandLine = "full_path"\swexecstandard.exe -sWorkingDir= "full_path"PauseStart= 1000PauseEnd= 1000UserInterface = YesRestart = NoUserName =Domain =Password =The meaning and usage of the entries in the ini-file can be found, for instance, in the description on codeproject.com ↗ (archive). For a start, note:
- To install the service, run at the command prompt:
XYNTService -i
- To uninstall the service, run at the command prompt:
XYNTService -u
ServiceNamedefines the name of the service as seen in the Services option of Administrative Tools, in the example:Manage_SWExecStandard.- To start/stop the 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 located in:
- the “exe” directory if the application is started in Visual Studio
- the “Documents and Settings/User” directory if the application is started from the command line
- the “Working directory” (see the service ini-file) if the application is started as a service
3. Ofun
Section titled “3. Ofun”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 tutorial examples. These functions demonstrate the rules that have to be followed when writing any specific output functions.
The binding to the application is defined in 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 following form:
int CalcLimit (CItem* pOwner, int nVO)pOwneris the pointer to the output function owner. It will be provided internally when calling the output function (effectively, it is determined by the Unit Name property of theOFUNobject; see the specification of RTDB objects in StateWORKS Studio).nVOis the value (Virtual Output Name) used when calling the function during execution.
While programming, we are not involved in calling the output function: we only have to provide the function in an Ofun library; the rest is done automatically if the system configuration correctly specifies the above-mentioned OFUN object properties.
When programming the output function, we gain access to the required RTDB objects by acquiring their pointers using the GetAssItem() method, as in the example:
C_PAR* pPar = (C_PAR*)(pOwner->GetAssItem(LIMB_Par_Value));With those pointers, we have access to the object methods needed when programming the output function.
When terminating, the output function should return a value that represents the result of the calculations and may be used as a control value (Input Name) by a state machine specification.
4. Ioh
Section titled “4. Ioh”Programming an I/O handler depends on the hardware drivers and may have very different forms. However, 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.
4.1 The Polling Thread and Its Methods
Section titled “4.1 The Polling Thread and Its Methods”The Standard handler is written for a SWTerm dll that does not generate events. Hence, the dll has to be polled, and 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 the SetOutput() and ChangedOn() methods, they are serialized in a Queue that is guarded by a Critical Section; the Queue is then served by the Thread.
The Thread contains a while loop that periodically checks the content of the Queue. If the Queue is not empty, the pending 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 SWTerm project). The name of the dll can be specified in StateWORKS Studio by the Unit specification.
4.2 The Create() Method
Section titled “4.2 The Create() Method”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 the Queue sender is connected to it.
- The Critical Section is initialized.
- The polling time for the Thread is defined.
4.3 The Connect() Method
Section titled “4.3 The Connect() Method”The connecting functions for the output objects of type CMD, DO, and NO in the Connect() method ensure that changes to those objects values call the OnChanged() method. In addition, a polling Thread is created in the Connect() method.
4.4 The Destroy() Method
Section titled “4.4 The Destroy() Method”The Destroy() method gently terminates the polling Thread and closes the dll.
4.5 The ChangedOn() and SetOutput() Methods
Section titled “4.5 The ChangedOn() and SetOutput() Methods”The ChangedOn() method is triggered by changes in RTDB object values. The SetOutput() method is called while executing virtual outputs. Both methods pack the received output values into a message and put the message into the Queue.