IEC 61850 server tutorial

This tutorial introduces in writing IEC 61850/MMS server applications with libIEC61850. It will start with a very simple example and will step by step introduce the features of the server API.

Example 1 – A simple server example

This example will show you how to create the most simple IEC 61850 server application. The complete source-code of this example can be found in the examples folder of the source-code distribution.

IedServer iedServer = IedServer_create(&iedModel);

IedServer_start(iedServer);

while (running) {
    Thread_sleep(1);
}

IedServer_stop(iedServer);

IedServer_destroy(iedServer);

The first code line creates a new IedServer object. This object is responsible for managing the complete MMS protocol stack and the IEC 61850 data model. The parameter contains the IEC model description that was generated by the model generator tool. The model is contained in the generated c source file static_model.c. There are also other supported ways to create the data model: You can use the integrated configuration file parser to read the data model from a file or you can construct the data model dynamically by using API calls.

The libIEC61850 user API is designed in an object-oriented fashion. IedServer and most of the other API data types can be seen as classes that are known from other programming languages like C++ or Java. The methods to operate on the data types all start with the name of the data type followed by an underscore and the methods name (e.g. IedServer_start). The first parameter is always the reference to the data type instance to operate on.

Line 3 starts the protocol stack. After invoking the IedServer_start function the server starts listening for client connections. Therefore a new thread will be started. For every new incoming client connection a dedicated thread is started to handle this connection.

Lines 5 until 7 is the main loop of the application. Here you can feed process values to the MMS server or react on client activities.

Line 9 stops the MMS server. A call to the IedServer_stop function also closes all client connections.

Line 11 does the cleanup. It frees all resources allocated by the IedServer instance.

Example 2 – Feeding process values

The following example builds upon the previous one. It will show you how to provide process values of your application to the MMS server. Values of MMS variables are represented by MmsValue objects in libIEC61850. MmsValue instances can hold values of all MMS data types the MMS server provides.

Values that are provided when a client requests them are handled internally by the IedServer instance. Most of them are stored in a data structure I call the MMS server cache. You can get access to this values by the getValue-method of IedServer. MmsValue objects can be complex tree structures. In this case the nodes of the tree are of the types MMS_STRUCTURE or MMS_ARRAY and the leaves are of one of the basic types (like MMS_INTEGER or MMS_VISIBLE_STRING).

	float power = 500.f;

	MmsValue* powerValue = MmsValue_newFloat(power);
	MmsValue* powerTimestamp = MmsValue_newUtcTime(time(NULL));


	while (running) {
		MmsValue_setUtcTime(powerTimestamp, time(NULL));

		IedServer_lockDataModel(iedServer);

		IedServer_updateAttributeValue(iedServer, 
                         IEDMODEL_Inverter_MMXU1_TotW_mag_f, powerValue);
		IedServer_updateAttributeValue(iedServer, 
                         IEDMODEL_Inverter_MMXU1_TotW_t, powerTimestamp);

		IedServer_unlockDataModel(iedServer);

		power += 0.1f;

		MmsValue_setFloat(powerValue, power);

		Thread_sleep(500);
	}

Note: Next to the generic function IedServer_updateAttributeValue that can handle all IEC 61850 data types and takes an MmsValue object as an argument you can also use convenience functions like IedServer_updateFloatAttributeValue or IedServer_updateBooleanAttributeValue that use native types as arguments and thereby avoid handling with MmsValue objects at the user side.

Example 3 – Control model

You can make use of the control model in your server applications. The IEC 61850 control model provides a way to control IEDs. E.g. if the IED works as a circuit breaker with a controllable switch you can use a control command to open or close the switch. As a server application you receive such control commands from the client. This works for all data objects that are of a controllable CDC (Common Data Class). Examples of such CDCs are SPC, DPC and APC.

In libIEC61850 you can react on such commands by setting a control handler for a model node that represents a controllable data object. The control handler receives the control value from the server stack together with a parameter that is given by the application when setting the control handler. This parameter can be used by the control handler to distinguish between the controllable data objects present in the data model. The control handler returns true if the control operation has been successful or false to indicate that the control operation failed. Depending on the control model this information is used by the stack to return a negative or positive response to the client. The actual control operation must only be performed if not in test mode and the test parameter is false.

ControlHandlerResult
controlHandler(void* parameter, MmsValue* value, bool test)
{
    if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) {

        if (test == false)
            performAction();

        IedServer_updateAttributeValue(iedServer, 
            IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);

        return CONTROL_RESULT_OK;
    }

    return CONTROL_RESULT_FAILED;
}

