IEC-60870

IEC 60870-5-104 is a telecontrol protocol used to monitor and control geographically distributed process equipment - substations, RTUs and similar equipment in electric power systems. It runs over TCP/IP, typically on the well-known port 2404.

The protocol is push driven: once a session is established, the remote station (RTU) sends unsolicited application service data units (ASDUs) for the information objects it holds. There is nothing for a client to actively read or write - it only subscribes and receives what the station reports.

Supported Operations

Name Value Description

subscribe

Go and Java. The only supported operation. A subscription’s tag address selects which information objects of which station(s) are delivered to it; the station itself decides what and when to send.

Reading, writing and browsing are not implemented and a request for any of them is rejected.

Connection String

IEC 60870-5-104 has the following connection string format:

iec-60870-5-104:tcp://{ip-address}:{port}?{options}

The only supported transport is tcp, defaulting to port 2404:

iec-60870-5-104:tcp://192.168.178.101:2404

Connection String Options

Name

Type

Default Value

Required

Description

Name

IEC 60870-5-104

Code

iec-60870-5-104

Maven Dependency

<dependency>
  <groupId>org.apache.plc4x</groupId>
  <artifactId>plc4j-driver-iec-60870</artifactId>
  <version>1.1.0</version>
</dependency>

Default Transport

tcp

Supported Transports

  • tcp

Config options:

request-timeout-ms

INT

4000

Maximum time (in milliseconds) to wait for the test-frame and start-data-transfer handshake replies during connection setup.

Transport config options:

tcp

tcp.connect-timeout-ms

INT

5000

Connection timeout in milliseconds.

tcp.read-timeout-ms

INT

0

Socket read timeout in milliseconds. 0 means no timeout.

tcp.write-timeout-ms

INT

0

Socket write timeout in milliseconds. 0 means no timeout.

tcp.no-delay

BOOLEAN

true

Enable TCP_NODELAY (disable Nagle’s algorithm).

tcp.keep-alive

BOOLEAN

false

Enable SO_KEEPALIVE.

tcp.send-buffer-size

INT

81920

Send buffer size in bytes. 0 uses system default.

tcp.receive-buffer-size

INT

81920

Receive buffer size in bytes. 0 uses system default.

tcp.local-address

STRING

Local address to bind to (optional). If not set, uses default.

tcp.local-port

INT

0

Local port to bind to (optional). 0 uses ephemeral port.

Tag Addresses

Addressing is implemented in Go and Java. The protocol is push driven, so tags are used with subscriptions only. See the protocol support matrix for what each implementation does.

General Format

A tag address names one information object of one station and is written as <asdu>/<ioa>:

  • <asdu> is the 2 octet common address of ASDU (the station address). Write it either as one decimal number 0..65535 or octet by octet as <low>/<high>, each 0..255.

  • <ioa> is the 3 octet information object address. Write it either as one decimal number 0..16777215 or octet by octet as <low>.<middle>.<high>, each 0..255.

The octet-wise forms are written least significant octet first, the order the octets appear in on the wire. So 1/2 as an ASDU address means 1 + 2 * 256 = 513, not 258.

This is the opposite order of the identically shaped a/b/c address of the KNX driver, where the most significant part (main group) comes first. A KNX-style reading of an IEC-60870 address is silently a valid address of a different station, so double-check the order when porting addresses or copying examples between the two drivers.

A * may take the place of any number and makes that position a wildcard, so that one subscription can cover a whole station, a whole octet range, or everything the connection reports.

Surrounding whitespace is trimmed and leading zeros are accepted, up to the number of digits the maximum of that component has (3 for an octet, 5 for a whole ASDU address, 8 for a whole information object address). So 007/002.03.004 is accepted while 000007/2 is not. Whitespace inside an address is not accepted.

Every incoming information object is matched against the address of each subscription and only delivered to the subscriptions that cover it - the station itself reports everything it has, the filtering happens in the driver.

Malformed and out-of-range addresses are rejected when the request is built, with a PlcInvalidTagException.

Earlier versions of this driver ignored the address entirely: any string was accepted and every subscription received every incoming ASDU, leaving the filtering to the consumer. Existing code therefore needs its addresses reviewed - in particular a bare * is no longer an address, the "everything" address is */* - and consumer-side filtering can now be dropped.

Data Types

A tag address carries no type information: the datatype of an information object follows from the type identification of the ASDU that delivers it, which is only known at runtime, not from the address itself.

Both drivers attach the information object’s quality descriptor to the delivered value as metadata alongside it, rather than silently folding it into the value: invalid, notTopical, substituted, blocked and, where the ASDU carries it, overflow. A reading the station marked invalid, blocked, substituted or not topical therefore does not arrive looking like a good one - check this metadata before trusting a value.

Examples

Table 1. Examples
Address Meaning

1/2

ASDU 1, information object 2

65535/16777215

the widest address both fields can carry

1/2/3

ASDU 513 (low octet 1, high octet 2), information object 3

1/2.3.4

ASDU 1, information object 262914 (low 2, middle 3, high 4)

1/2/3.4.5

ASDU 513, information object 328707

*/*

every information object of every station

1/*

every information object of ASDU 1

*/3.4.5

information object 328707 of every station

1/2/3.*.5

low and high octet pinned, middle octet wildcarded