Switch branches/tags
Nothing to show
Find file Copy path
8f9f194 Oct 10, 2018
1 contributor

Users who have contributed to this file

570 lines (387 sloc) 27 KB

lib60870.NET 2.2 User Guide


lib60870.NET is an implementation of the IEC 60870-5 protocol for client (master station) and server (controlled station). The library implements all data types of the IEC 60870-5-101/104 specifications. lib60870.NET is a pure C#/.NET implementation. It is designed to be as easy to use as possible. Since it is using only standardized .NET features it is compatible with Windows systems and the Microsoft .NET implementation as well as on Linux and other operating systems running with Mono.

The client/server API is strictly asynchronous. You send the requests with non-blocking functions and will have to handle the response and other events in callback functions.

The list of supported features:

  • CS 101 (IEC 60870-5-101) balanced and unbalanced serial modes

  • CS 104 (IEC 60870-5-104) client and server TCP/IP communication

  • CS 104 supports encrypted and authenticated TLS communication

  • CS 104 uses the CS 101 application layer

  • CS 104 slave supports multiple redundancy groups

  • Master/Client supports sending system commands, process commands, parameter commands, and data messages in reverse direction.

  • Slave/Server supports sending data messages in monitoring direction and commands in reverse direction

  • The list of supported ASDU types can be found in the annex

  • The library supports user defined private ASDU types

  • transmission of transparent files

  • CS 101 protocol over TCP/IP (client/server)

  • Support for .NET Standard, Mono, and .NET core 2.0

NOTE: CS stands for "companion standard" and specifies variants of the communication protocols and services defined in the IEC 60870-5 standard series.

Master (client) side programming

For master side programming the following API classes can be used:

  • CS101Master for a CS 101 compliant balanced mode or unbalanced mode serial connection.

  • Connection for a CS 104 compliant TCP/IP connection

All of these classes implement the abstract base class Master that provides a set of methods to send commands and messages to the slave.

Create a connection to a CS104 server

Since an IEC 60870-5-104 (CS 104) connection is based on a TCP client/server connection the connection will be established by the client(master). The server(slave or outstation) is passively waiting for connections.

In C# a connection is simply created by calling a Constructor of the Connection class:

Connection con = new Connection ("");

This creates a new Connection object that is ready to connect to the server. If you can go with the default connection parameters (TCP port 2404 and a common set of the IEC 60870-5-104 parameters) you can now simply call the Connect method:

con.Connect ();

When the connection has been established correctly you can use the connection object to send commands and receive data.

To use the Connection class you have to import the lib60870.CS104 namespace into your application:

using lib60870.CS104;

Preparing a CS101 connection to one or more slaves

CS101 provides two kind of serial connection modes for master/slave communication.

Balanced mode supports communication between a single master and a single slave using a dedicated serial line. Both ends can spontaneously send messages at any time.

Unbalanced mode supports communication between a single master and multiple slaves on a serial bus. Each slave is addressed by its unique link layer address. Slaves do not send spontaneously. They only respond after a request form the master. The master can address multiple slaves by using a broadcast address.

Configuring the serial port

For both mode first the serial port has to be configured and initialized. The following code shows an example how to prepare the serial port for using with the library:

Example configuration of the serial port
SerialPort port = new SerialPort ();
port.PortName = "/dev/ttyUSB1";
port.BaudRate = 9600;
port.Parity = Parity.Even;
port.Handshake = Handshake.None;
port.Open ();

Before passing the SerialPort object to the library the port has to be opened.

Setting the link layer parameters is an optional step. When not explicitly set a default set of parameters will be used for the new master instance.

Example link layer parameter settings
LinkLayerParameters llParameters = new LinkLayerParameters();
llParameters.AddressLength = 2;
llParameters.UseSingleCharACK = false;

In the example the length of the link layer address will be set to two bytes and the library will be instructed not to send single char ACK messages but use short messages instead. The new LinkLayerParameters object will be passed to the master constructor in the next step.

Create and use a new unbalanced master instance

For balanced and unbalanced communication mode the CS101Master class has to be used.

The following code create a new unbalanced master instance and provides the previously set link layer parameters. The SetASDUReceivedHandler method provides a callback function for received ASDUs. The AddSlave function will create a new slave specific state machine to handle all communication with the slave with link layer address 1.