int main(int argc, char** argv) {

	iedServer = IedServer_create(&iedModel);

	IedServer_start(iedServer, 102);

	IedServer_setControlHandler(iedServer, 
                IEDMODEL_GenericIO_GGIO1_SPCSO1, 
                (ControlHandler) controlHandler,
                IEDMODEL_GenericIO_GGIO1_SPCSO1);

	if (!IedServer_isRunning(iedServer)) {
		printf("Starting server failed! Exit.\n");
		IedServer_destroy(iedServer);
		exit(-1);
	}

	running = 1;

	signal(SIGINT, sigint_handler);

	while (running) {
		Thread_sleep(1);
	}

	IedServer_stop(iedServer);

	IedServer_destroy(iedServer);
} 

Example 4 – Client authentication

libIEC61850 provides support for basic client authentication. The only supported method now is password authentication. To use authentication you need to dig a bit deeper into the MMS stack. MMS uses the ACSE (Association Control Service Element) of the ISO protocol stack to initiate a new client session. ACSE is also responsible for client authentication.

In libIEC61850 the complete ISO protocol stack up to the MMS layer is handled internally by an IsoServer object. To feed the ACSE layer with the required authentication parameters you first need access to the IsoServer object that is associated to the IedServer instance. This has to be done with the getIsoServer-method of IedServer. Then you can call the setAuthenticationParameter-method of IsoServer to provide the authentication information. The authentication information is provided by an AcseAuthenticationParameter data structure.

IedServer iedServer = IedServer_create(&staticIedModel);

/* Activate authentication */
AcseAuthenticationParameter auth =
    calloc(1, sizeof(struct sAcseAuthenticationParameter));
auth->mechanism = AUTH_PASSWORD;
auth->value.password.string = "testpw";

IsoServer isoServer = IedServer_getIsoServer(iedServer);
IsoServer_setAuthenticationParameter(isoServer, auth);

IedServer_start(iedServer);

In line 1 of the source-code example an instance of IedServer is created as usual.

Lines 4 to 7 allocates memory for the AcseAuthenticationParameter data structure and initializes this structure with the authentication method and the authentication value which is a string that is used as the password.

In line 9 and 10 you get access to the IsoServer instance and feed ít with the authentication parameters. This has to be done before calling the start-method of IedServer.

Create a data set

There is not yet an API function to create data sets by the application code. You can create statically configured data sets by adding a data set description to a Logical Node description in the ICD file. Also clients that connect to the server can create domain specific and association specific data set by using the corresponding MMS services.

Example 5 – Installing write access handlers for data attributes

If the server application should be informed when a client writes a variable (e.g. a configuration value) a WriteAccessHandler has to be used. The WriteAccessHandler can also be used to implement access control. It is a callback function that is called after the server received a write request from a client but before sending the response. You can register the same callback function multiple times for different data attributes and distinguish the changed data attribute by checking the attributes reference. An example can be found in server_example5.

First it is required to define the callback function:

static bool
writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection)
{
    if (dataAttribute == IEDMODEL_Inverter_ZINV1_OutVarSet_setMag_f) {
        printf("New value for OutVarSet_setMag_f = %f\n", 
            MmsValue_toFloat(value));
        return true;
    }

    return false;
}

The handler returns true if the value is correct and accepted and the server will answer with a positive response to the client. Otherwise a negative response will be send. Based on the ClientConnection object that is provided the user can also implement an access control system. If the handler

This callback function can be registered at an IedServer instance together with the data attribute handle of the data attribute to handle.

IedServer_handleWriteAccess(iedServer, IEDMODEL_Inverter_ZINV1_OutVarSet_setMag_f,
   writeAccessHandler);

Changing the IED name

In order to change the IED name at runtime of your server application you can use the function

IedModel_setIedName(&iedModel, "MYIEDNAME");

when using a static model.

When you are using a dynamic data model you should use the function

IedModel_setIedNameForDynamicModel(iedModel, "MYIEDNAME");

NOTE: It is essential to call these functions BEFORE the IedServer_create function is called!

Using the log service

In order to use the log service it is required to define a log and a LCB (Log Control Block) in the ICD file or dynamically create it with the data model API.

In addition to this a LogStorage instance is required to store the actual log data. A log storage can be implemented by the user. If you don’t want to implement your own LogStorage you can use the LogStorage implementation based on the SQLite database that is provided with the libiec61850 source code distribution. You can also find a complete code example there.

IedServer iedServer = IedServer_create(&iedModel);

LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db");
LogStorage_setMaxLogEntries(statusLog, 10);

IedServer_setLogStorage(iedServer, "GenericIO/LLN0$EventLog", statusLog);

The above code creates a LogStorage instance and connects it to the “EventLog” log with the IedServer_setLogStorage function. All events for the “EventLog” log are stored in the sqlite database.
With the LogStorage_setMaxLogEntries the capacity of the log can be restricted to the specified number of entries.