IEC 61850 client tutorial

The IEC 61850 client API contains functions to support model discovery, reading and writing data attributes, data set handling, configuration and reception of reports, file services and control operations. This client API is designed to be as close as possible to the IEC 61850 ACSI (abstract communication service interface) as defined in IEC 61850-7-2. But be aware that there are some points where there are some deviations from the ACSI due to practical reasons.

Create a connection to a server

Before you can connect to a server you have to create an IedConnection object. A connection will be established with the IedConnection_connect method. The first argument is the newly created IedConnection instance. The second argument provides a pointer to a variable where the client stack can store error informations. This is also the case with most other client API functions (IedConnection_…) where errors can occur. The third argument is a string containing the IP address or hostname of the server to connect. The last argument provides the TCP port of the server. The default TCP port of MMS is 102. So you should always select this port.

Practical examples on how to use the IEC 61850 client API can be found in the examples folder in the libiec61850 source distributions. These examples start with “iec61850_client”.

IedClientError error;

IedConnection con = IedConnection_create();

IedConnection_connect(con, &error, "192.168.1.2", 102);

if (error == IED_ERROR_OK) {
  // do some work

  IedConnection_close(con);
}

IedConnection_destroy(con);
...

The code snippet above will connect to a server with IP address 192.168.1.2 listening on port 102 (the default MMS port). If you finished your task you have to call IedConnection_close to properly close the connection to the server. After you closed the connection you should also call IedConnection_destroy to release all resources allocated by the client stack.

Control connection parameters

The IEC 61850/MMS protocol is a complex protocol consisting of a lot of OSI protocol layers. Most of them have their own parameters. Fortunately in most cases the default values of these parameters are sufficient to connect to a device. Some devices though have problems with default parameters and require a special treatment.

The IsoConnectionParameters object, that is associated with a MmsConnection can be used to set the lower layer parameters (below the MMS layer). It can also be used to set authentication values (e.g. a password) if the server requires authentication.

For more details on how to change these parameters please have a look at the examples and the API reference documentation.

Data model discovery

IEC 61850 provides a wide range of functions to discover the data model present at the server. These functions are especially interesting if you want to write a generic client. If you know the data model of the device you want to communicate to you don’t need these functions.

To request the logical device list from the server you can call the IedConnection_getLogicalDeviceList method.

LinkedList deviceList = IedConnection_getLogicalDeviceList(con, &error);

The resulting linked list contains the names of all logical devices of the server as a list of C strings.

The directory of a logical device can be retrieved by calling IedConnection_getLogicalDeviceDirectory with the name of the logical device as a parameter.

The following example iterates the list of logical devices and requests the logical node list for each logical device:

LinkedList device = LinkedList_getNext(deviceList);

while (device != NULL) {
    printf("LD: %s\n", (char*) device->data);

    LinkedList logicalNodes = IedConnection_getLogicalDeviceDirectory(con, &error,
        (char*) device->data);

    device = LinkedList_getNext(device);
}

Reading and writing data objects

You can read or write simple or complex data attributes/objects with the IedConnection_readObject and IedConnection_writeObject funtions. Before you can use these functions you have to establish a connection to a server as explained above. The parameter lists of both functions are similar. The first argument is the connection object of an established connection. The second argument is a pointer to an IedClientError variable. The third argument is the object reference of the data attribute/object you want to access. The fourth argument is the Functional Constraint (e.g. MX).

Note: You cannot access whole IEC 61850 data objects with these functions. For example a call to IedConnection_readObject will result in a single MMS read command. This is restricted to functional constraint data. So in order to read all values of an IEC 61850 data object you have to use multiple readObject calls for each functional constraint the data object has elements of. This seems as a restriction at the first but turns out to be very useful. E.g. you can read all measurement or status values (functional constraints MX or ST) of an IEC 61850 data object without reading configuration values or control variables (functional constraints CF or CO).

IedClientError error;

...

/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, 
     "simpleIOGenericIO/GGIO1.AnIn1.mag.f", MX);

if (value != NULL) {
    float fval = MmsValue_toFloat(value);
    printf("read float value: %f\n", fval);
    MmsValue_delete(value);
}

/* write a variable to the server */
value = MmsValue_newVisibleString("libiec61850.com");

IedConnection_writeObject(con, &error, 
   "simpleIOGenericIO/GGIO1.NamPlt.vendor", DC, value);

if (error != IED_ERROR_OK)
    printf("failed to write simpleIOGenericIO/GGIO1.NamPlt.vendor!\n");

The IedConnection_readObject and IedConnection_writeObject functions use instances of MmsValue as results or parameters. The benefit of these functions is that they are very flexible and can also be used to access complex (structured) data. Also the type of the result has not to be known in advance when using the readObject function. One other consequence is that for the read function the API user always have to free (by using MmsValue_delete) the data after processing. Also by using the writeObject functions you have to deal with MmsValue and its special setter functions. To avoid this the client API also contains some convenience functions that allow read and write with native data types as parameters. These functions are restricted to access simple (basic) data attributes. Also the type of the result has to be known in the read case.

An example for these functions is the IedConnection_readFloatValue function:

float magF = IedConnection_readFloatValue(con, &error, 
   "simpleIOGenericIO/GGIO1.AnIn1.mag.f", MX);

Handling data sets

Data sets are groups of data attributes (DA) or functional constraint data objects (FCDO). They are used to simplify access to functional related groups of variables. E.g. if you want to read the most important status values of a server you don’t have to ask the server for each individual variable. Instead you define a data set (or most probably use a predefined one) that contains all the required data and request them with a single read with the data set reference as an argument. Data sets are also intended to be used in relation with reports, GOOSE messages and logs (time series of data).

So far the IEC 61850 client API supports the following data set related services:

  • read data set values
  • define a new data set
  • delete an existing data set
  • read the directory (list of variables) of the data set