CS101Master master = new CS101Master(port, LinkLayerMode.UNBALANCED, llParameters);
master.SetASDUReceivedHandler (asduReceivedHandler, null);
master.AddSlave (1);

The link layer parameters and application layer parameters are optional parameters. If not set default instances of the parameter classes are created and used.

Before sending a command or other request to a specific slave the SlaveAddress property has to be set.

Example: Set the slave address before sending a command
master.SlaveAddress = 1;
master.GetFile (1, 30000, NameOfFile.TRANSPARENT_FILE, new Receiver ());

Balanced master

The balanced master is created the same way. Just the link layer mode parameter is different.

Example: Creating a balanced CS 101 master
CS101Master master = new CS101Master (port, LinkLayerMode.BALANCED, llParameters, alParameters);
master.OwnAddress = 1;
master.SlaveAddress = 2;
master.SetASDUReceivedHandler (asduReceivedHandler, null);
master.SetLinkLayerStateChangedHandler (linkLayerStateChanged, null);

In balanced mode the slave address has only to be set one time, because there is only one client.

The LinkLayerStateChangedHandler can be used to track changes of the link layer state. This way it can be detected when there is an error or the other side’s link layer is no longer available.

Sending a read request

The IEC 60870 documents don’t recommend this service (cyclical data requests or polling) but it is an easy way to get the required data. You just need to know the common address (CA) and the information object address (IOA) to create the proper request.

con.SendReadCommand(1 /* CA */, 2001 /* IOA */);

The call is non-blocking. You have to evaluate the response in the ASDUReceivedHandler callback function.

Typically it is expected that the server response contains only the basic data type without timestamps (that is using the message types for a specific data type that does not contain the timestamps)!


You can also request a group of data items from a slave with a single request. On the master (client) side you can simply use the SendInterrogationCommand method of the Connection object:

con.SendInterrogationCommand (CauseOfTransmission.ACTIVATION, 1, 20);

The client/master side method signature looks like this:

public void SendInterrogationCommand(CauseOfTransmission cot, int ca, byte qoi)

The parameter ca is the common address (CA) as in the other methods. The parameter qoi is the "Qualifier of interrogation" (QOI). The value "20" (indicating "station interrogation") for the QOI indicates that it is an request for all data points. Other values for QOI will indicate that the client (master) only wants to receive data from a specific interrogation group.

Clock synchronization procedure

For the clock synchronization procedure the controlling station (master) sends a C_CS_NA_1 ACT message to the controlled station (slave) containing the current valid time information as a CP56Time2a typed time value. The controlled station has to update its internal time and respond with a C_CS_NA_1 ACT_CON message after all queued time-tagged PDUs have been sent.

Clock synchronization of the controlled station can be done with a with the SendClockSyncCommand method of the Connection class.

CP56Time2a currentTime = new CP56Time2a (DateTime.Now);
con.SendClockSyncCommand (1 /* CA */, currentTime);

Command procedures

Commands are used to set set points, parameters or trigger some actions at the outstation.

The following command types (data types are available for commands):

  • C_SC (single command) - to control binary data (switch…​)

  • C_DC (double command) - to control binary data with transition state (moving switch…​)

  • S_RC (step position command) - to control a step position

  • S_SE (setpoint command) - to control a set point (scaled value, normalized value, floating point values) - may also be used to set parameters, alarm limits etc.

These command types are also available in a version with a time tag (CP56TIme2a).

There are two different command procedures available. The direct operate command procedure and the select and operate command procedure.

To send a command for the direct operate command procedure you have to send an ACTIVATION APDU to the outstation.

Connection con = new Connection ("");
con.SendControlCommand (TypeID.C_SC_NA_1, CauseOfTransmission.ACTIVATION, 1,
              new SingleCommand (5000, true, false, 0));

To issue a single command you have to provide the proper TypeID (C_SC_NA_1) and pass a SingleCommand instance to the SendControlCommand method.

The constructor of SingleCommand has the following signature:

public SingleCommand (int ioa, bool command, bool selectCommand, int qu)

In order to send a direct operate command the selectCommand parameter should be false. The qualifier (qu) should in general be set to 0.

