Multiple instances of the same machine

Sometimes you want to run multiple instances of the same state machine (e.g. processing tree serial interfaces with the same state machine). In object orient languages this is easy as the concept of objects is the basis of these languages.

Luckily in C there there is also a well known method to do this – providing a pointer to a structure that contains all private data of the function – in our case the state handler function. The code generator automatically creates such a structure for you.

In this example we will show you how to “create multiple instances” of the state machine. And then we show you how to create an own structure where additional private data can be defined.

Let's start …

Create Multiple Instances

Follow these simple steps to prepare the generated code for multiple instances. First set some parameters in the configuration file:

Set parameter HsmFunctionWithInstanceParameters to YES

Set parameter HsmFunctionWithEventParameter to YES

As a result the generated state machine handler signature looks like this:

void testcase(TESTCASE_INSTANCEDATA_T* userInstanceVar, TESTCASE_EVENT_T msg)

And you can now simply declare multiple variables of type TESTCASE_INSTANCEDATA_T which serve as object data store. When calling the state machine just point to one of these variables. To let the state machine know which object number it is there is a predefined member of the struct called inst_id. You can set it to any value that makes sense in the state handler (e.g. provide the COM port number the machine currently processes).

TESTCASE_INSTANCEDATA_T instDataA = TESTCASE_INSTANCEDATA_INIT; TESTCASE_INSTANCEDATA_T instDataB = TESTCASE_INSTANCEDATA_INIT; TESTCASE_INSTANCEDATA_T instDataC = TESTCASE_INSTANCEDATA_INIT; // Set object ID if the machine needs to know which object it is // E.g. which serial port to open ... instDataA.inst_id=0; instDataB.inst_id=1; instDataC.inst_id=2; // call "object A" testcase(&instDataA, msg); // call "object B" testcase(&instDataB, msg); // call "object C" testcase(&instDataC, msg);

I think you agree that this is no magic and easy to understand.

Provide own Instance Struct

Sometimes it makes sense to provide an own instance structure. In our example we want to store the baud rate, parity and other values relevant for each serial port. So the state machine can store there all local data and makes it “object oriented” this way.

So first define an own struct in a file called own_inst_type.h (as an example). As you can see all local data like the noRxBytes is defined there but also variables that are initialized just once like the bit rate which is more like a parameter.

#ifndef __OWN_INST_TYPE__ #define __OWN_INST_TYPE__ typedef struct InstanceData MY_TESTCASE_INSTANCEDATA_T; #include <stdint.h> #include <testcase_ext.h> #include "testcase.h" struct InstanceData{ uint16_t baudrate; uint8_t noBits; uint8_t parity; uint8_t stopBit; TESTCASE_INSTANCEDATA_T instanceVar; }; #endif // __OWN_INST_TYPE__

Then tell the code generator to use this struct. Add the third line to the configuration file which then looks like this:

HsmFunctionWithInstanceParameters=YES
HsmFunctionWithEventParameter=YES
HsmFunctionUserDefinedParameter=MY_TESTCASE_INSTANCEDATA_T

The generated state machine signature now looks as follows. The difference is that the own instance structure is now used instead of the generic one.

void testcase(MY_TESTCASE_INSTANCEDATA_T* userInstanceVar, TESTCASE_EVENT_T msg)

To use the newly defined instance type declare the instance variables now as follows:

// init three different serial ports MY_TESTCASE_INSTANCEDATA_T instDataA = {0,9600,8,'N',1,TESTCASE_INSTANCEDATA_INIT}; MY_TESTCASE_INSTANCEDATA_T instDataB = {0,19200,8,'E',1,TESTCASE_INSTANCEDATA_INIT}; MY_TESTCASE_INSTANCEDATA_T instDataC = {0,9600,8,'N',1,TESTCASE_INSTANCEDATA_INIT}; // Set object ID if the machine needs to know which object it is // E.g. which serial port to open ... instDataA.instanceVar.inst_id=0; instDataB.instanceVar.inst_id=1; instDataC.instanceVar.inst_id=2; // call "object A" testcase(&instDataA, msg);

Now the state machine is ready to use the new private instance data inside the machine code.

Putting all Things Together

Coming back now to the example of three serial interfaces. The following figure shows a simplified state machine for handling a serial interface. The state machine is generic. If com0 or com1 or com2 is processed is determined from the content of the instance variable.

Hope that the concepts how to run multiple state machines of the same type is clear. And also how to define an own instance structure that is necessary for more complex machines.

In case of any questions don't hesitate to contact us!

{(rater>id=3|name=How do you like this article?|type=rate)}