To represent a data set client side the library uses objects of type ClientDataSet. ClientDataSet can be considered as a container to store the values of a data set and consists of these functions:

void
ClientDataSet_destroy(ClientDataSet self);

MmsValue*
ClientDataSet_getValues(ClientDataSet self);

char*
ClientDataSet_getReference(ClientDataSet self);

int
ClientDataSet_getDataSetSize(ClientDataSet self);

The actual values of the data set are stored as MmsValue instance of type MMS_ARRAY. There is an array element for each member of the data set.

To access the data set related services these functions have to be used:

ClientDataSet
IedConnection_readDataSetValues(IedConnection self, IedClientError* error,
		char* dataSetReference, ClientDataSet dataSet);

void
IedConnection_createDataSet(IedConnection self, IedClientError* error,
		char* dataSetReference, LinkedList /* char* */ dataSetElements);

void
IedConnection_deleteDataSet(IedConnection self, IedClientError* error,
		char* dataSetReference);

LinkedList /* <char*> */
IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error,
		char* dataSetReference, bool* isDeletable);

The IedConnection_readDataSetValues creates a new instance of ClientDataSet if the last parameter (dataSet) is NULL. A call to this function triggers a request to the server. If you call the function multiple times you can provide the formerly received ClientDataSet instance as dataSet parameter. In this way you can reuse the container.

This example shows how to read the values of a data set:

ClientDataSet dataSet = IedConnection_readDataSetValues(con, &error, 
    "simpleIOGenericIO/LLN0.Events", NULL);

if (error == IED_ERROR_OK) {
   printf("Read data set %s\n", ClientDataSet_getReference(dataSet));

   IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", dataSet);
}

To create a new data set you have to build a list containing the object references to the functional constraint data that should become the data set members. Please note that the FC has to be present in squared brackets as shown below:

LinkedList dataSetItems = LinkedList_create();

LinkedList_add(dataSetItems, "simpleIOGenericIO/GGIO1.SPCSO1.stVal[ST]");
LinkedList_add(dataSetItems, "simpleIOGenericIO/GGIO1.SPCSO1.t[ST]");
LinkedList_add(dataSetItems, "simpleIOGenericIO/GGIO1.SPCSO2.stVal[ST]");
LinkedList_add(dataSetItems, "simpleIOGenericIO/GGIO1.SPCSO2.t[ST]");

IedConnection_createDataSet(con, &error, "simpleIOGenericIO/LLN0.newDataSet", dataSetItems);

This example will create a new data set with the name simpleIOGenericIO/LLN0.newDataSet containing four different elements.

Receiving reports

Reports are used for event based message transmission. If you need to be updated with the states of certain variables reporting provides the means without forcing you to periodically send read requests to the server. This preserves network bandwidth especially in cases where values only change sporadically.

Reports are defined for data sets. A server typically contains preconfigured Report Control Blocks(RCB). A client has to reserve, configure and activate such a RCB prior to receiving report messages from the server.

To handle reports client side these data types are involved:

  • ClientReportControlBlock – is the data container to hold the RCB values client side
  • ClientReport – represents a received report
  • ClientDataSet – container for the data values of a received report
  • ReportCallbackFunction – callback function when a report is received
  • ReasonForInclusion – enumeration to indicate the reason for the inclusion of a data set member into the report

To get started with reports you need to configure and enable a RCB at the server. You can start by reading the values of the RCB with the IedConnection_getRCBValues function:

getRCBValues

You have to specify the RCB by a special reference as the third argument of the function. Please not the “RP” inserted between the LN name and the RCB name. This has to be used for unbuffered reports. If you want to enable a buffered report you have to use “BR” instead. The function reads all values of the RCB from the server and creates a new instance of ClientReportControlBlock. If you want to use an existing instance of ClientReportControlBlock you can specify the instance as the last argument of the function.

If you want to write to the RCB you can also create a new instance with the ClientReportControlBlock_create function. Set the values with the setter functions and use the IedConnection_setRCBValues function to write the changed or new values to the server:

setRCBValues

Please note that in contrast to the IedConnection_getRCBValues function that always reads all RCB values the setRCBValues only write the requested values. These values need to be specified by using the fourth argument of the function.

Prepare the client to receive reports:

In order to receive reports and access the report data you have to provide a callback function to the client API. A very simple callback function and the other required preparation steps can look like this:

static void
reportHandler (void* parameter, ClientReport report)
{
	printf("report received\n");
}

ClientDataSet dataSet = IedConnection_readDataSetValues(con, 
     &error, dataSetReference, NULL);

IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB", 
     reportHandler, NULL, dataSet);

First it is required to get a ClientDataSet instance that can be used by the report receiver part of the client stack to store the report data. The second step is to connect the report callback function and the data set representation to the RCB. This is done by calling the IedConnection_installReportHandler

Then reporting can be enabled by using the IedConnection_setRCBValues function:

ClientReportControlBlock_setRptEna(rcb, true);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA, true);

If you no longer want to receive reports from a RCB you can use the IedConnection_setRCBValues function with rptEna set to false or the IedConnection_disableReporting function.

Controlling a device

Please have a look at the separate control tutorial.

Using file services

TBD

Access to the underlying MMS API

If you need to perform some advanced tasks that are not supported by the IEC 61850 client API you can also get access to the underlying MMS API. Be aware that by using this API it is not guaranteed that the resulting behavior is compliant with the IEC 61850 specification. It is in the responsibility of the application programmer to ensure that the resulting program is compliant with the specification if this is required. You can get the underlying MmsConnection object by calling the IedConnetion_getMmsConnection function.

Using authentication

Currently the client stack only supports basic password authentication. Work on certificate based authentication is in progress.

To use authentication it is required to access the MmsConnection object:

clientSideAuthentication