If the command has been successful the outstation will answer with an ACT_CON response message with the negative flag not set. In case the outstation cannot execute the command it will also answer with an ACT_CON response but with the negative flag set. You can check if this flag is set with the IsNegative property of the received ASDU instance.

Slave (server) side programming

Server configuration and setup

To configure and setup an IEC 60870-5-104 server/slave an instance of the Server class is required.

Server server = new Server ();

After the server instance is created it can be configured

Server mode

The server provides three different modes.

The default mode (SINGLE_REDUNDANCY_GROUP) allows only a single active client connection. An active client connection is a connection where ASDUs (application data units) are sent. All other connections are only standby connections that don’t send application layer data. There is a single queue for events. Events are also stored when no client is connected or when no connection is active.

The second mode (CONNECTION_IS_REDUNDANCY_GROUP) allows multiple active client connections. Every connection has its own event queue. The event queue will be deleted when the client connection is closed. This mode can be used when more than one client has to access the application data. This mode is easy to use. But the drawback of this mode is that events are lost when no client is connected.

The third mode (MULTIPLE_REDUNDANCY_GROUPS) allows multiple active client connections while preserving events when no client is connected. In this mode clients can be assigned to specific redundancy groups. The assignment is based on the IP address of the client. A redundancy group can have multiple simultaneous connections but only one of these connections can be active. The number of activated connections is restricted by the number of redundancy groups. Each redundancy group has a dedicated event queue.

The server mode can be set with the ServerMode property of the Server class.

server.ServerMode = ServerMode.CONNECTION_IS_REDUNDANCY_GROUP;

Define multiple redundancy groups

Redundancy groups only have to be created explicitly when using the servermode MULTIPLE_REDUNDANCY_GROUPS. You can assign multiple IP addresses to a redundancy group. Incoming connections from one of these IP addresses will then automatically be assigned to this redundancy group.

When a redundancy group has no assigned IP address it works as a "catch all" group. This means that all incoming connections that are not assigned to one of the other groups will end up in this group.

Example how to define multipe redundancy groups
/* Configure a server with three redundancy groups */

server.ServerMode = ServerMode.MULTIPLE_REDUNDANCY_GROUPS;

RedundancyGroup redGroup1 = new RedundancyGroup("red-group-1");

RedundancyGroup redGroup2 = new RedundancyGroup("red-group-2");

/* add a "catch all" redundancy groups - all other connections are handled by this group */
RedundancyGroup redGroup3 = new RedundancyGroup("catch all");


Restrict the number of client connections

The number of clients can be restricted with the MaxOpenConnections property of the Server class.

server.MaxOpenConnections = 2;

In this case the server will only allow two concurrent client connections.

Setting local port and IP address

The default TCP port for IEC 60870-5-104 is 2404. The port can be changed with the SetLocalPort method of the Server class.


Per default the server listens to all local IP addresses. With the SetLocalAddress method of the Server class it is possible to restrict the server to a single local IP address.


Set the size of the event queue

The maximum size of the event queue(s) can be set with the MaxQueueSize property of the Server class. The default size is 1000. Each queue entry needs approximately 260 bytes.

server.MaxQueueSize = 10;

Set a connection request handler to restrict the access and track connections

The ConnectionRequestHandler can be used to restrict the access to the server. With the return value the application can allow or deny the connection attempt of a client.

A ConnectionRequestHandler can be set with the SetConnectionRequestHandler method of the Server class. The second parameter is an arbitrary user provided object that will be passed to the handler when it is called. If not needed it can be set to null.

server.SetConnectionRequestHandler (connectionRequestHandler, null);
Example how to implement a ConnectionRequestHandler in C#
static bool connectionRequestHandler(object parameter, IPAddress ipAddress)
  // Allow only known IP addresses!
  // You can implement your allowed client whitelist here
  if (ipAddress.ToString ().Equals (""))
    return true;
    return false;

In the handler you can optionally check the client IP address against a whitelist of allowed clients or implement a blacklist.

Starting/Stopping the server

After the server is configured it can be started with the Start method

server.Start ();

To deactivate the IEC 60870-5-104 service the server can be stopped with the Stop method.

server.Stop ();

Handling interrogation requests

On the server side you should use the InterrogationHandler delegate to handle the Interrogation request. Depending on the QOI value your should return different information objects. For a simple system it is enough to only handle station interrogation requests (QOI = 20).

According to the specification the server has to respond the ACTIVATION request from the client with the ACT_CON response followed by ASDUs containing the information objects with the COT = INTERROGATED_BY_STATION. After sending all information objects the server has to send the initial interrogation command message with COT = ACT_TERM to indicate that the transmission of the interrogation data is finished.

Example how to implement an InterrogationHandler in C#
  private static bool interrogationHandler(object parameter, ServerConnection connection, ASDU asdu, byte qoi)
    Console.WriteLine ("Interrogation for group " + qoi);

    // send ACT_CON
    connection.SendACT_CON (asdu, false);

    // send information objects
    newAsdu.AddInformationObject (new MeasuredValueScaled (100, -1, new QualityDescriptor ()));
    newAsdu.AddInformationObject (new MeasuredValueScaled (101, 23, new QualityDescriptor ()));
    newAsdu.AddInformationObject (new MeasuredValueScaled (102, 2300, new QualityDescriptor ()));
    connection.SendASDU (newAsdu);

    newAsdu = new ASDU (TypeID.M_ME_TE_1, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 3, 1, false);
    newAsdu.AddInformationObject(new MeasuredValueScaledWithCP56Time2a(103, 3456, new QualityDescriptor (), new CP56Time2a(DateTime.Now)));
    connection.SendASDU (newAsdu);

    newAsdu = new ASDU (TypeID.M_SP_TB_1, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, false);
    newAsdu.AddInformationObject (new SinglePointWithCP56Time2a (104, true, new QualityDescriptor (), new CP56Time2a (DateTime.Now)));
    connection.SendASDU (newAsdu);

    // send ACT_TERM
    connection.SendACT_TERM (asdu);

    return true;

Spontaneous transmission of messages

For spontaneous message transmission on the server side the API user has to allocate an ASDU object, add Information Objects to the ASDU and put the ASDU into the transmission queue. The transmission queue is a FIFO (first in first out) list. If the queue is full the oldest message will be deleted and replaced by the newly added message. Messages will only be sent if the there is an active client connection. Otherwise the messages will remain in the queue until a connection is activated.

CS104 The size of the queue is controlled by the property MaxQueueSize of the Server object..

These are the required steps:

  1. Step: Create a new ASDU instance

    ASDU newAsdu = new ASDU (TypeID.M_ME_NB_1, CauseOfTransmission.PERIODIC, false, false, 2, 1, false);
  2. Step: Add an information object to the ASDU

    newAsdu.AddInformationObject (new MeasuredValueScaled (110, -1, new QualityDescriptor ()));
  3. Step: Add the ASDU to the transmission queue

    server.EnqueueASDU (newAsdu);

Adding multiple Information Objects to a single ASDU

Multiple information objects can be included in the same ASDU when the information objects are all of the same type.

The return value of the AddInformationObject method indicates if the information object has been added successfully. When the ASDU capacity is already reached the return value is false and the InformationObject instance has not been added.

Advanced topics

Creating non-standard information object types

For lib60870.NET you should derive a new class from the InformationObject class.

CS 101 protocol over TCP/IP

The CS 101 master or slave protocols can also be used over a TCP/IP connection with the TcpServerVirtualSerialPort and TcpClientVirtualSerialPort classes. These classes can be used to configure the CS 101 master or slave as a TCP/IP client or server.

NOTE: This is a non-standard extension! According to the IEC 60870-5 standard TCP/IP has to be used according to IEC 60870-5-104 (CS 104).

To use TCP/IP instead of a serial connection you have to create an instance of the TcpServerVirtualSerialPort or TcpClientVirtualSerialPort class and handle the instance to the constructor of the CS101Master or CS101Slave class.

Example CS 101 slave acting as TCP/IP server
TcpServerVirtualSerialPort port = new TcpServerVirtualSerialPort ();
port.Start ();

CS101Slave slave = new CS101Slave (port, llParameters);


port.Stop ();

The Start and Stop methods are used to start and stop the TCP/IP client and server. The server class allows only a single client connection.

ANNEX - Reference information

Supported message types

The library supports the following ASDU (application service data unit) types.

Table 1. IEC 60870-5-101/104 message types
Message type Description C C#


Single point information (BOOLEAN)




Single point information (BOOLEAN) with CP24Time2a