83 thoughts on “IEC 61850 client tutorial

  1. bikash

    Hi,

    I want to use authentication in the application which server example shall i run to create a server which requires validation frm the client, i tried with mms_client_example4, its not able to crete the server please help.

    Thanks
    Bikash

    1. Michael Zillgith Post author

      The server_example4 uses password authentication. If you want to use some of the example clients (like iec61850_client_example5) you have to change the password to match the server.

  2. bikash

    Hi,

    Please help on this, I am connecting with the actual device P14NB model, the client example is not able to connect gives an error ” Connection rejected by server”.

    Thanks,
    Bikash

  3. bikash

    Hi,
    while establishing connection with the IED getting error in method static CotpIndication
    parseCotpMessage(CotpConnection* self), here tpduType returning as 128.

    Thanks in advance,
    Bikash

      1. Michael Zillgith Post author

        Hi Bikash, Hi Jens,
        can you please send me a wireshark capture file (to the email at the contact page) of the connection attempt? I guess it is some problem with the connection parameters (maybe T selector).

  4. Mladen

    Hello, I have trouble using the reporting with the C# API and ABB relay protections. I have followed the Reporting exmple and my code looks like this:

    string rc_ref = "AA1J1Q11LD0/LLN0.BR.rcb_StatUrg"; //Report is Buffered
    ReportControlBlock rcb = connection.GetReportControlBlock(rc_ref);
    rcb.GetRCBValues();

    The GetRCBValues(); function throws exception with code 99 after calling

    IedConnection_getRCBValues (connection, out error, objectReference, self);

    The RCB path in the protection is AA1J1Q11LD0/LLN0.rcb_StatUrg.
    I have tried calling GetBufTm(); GetIntgPd(); GetTrgOps(); GetDataSetReference(); they all return 0 or Null.

      1. Chris

        Hey Mladen,

        I’m also working ABB protection equipment and I’m running into the same error you described, except I’m using the C api. How did you solve your error? My erroneous line looks like:

        ClientReportControlBlock rcb = IedConnection_getRCBValues(con, &error, “REL_90LD0/LLN0.RP.rcbMeasFlt”, NULL); // Unbuffered report

        I assume it is the reference string that is the issue but, if not, maybe how you solved it in C# could help me also.

        I’ve tried several variations of this reference string, with combinations of ‘$’, ‘/’, and ‘.’ as separators, but nothing seems to work. The best I’ve gotten is no error returned but rcb was returned as NULL, breaking any following code.

        Thanks,
        Chris

        1. Michael Zillgith Post author

          Hi Chris,

          your reference string seems to be correct for me. Can you send me a wireshark capture file (e.g. by email) so I can see what the server answers? Then it should be easy to determine the problem.

          Regards,
          Michael

  5. Manoj

    Hi,
    Please help me i stucked one error like Failed to create data set from the function in Client API C#.
    IedConnection_createDataSet(connection, out error, dataSetReference, linkedList);
    out error is giving= 20
    Thanks in advance,
    Manoj

    1. Michael Zillgith Post author

      Some server devices don’t support the CreateDataSet service. Information about that can be found in the PICS document of the server. Anyway the error code is misleading. I will change this in the upcoming version.

      1. Manoj

        HI..
        i unable to load the iec61850 Dll file when my applicaton run.. its not taking the module iec61850 i copied this in sys32 folder…

        Thnks in Advance

  6. Ashwin

    Hi
    Any idea why this is failed?
    I am not able to write value to server using following:
    write fail due to IED_ERROR_ACCESS_DENIED = 21
    /* write a variable to the server */
    value = MmsValue_newVisibleString(“libiec61850.com”);
    IedConnection_writeObject(con, &error,
    “simpleIOGenericIO/GGIO1.NamPlt.vendor”, DC, value);

    if (error != IED_ERROR_OK)
    printf(“failed to write simpleIOGenericIO/GGIO1.NamPlt.vendor!\n”);

    1. Michael Zillgith Post author

      Hi,
      if you are using this code with the server examples of libiec61850 then this is prevented by the default access rights. Per default access to variables with FC=DC and FC=CF is not allowed. To change this you have to add this code to the server:

      IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);

      1. Jonhson

        Hi,
        i have the same problem, but in my case I don’t have an error, everything is getting fine. I can’t write on the server, i tried all methods but nothing. I don’t really know what is happening. I can only read a dataobject, get some informations. but no writing.
        MmsValue* ctVal=MmsValue_newBoolean(true);
        IedConnection_writeObject(con,&error,”APR_5ARS/CSWI1.Pos.ctlVal”,IEC61850_FC_ST,ctVal); IedConnection_writeBooleanValue(con,&error,”APR_5ARS/CSWI1.Pos”,IEC61850_FC_ST,true);
        MmsValue* oper2=IedConnection_readObject(con,&error,”APR_5ARS/CSWI1.Pos.stVal”,IEC61850_FC_ST);
        bool state=MmsValue_getBoolean(oper2);
        if(error == IED_ERROR_OK)
        qDebug()<<"Lecture possible, état : "<<state;
        else
        qDebug()<<"Lecture impossible";

        I put the CtVal true, but the status is always false. I add in the server :
        IedServer_setWriteAccessPolicy(iedServer,IEC61850_FC_ALL,ACCESS_POLICY_ALLOW);

        But nothing is working, so anyone can help me please?

        1. Michael Zillgith Post author

          This is because it is generally not allowed to write to variables with FC=ST. These are read-only.

          1. Jonhson

            Now i tried to use server_example_control, and client_example3 and it’s working but when i use the same code in my project. It’s not working, the server can’t handler the controlhandler or checkhandler. So what can i do ?

            ControlObjectClient control=ControlObjectClient_create(“APR_5ARS/CSWI1.Pos”,con);
            MmsValue* ctlVal=MmsValue_newBoolean(true);
            ControlObjectClient_setOrigin(control,NULL,3);
            if(ControlObjectClient_operate(control,ctlVal,0)){
            qDebug()<<"Operation OK";
            } else{
            qDebug()<<"Operation failled";
            }
            MmsValue_delete(ctlVal);
            ControlObjectClient_destroy(control);

  7. Methane

    Hi,
    Thank you for the interesting lib, all most I had been experienced all the examples and every thing was working fine within windows system. I tried the client example3 with the server example3 and writing value to change the SPCSO1, SPCSO3 and it was working fine, Please any idea if I want also to control the external device over the output from the header in to the beagle bone lets say GPIO 44? thank you for helping in advance

  8. Serenella Ferri De Collibus

    Hi,
    I’m new on libiec61850. I’m trying to use the reports and I’m noticed the function ‘IedConnection_enableReporting’ is never implemented in the library ( in the example:

    IedConnection_enableReporting(con, &error, ”
    simpleIOGenericIO/LLN0.RP.EventsRCB”,
    clientDataSet,
    TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY,
    reportCallbackFunction,
    ClientDataSet_getDataSetValues(clientDataSet));).
    How can I set the trigger options and enable the reports?
    Thank you.

    1. Michael Zillgith Post author

      Hi, the IedConnection_enableReporting function is outdated. For how to enable reporting you can find an example in examples/iec61850_client_example_reporting.

  9. Andrew

    If, I want to subscribe more than one report.
    What can i do?
    I must use some threads or something else?
    Thanks for advice.

  10. muneer

    sir,i have used IedConnection_getFile(…) to download a file from server,but i don’t understand where it is downloaded(location) .i got the response that the number of byte is recieved.

    1. Michael Zillgith Post author

      You have to provide a callback function to receive the data of the file. It is up to you to store the data in a file locally when required. Please look at the example in the folder examples/iec61850_client_example_files.

  11. said

    IEC 61850 client tutorial says that “This client API is designed to be as close as possible to the IEC 61850 ACSI (abstract communication service interface) as defined in IEC 61850-7-2. But be aware that there are some points where there are some deviations from the ACSI due to practical reasons.”
    can you tell me the deviations, please?

    1. Michael Zillgith Post author

      Since the ACSI is actually not an API description but a description what the services are about to do this is not easy to tell. I have not an exhaustive list of such “deviations”. There are some differences in the scope of allowed parameters where the API is usually more close to the MMS mapping. Also in some services there is not a simple one to one mapping between ACSI services and MMS services. E.g. the data model discovery functions like GetLogicalNodeDirectory using the MMS GetNameList cannot be easily mapped one to one without a lot of redundant MMS requests. Here the GetNameList call is done only once for each logical device (MMS domain) and the results are cached in the client for later API calls. So not each API call will result in MMS request.

      1. said

        Thank you for the explanation, Michael.
        If I want to connect/communicate this IEC 61850 client to IEDs from many vendors ( eg. siemens, ABB, alstom etc), whether there will be a problem/issue in the communication because of those “deviations” ?
        Best regards.

        1. Michael Zillgith Post author

          No this doesn’t influence the standard compliance of the communication. It only affects the API and is intended to improve usability and efficiency.

  12. Serenella Ferri De Collibus

    Hi,
    I’m using libiec61850 with OpenMUC. I use the server side in order to simulate the IED, in particular the server_example_5, and a bundle in OpenMUC attempts to write the DA ied1Inverter/ZINV1.OutWSet.setMag.f:SP on the server.
    The policy for FC=SP is set to ALLOW but the write operation from the OpenMUC bundle fails; the error is ‘object_access_denied’ coming from the openiec61850 java library used by OpenMUC. Any idea?
    Thanks.
    Serenella Ferri De Collibus

  13. Jarkko

    I am trying code below with ABB relay but GetRCBValues() is returning error code 22 IED_ERROR_OBJECT_DOES_NOT_EXIST). RCB id should be correct, because I checked it with PCM600 tool.

    string rcbReference1 = “AA1J1Q01A1LD0/LLN0.BR.rcbMeasFlt”;
    ReportControlBlock rcb1 = con.GetReportControlBlock(rcbReference1);
    rcb1.GetRCBValues();

    1. Jarkko

      Had to add indexes to RCB id:s

      string rcbReference1 = “AA1J1Q01A1LD0/LLN0.BR.rcbMeasFlt01”;
      string rcbReference2 = “AA1J1Q01A1LD0/LLN0.BR.rcbMeasReg01”;

  14. said

    Hi Michael,
    How to get stval of GGIO1.SPCO1 from report when the dataset of the report for the GGIO1.SPCO1 contains not only stval, but contains q and t. for example the dataset is :
    -GGIO1.SPCO1
    — stval
    — q
    — t
    -GGIO1.SPCO2
    — stval
    — q
    — t
    …….
    Please tell me the source code to get stval of GGIO1.SPCO1, because in the example (client_example_reporting.c) the dataset of the server containts stval only,
    (-GGIO1.SPCO1.stval, -GGIO1.SPCO2.stval, -GGIO1.SPCO3.stval, -GGIO1.SPCO4.stval)
    thank you

    1. rock61850

      hello
      i got this msg

      An unhandled exception of type ‘System.DllNotFoundException’ occurred in iec61850dotnet.dll

      Additional information: Unable to load DLL ‘iec61850’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)

      please help

  15. Alexandr Leonov

    Hi
    I’m testing the receiving of reports in Visual Studio 2013, following the example from the folder libiec61850-1.0.0 \ dotnet \ report_new_dataset \ inside the dotnet project assembly.
    Testing is performed on Windows7 using libiec61850-1.0.0 library.
    In the example, I changed the references to the dataset and the dataset within the dataset according to the device.
    When you start the application, the device generates a report and starts the report sending mechanism. Reports come to the PC – this can be viewed using Wireshark, but the reportHandler (Report report, object parameter) is not called.
    When checking by the debugger, even the internal report handler IInternalReportHandler (IntPtr parameter, IntPtr report) is not called.
    I assume that the native library iec61850.dll does not call the callback callback function.
    What could be the problem ?

    1. Michael Zillgith Post author

      Hi,
      the reports are identified by the rptId string. If the rptId of the received report doesn’t match with the expected rptId the report will be ignored. When the RptId attribute is not explicitly set in the report control block the server has to send a default rptID with the report. This default rptID should reflect the full object reference of the report control block. Since it is not so clear from the IEC 61850 specification how the exact format of this object reference should be there are different ways that servers encode it (e.g. using “$” or “.” as component separator). This may cause the problem. There has also been a fix regarding this behavior (see github or the latest release 1.0.1). To be sure you can try to set the RptId manually before enabling the report:


      rcb.SetRptId("myreportid");
      rcb.SetRCBValues();

      Then the client should be able to identify the report.
      Please let me know if this doesn’t solve your problem.

      1. Alexandr Leonov

        To solve this problem I was helped by a new implementation libiec61850-1.0.1
        When you create a report, you do not need to specify RptId.
        The server in the response sends the full RptId link in this format “IED_000000001CTRL / Q0GGIO1 $ RP $ urcbA01”
        Everything is working. Thank you

  16. Alexandr Leonov

    Hi Michael,
    If you consider the object model of the device using MMS services, then the order of the attributes in the object is “IED_000000001CTRL / Q0GGIO1.SPCSO3”

    сtlModel [CF]
    Oper [CO]
    сdcNs [EX]
    сtlNum [ST]
    origin [ST]
    q [ST]
    stVal [ST]
    t [ST]

    And if you specify a dataset with this object as – IED_000000001CTRL / Q0GGIO1.SPCSO3 [ST],
    Then in the report comes a set of data with a different order of attributes
    origin [ST]
    ctlNum [ST]
    stVal [ST]
    q [ST]
    t [ST]
    In this case, the first three attributes are not present at all, but I understand it so because of FunctionalConstraint = ST
    What is the problem with the order of the attributes? Is there another way to specify a dataset?

    1. Michael Zillgith Post author

      Hi Alexandr,
      if you got the first list with the MMS get name list service then the server has to return the list in alphabetical order (this is mandatory according to the MMS specification). But when you read the variables with the read services or got it from reports or by using the MMS get variable access attributes service you will receive the data attributes in the order they are defined in the data model (and this order is also specified in the IEC 61850 specification). So there is a discrepancy that is required by the different specification parts.

  17. said

    Hi Michael,
    How to get stval of GGIO1.SPCO1 from report when the dataset of the report for the GGIO1.SPCO1 contains not only stval, but contains q and t. for example the dataset is :
    -GGIO1.SPCO1
    — stval
    — q
    — t
    -GGIO1.SPCO2
    — stval
    — q
    — t
    …….
    Please tell me the source code to get stval of GGIO1.SPCO1, because in the example (client_example_reporting.c) the dataset of the server containts stval only,
    (-GGIO1.SPCO1.stval, -GGIO1.SPCO2.stval, -GGIO1.SPCO3.stval, -GGIO1.SPCO4.stval)
    thank you

    1. Michael Zillgith Post author

      Hi,
      in this case the MmsValue objects in the data set are structured values. You can excess the elements with the MmsValue_getElement function.

      1. Richard Lee

        Hi Michael:
        Continue the question,when GGIO1.SPCO1 is like below:
        -GGIO1.SPCO1
        — stval
        — q
        — t
        And when i get the report mms value ,a structured values like:
        -0
        -0
        -2017-03-28 12:00:00 xxx
        My question is,when i get the values by using MmsValue_getElement function,how can i know the first 0 is stval,the second 0 is q ? How can i get the Data Attributes‘ names of the DO(or mms value)? Could you give some demo code ?
        Thank you very much!

        1. Michael Zillgith Post author

          Hi,
          the values received by MMS don’t contain type information about structured types. But any element that is a basic type contains some type information (MMS_FLOAT, MMS_INTEGER, MMS_BITSTRING…). You can get the type with the MmsValue_getType function. You may then use the type of the element to infer the signification. Anyway this may not be unique because there may be different elements of the same type.
          For the general case there are two ways:
          1) You can rely on the order of the data attributes as they are specified in the CDC of the data object. If the device is standard compliant you can rely on this order.
          2) You can use the IedConnection_getVariableSpecification function once for each dataset member to get the exact type information including the names of the elements. All the required information can be found in the returned MmsVariableSpecification structure.

          1. Richard Lee

            Thank you very much for your reply.
            If any more question , i will ask you again later(after think myself)!
            Thank you~~~~~~

      2. said

        I have used ” MmsValue_getBoolean(MmsValue_getElement(MmsValue_getElement(dataSetValues, i), j)) where i & j are the index, And it’s works 🙂

        Thank you for your suggestion, Michael

  18. Alexandr Leonov

    Hi Michael
    Michael this time I had a problem with the native library iec61850.dll.
    I designed the project on my machine. Then I transferred both the project and the library – iec61850.dll to another PC. On the PC, I copied this library in the same way to the System32 folder. In this case, on the other machine, the program crashes and displays a BadImageFormatException error. I read: it’s a mistake of the library image. The other machine does not have Visual Studio, but there’s FrameWork. Why does this happen?

    1. Michael Zillgith Post author

      Hi, I am not sure. Maybe you need to compile with “Release” instead of “Debug” target? Or perhaps one machine is 64 bit while the other is 32 bit.

  19. said

    Hi Michael,
    How to get timestamp including the miliseconds from the MmsValue?
    I have simulated MmsValue (using IED simulator) with timestamp 14/08/2016 18.09.21.656.
    When I used function “MmsValue_toUnixTimestamp”, the result was : 1471172961 s => 14/08/2016 18.09.21,000 (without mili second)
    But when I used function “MmsValue_getUtcTimeInMs”, the result was : -2000820872 (the result was minus , why ?)
    Which function that I must choose to get timestamp including the miliseconds?

    1. said

      Sorry, Michael. It’s my fault 🙂
      Prior, I have used “printf(” TIMESTAMP (MILISECOND) : %d”, MmsValue_getUtcTimeInMs((MmsValue_getElement(MmsValue_getElement(dataSetValues, i), j)));
      It’s should be using “printf(” TIMESTAMP (MILISECOND) : %lld”, MmsValue_getUtcTimeInMs((MmsValue_getElement(MmsValue_getElement(dataSetValues, i), j)));
      %lld because it is integer 64 bit.
      And It’s works, thank you michael 🙂

  20. said

    Hi Michael,
    File client_example_reporting.c is example for reporting but it’s just for one report of an IED.
    Do you have example for more than one report (multiple reports of an IED)?

    1. said

      I already have reportCallbackFunction, dataSetDirectory, clientDataSet, rcb for report #1 then I add reportCallbackFunction2, dataSetDirectory2, clientDataSet2, rcb2 for report #2, And it’s works 🙂
      thank you

  21. said

    Hi Michael,
    I have used function “IedConnection_close(con)” to release the connection between IEC61850 client and the IED, but the IEC61850 client did not send ‘release’ to the IED. I have used function “IedConnection_release” too, but It did not send ‘release’ to theIED too. Which function that send ‘release’ to IED?
    Thank You

    1. Michael Zillgith Post author

      Hi, you can use the IedConnection_abort function or IedConnection_release function before calling IedConnection_close .

  22. Hilaly Adam

    Hi Michael,
    I’m using your library to read reports from GEMultilin F650 and I can read a report but I don’t know if it’s possible or how to use the report handler to read multiple reports

    1. Michael Zillgith Post author

      Hi, you can subscribe to multiple reports. Just use a second instance of ClientReportControlBlock and install a second report handler with the RCB object reference and report ID.

      1. Hilaly Adam

        I’m using different ReportControlBlock in my application but with one handler (I used the example I found in the library in a for loop and with only one handler that detect the report and save the data in a specific location) but it’s not working should I make a report handler for every report or just one is enough ?

  23. Patrick

    Hi,
    In the example, you say:
    static void
    reportHandler (void* parameter, ClientReport report)
    {
    printf(“report received\n”);
    }

    ClientDataSet dataSet = IedConnection_readDataSetValues(con,
    &error, dataSetReference, NULL);

    IedConnection_installReportHandler(con, “simpleIOGenericIO/LLN0.RP.EventsRCB”,
    reportHandler, NULL, dataSet);

    For the reporting part. But now the prototype of installReportHandler don’t use dataSet?
    Unfortunately my subscription doesn’t work, is it due to dataset missing?
    Thanks

  24. Dumyie

    Hi Michael

    I just started going through your tutorials and demo application but l cannot get the example applications to work. I am getting the error below when l try to run the examples you gave:
    An unhandled exception of type ‘System.DllNotFoundException’ occurred in iec61850dotnet.dll

    Your help would be greatly appreciated.

    Thanks.

    1. Michael Zillgith Post author

      Hi. I guess you need to compile the native dll (iec61850.dll) and put it in a system folder where windows can find it.

  25. chunyunzhe

    Hi
    There is a problem for report. I executed IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_INTG_PD);
    the next step shows error is equal to IED_ERROR_OK, but I goto function ‘reportCallbackFunction’ and print the value of **report->dataSetValues.value.structure.components. It shows that dataAccessError is DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED. What is the reason or why I was denied? My OS is Debian9 64bit and the examle is client_example1.c
    thanks for your helping!

  26. Pin

    HI,
    I a new guy. I have a question in example 1.

      Q: Do I need client, server and mms_client those three example processing in the same time ?

    Is there any thing wrong about my concept?

    Thank you

  27. Pen

    Hi Michael
    Great job ! But I’m realy confused at the object reference “simpleIOGenericIO/GGIO1.AnIn1.mag.f”, which I supposed it should be “simpleIO/GenericIO/GGIO1.AnIn1.mag.f” or maybe simpleIO:GenericIO/GGIO1.AnIn1.mag.f” ?
    Why there is no character between IED name and LD name ? Is this the standard ? If I write a server using this naming scheme , Can it be accessed by third-party programs?
    Thanks !

    1. Michael Zillgith Post author

      There is no separator between the IED name and the LD name. They are simply concatenated. I agree that it is confusing but that’s the standard. Also you cannot use the separator characters in the IED name of LD name.

    1. Michael Zillgith Post author

      Hi,
      in general it is not possible. Usually the LD names you see from the client are a concatenation of the IED name and the LD inst attribute. So when you have multiple logical devices you might extract the common part of the LD names to identify the IED name. But if “functional naming” is used this may not work.

      1. Anson Chen

        Ok, thanks. We currently use a static IED name as prefix of id of devices, but the prefix may be changed in the future. I guess I have to find other ways to pass IED name/prefix to clients.

  28. Anson Chen

    Hi Michael,

    I have a question about p selector. I have successfully changed p selector parameter for client, but I do not find a way to restrict p selector value used in connection in server . Is there any way to do this ?

    1. Michael Zillgith Post author

      No. The server doesn’t check these parameters. This is by purpose to avoid configuration problems.

  29. Mo

    Hi Michael,

    I want to creat several mms client with different IP Addresses,how to creat a mms client with a designated IP Address?

    1. Michael Zillgith Post author

      Hi,
      There is no such feature yet. I will add it to the to do list.

  30. Boris S

    Hi,
    I am new to IEC61850. I set up a client-server communication using examples from your code and discovered the data model. However I am having troubles with reading data from server. This is my code:
    readDataObjectValueFromServer(IedConnection con)
    {
    IedClientError error;
    MmsValue* value;
    float fval;
    while (true)
    {
    /* read an analog measurement value from server */
    value = IedConnection_readObject(con, &error,
    “TMST1/GGIO1.AnIn01.mag.f”, IEC61850_FC_MX);

    if (value != NULL) {
    fval = MmsValue_toFloat(value);
    printf(“read float value: %f\n”, fval);
    MmsValue_delete(value);
    }
    }
    }
    This is part of the SCL file that describes GGIO:




    Server reports no error, but I always read value as 0, it doesn’t matter how much I change the input voltage. I am using a com.tom device, and I have other software tools to test if the device is actually acquiring data, and when using those tools (this tool also uses IEC 61850) I can see that value at GGIO1.AnIn01 is not 0. I am using Visual Studio to build and run this program.

    Thank you!

    1. Boris S

      Hi,
      So in my further efforts to further debug the error. I stumbled upon your function MmsValue_getTypeString.

      So this is my program now:
      void
      readDataObjectValueFromServer(IedConnection con)
      {
      IedClientError error;
      MmsValue* value, *value1, *value2, *value3, *value4;
      float fval;
      int ival;
      const char* str1, *str2, *str3, *str4, *str5;
      while (true)
      {
      /* read an analog measurement value from server */
      value = IedConnection_readObject(con, &error,
      “TMST1/GGIO1.AnIn01.q”, IEC61850_FC_ST);
      printf(“\nq error %d”, error);
      value1 = IedConnection_readObject(con, &error,
      “TMST1/GGIO1.AnIn01.t”, IEC61850_FC_ST);
      printf(“\nt error %d”, error);
      value2 = IedConnection_readObject(con, &error,
      “TMST1/GGIO1.AnIn01.ctlModel”, IEC61850_FC_CF);
      printf(“\nctl error %d”, error);
      value3 = IedConnection_readObject(con, &error,
      “TMST1/LPHD1.PhyHealth.stVal”, IEC61850_FC_ST);
      printf(“\nLPHD error %d”, error);
      value4 = IedConnection_readObject(con, &error,
      “TMST1/GGIO1.AnIn01.mag.f”, IEC61850_FC_MX);
      printf(“\nMag error %d”, error);
      if (value != NULL) {
      str1=MmsValue_getTypeString(value);
      str2 = MmsValue_getTypeString(value1);
      str3 = MmsValue_getTypeString(value2);
      str4 = MmsValue_getTypeString(value3);
      str5 = MmsValue_getTypeString(value4);
      printf(“\nq value: %s\n”, str1);
      printf(“t value: %s\n”, str2);
      printf(“ctl value: %s\n”, str3);
      printf(“LPHD1.PhyHealth value: %s\n”, str4);
      printf(“Magnitude float value: %s\n”, str5);
      fval = MmsValue_toFloat(value4);
      printf(“\n\nread float value: %f\n”, fval);
      ival = MmsValue_toInt32(value4);
      printf(“read int value: %d\n”, ival);
      MmsValue_delete(value);
      MmsValue_delete(value1);
      MmsValue_delete(value2);
      MmsValue_delete(value3);
      MmsValue_delete(value4);
      }
      }
      }

      This is my output:
      q error 0
      t error 0
      ctl error 0
      LPHD error 0
      Mag error 0
      q value: access-error
      t value: access-error
      ctl value: access-error
      LPHD1.PhyHealth value: integer
      Magnitude float value: float

      read float value: 0.000000
      read int value: 0

      Apperantly I am having problems with accessing AnIn01.q, AnIn01.t and AnIn01.ctlModel data attributes. But there is no problem while accessing and reading LPHD1.PhyHealth.stVal and GGIO1.AnIn01.mag.f. So why am I not reading any actual values. Also error variable is always 0, why isnt there an error showing me the access error?

      Thank you!

  31. Boris S

    Hi,
    I am going through your example, and trying to read from server an ST data attribute. I can see in this code that you use function MmsValue_toFloat to convert the value and read it right.
    /* read an analog measurement value from server */
    MmsValue* value = IedConnection_readObject(con, &error,
    “simpleIOGenericIO/GGIO1.AnIn1.mag.f”, MX);

    if (value != NULL) {
    float fval = MmsValue_toFloat(value);
    printf(“read float value: %f\n”, fval);
    MmsValue_delete(value);
    }

    My question is what function should I use to convert an ST or CF data attribute to something that can be shown on terminal. I am not sure If the ST value is actually a string or something else?

    Thank you!!

  32. Karl

    Hi Michael,

    I am a total newbie in MMS and IEC61850 but have got assigned to make changes in a program and one of the suggestions was to use the libiec61850 so I downloaded 1.1.1 and used cmake and got a prepared project for Visual Studio 2015. Built the lib and modfied one of the examples to download files(which is the main task that this is going to be used for) from a device – that worked fine:-). This lib is a static one and the problem is that I need to convert the lib for use in Borland Builder 6 C++ since the rest of the software is built with it. I have found a “recipe” on how to convert a lib from Visual Studio C++ to Borland C++ but that requires that I have a DLL to work with from the start. I have then tried to create a DLL from scratch in VS but I suddenly get compiler errors for all files that contain ‘bool’. I have compared with the static lib projectfiles(using the same ones in fact in my last attempt to build) but I can’t see any difference in content. I have included all files ,both .c and .h, in the new DLL-Project that I created and there are no errors like Can’t open include-file xxxx.h. Syntax-errors where the ‘bool’-type is located in a lot of files only…so far anyway ( (bool MmsValue_getBoolean(const MmsValue* value) for example).

    Any idea what this problem comes from? Maybe you have a better idea than me trying to create my own DLL for converting in this case?

    Regards,
    Karl

    1. Karl

      Hi,

      Having not built the full solution the iec61850 dll was not present but building it all then the DLL showed upp and was found to be built in the iec61850-shared Project. So now the Borland Environment links correctly after converting the DLL:-).

      Regards,
      Karl

  33. K

    Hi,

    I have some questions about FileDirectoryEntry_getFileSize and FileDirectoryEntry_getLastModified.

    First I want to make sure what FileDirectoryEntry_getLastModified returns – is it milliseconds from 1970 or something else?

    Regarding FileDirectoryEntry_getFileSize I have encountered a couple of strange things. I have a dll which I have tested towards an Alstom Micom 61850 device. I ask for filename and filesize for Comtrade-files in a specific directory. Listing is OK. The received filesize seems however to be smaller than what the file is when fetching it. Creating the buffer using the received value from getFileSize results in a crash when the created buffer is much less than the filesize when adding sizes of the fetched chunks. I have doubled the return value from getFileSize and then the fetching of the file works fine. Some known problem or….?

    When using the same dll and running it towards a Siemens Siprotec device it seems like Comtrade-files do not have a size – getFileSize returns 0 for all these files(.cfg, .dat). There are other files though that have a filesize >0 that I can see in the listing(which seems to be working fine here as well). The getFileSize seems to be working for some files and not for some files.
    Do you have any suggestions/ideas on what the problem might be? Are there any circumstances when getFileSize does not return a filesize (except if the file really IS empty of course )? Links only maybe?

    Thank you!

    1. Michael Zillgith Post author

      Hi,
      FileDirectoryEntry_getLastModified returns the milliseconds from 1970 as you said.

      Regarding the file size issue I have not heard of that before. But there is not a lot to do if the server returns a wrong file size. Just be aware that it can happen. I don’t think it is a problem of the library because the file size decoding is very simple. Regarding the file size = 0 it is the same. If you can provide a pcap file containing the messages of the get file directory response I can check if there is a problem at the library side.

  34. K

    Hi and thanks for your answer:-)!

    The file size=0 in this case seemed to be how the device handles files. There were files with zero-size and a .SIP_FR-file that is a compressed thing. In order to get the file a getFile-call had to be done then the device compiles the content(I guess that is extracting from .SIP_FR). This was information from the device-vendor support. I have managed to make it work now but I have encountered another problem.

    The code I have altered to use libiec61850 is included in a DLL to be used by a process that creates multiple threads(one for each device) which in turn creates an instance of the class that is handling the file downloading from the device. When running the process and connecting/downloading from the devices one at a time it seems to work as expected. The files are downloaded and stored in database. Running the process connecting/downloading from multiple devices simultaneously however results in crashes with Access violation. I get the impression that it is happening when multiple IedConnection_getFile-calls are made.

    The method “int CIEC61850DEVICE::GetDisturbances()” (not a static method – just a class member) calls the IedConnection_getFile(con, &error, filePath.c_str() , downloadHandler, NULL).

    For the downloadHandler-function I have tried using:

    Global variables in .cpp:
    int iCurrentPos = 0;
    byte *ppData = NULL;(Before call to getFile: “ppData = new byte[fileSize]” is done];

    1. ================= Using static downloadHandler ====================
    Declared in the class CIEC61850DEVICE in header file:
    static bool downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead);

    Code in the .cpp-file:
    bool CIEC61850DEVICE::downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead)
    {
    memcpy(&((ppData)[iCurrentPos]), buffer, bytesRead);
    iCurrentPos += bytesRead;
    return true;
    }
    =====================

    2. ================= Trying to avoid use of static downloadHandler ===================
    Declared in the class CIEC61850DEVICE in header file:
    static bool downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead);
    bool NonStaticdownloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead);

    Source code in .cpp-file:

    bool CIEC61850DEVICE::downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead)
    {
    return static_cast(parameter)->NonStaticdownloadHandler(parameter, buffer, bytesRead);
    }

    bool CIEC61850DEVICE::NonStaticdownloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead)
    {
    memcpy(&((ppData)[iCurrentPos]), buffer, bytesRead);
    iCurrentPos += bytesRead;
    return true;
    }

    ======================================

    Both case 1 and 2 works fine when connecting to one device at a time. Multiple devices causes crashes. The use of global variables is probably the problem but since IedConnection_getFile requires(as I understand from examples) that the downloadHandler is a static function I don’t see another way to transfer the the values of variables iCurrentPos and ppData between the “downloadHandler” and “GetDisturbances()”.

    I have tried putting these variables in the class CIEC61850DEVICE but without success. It builds but when they are encountered in runtime I get Access Violation again.

    So my questions are:
    – Does libIEC61850 support multithreaded execution in a DLL like the above described situation?
    – If it does – how can this problem be eliminated? As I understand the situation it is the sharing of data between the static function and the filedownloading instance that is the problem but since getFile-call expects a static function – what to do?

    1. Michael Zillgith Post author

      Thanks for the info with the interesting file handling behavior of that device.

      It should be no problem to run the library in a multi-thread environment. You have to use a static class function in C++ (in C “static” has another meaning – it restricts the visibility of the function to the compile unit, and in the C sense the callback function doesn’t have to be static). In general it should work like you did it in your 2. approach. But I think you don’t need the “non-static” handler because once you have the object reference you can also access the instance variables.

  35. jonhson

    Hi Michael,
    i try to use the generator of model “genconfig.jar” with my file but i have one error which is “Unsupported type OCTET_STRING_64” so what can i do to solve this in order to generate my models?

    Thanks.

Comments are closed.