Double point information (ON/OFF/transient)




Double point information (ON/OFF/transient) with CP24Time2a




Step position information (-64 …​ 63, is transient)




Step position information (-64 …​ 63, is transient) with CP24Time2a




Bitstring32 (32 bit bitstring)




Bitstring32 (32 bit bitstring) with CP24Time2a




Normalized measured value (-1.0 …​ +1.0)




Normalized measured value (-1.0 …​ +1.0) with CP24Time2a




Scaled measured value (-32768 …​ +32767)




Scaled measured value (-32768 …​ +32767) with CP24Time2a




Short measured value (FLOAT32)




Short measured value (FLOAT32) with CP24Time2a




Integrated totals (INT32 with quality indicators)




Integrated totals (INT32 with quality indicators) with CP24Time2a




Event of protection equipment




Packed start events of protection equipment




Packed output circuit info




Packed single point with SCD




Normalized measured value (-1.0 …​ +1.0) without quality




Single point information (BOOLEAN) with CP56Time2a




Double point information (ON/OFF/transient) with CP56Time2a




Step position information (-64 …​ 63, is transient) with CP56Time2a




Bitstring32 (32 bit bitstring) with CP56Time2a




Normalized measured value (-1.0 …​ +1.0) with CP56Time2a




Scaled measured value (-32768 …​ +32767) with CP56Time2a




Short measured value (FLOAT32) with CP56Time2a




Integrated totals (INT32 with quality indicators) with CP56Time2a




Event of protection equipment with CP56Time2a




Packed start events of protection equipment with CP56Time2a




Packed output circuit info with CP56Time2a




Single command (BOOLEAN)




Double command (ON/OFF/transient)




Step command




Setpoint command, normalized value (-1.0 …​ +1.0)




Setpoint command, scaled value (-32768 …​ +32767)




Setpoint command, short value (FLOAT32)




Bitstring command (32 bit bitstring)




Single command (BOOLEAN) with CP56Time2a




Double command (ON/OFF/transient) with CP56Time2a




Step command with CP56Time2a




Setpoint command, normalized value (-1.0 …​ +1.0) with CP56Time2a




Setpoint command, scaled value (-32768 …​ +32767) with CP56Time2a




Setpoint command, short value (FLOAT32) with CP56Time2a




Bitstring command (32 bit bitstring) with CP56Time2a




Interrogation command




Counter interrogation command




Read command




Clock synchronization command




Reset process command




Delay acquisition command




Parameter of measured values, normalized value




Parameter of measured values, scaled value




Parameter of measured values, short floating point number




Parameter for activation



CS101/CS104 application layer parameters

The following parameters are for the CS101/CS104 application layer and are stored in the lib60870.CS101.ApplicationLayerParameters class.

Table 2. IEC 60870-5-101/104 application layer parameters
Parameter Description


Size of the COT field of the ASDU. Can be 1 or 2 (default). When the size is 2 the COT field contains the originator address (OA).


Originator address


Size of the common address (CA) field of the ASDU. Can be 1 or 2 (default).


Size of the information object addresses (IOA). Can be 1, 2, or 3 (default).


Maximum allowed length of the ASDU (default and maximum is 249). Should not be changed.

The LinkLayerParameters class stored the configuration parameters for the CS 101 link layer.

Table 3. IEC 60870-5-101 link layer parameters
Parameter Description


Size of the link layer address field of the LPCI. Can be 0, 1 (default), or 2.


Timeout for ACK of the link layer message


Timeout for repeated transmission of link layer messages.


Indicates if the secondary link layer will use single char ACK (E5)

CS104 specific parameters

The following parameters are stored in APCIParameters objects.

Table 4. IEC 60870-5-104 parameters
Parameter Description


Number of unconfirmed APDUs in I format. Sender will stop transmission after k unconfirmed I messages.


Number of unconfirmed APDUs in I format. Receiver will confirm latest after w messages


Timeout for connection establishment (in s)


Timeout for transmitted APDUs in I/U format (in s) when timeout elapsed without confirmation the connection will be closed. This is used by the sender to determine if the receiver has failed to confirm a message.


Timeout to confirm messages (in s). This timeout is used by the receiver to determine the time when the message confirmation has to be sent.


time until test telegrams will be sent in case of an idle connection