1 stable release

1.0.0 Aug 10, 2024

#428 in Hardware support

Download history 44/week @ 2024-08-04 68/week @ 2024-08-11 3/week @ 2024-08-18

115 downloads per month

Custom license

18KB
283 lines

RoboPLC I/O connector for IEC 60870-5 crates.io page docs.rs page

Introduction

IEC 60870-5 is a set of standards for telecontrol, teleprotection, and associated telecommunications for electric power systems, widely used in the European Union, the United Kingdom and other locations.

This crate provides I/O connector for RoboPLC.

The crate IS NOT FREE for any commercial or production use. Please refer to https://github.com/roboplc/roboplc-io-iec60870-5/blob/main/LICENSE.md for more information.

The client additionally supports:

  • Auto-reconnects
  • Multi-threading
  • Real-time safety
  • Enterprise support from the vendor

Note: as the client has got an asynchronous-manner reader loop, it is HIGHLY RECOMMENDED to use timeouts. In case if a remote does not respond, a request with no timeout gets stuck forever.

Example

IEC 60870-5 101 (Serial)

The crate does not provide any client for IEC 60870-5 101 (Serial) as such does not require re-connection or a special telegram processing logic. Any communication library plus IEC 60870-5 crate can be used to create/parse IEC 60870-5 101 telegrams.

IEC 60870-5 104 (TCP)

Connecting a client

use roboplc::comm::Timeouts;
use roboplc_io_iec60870_5::iec104::{Client, PingKind};
use std::time::Duration;

// Create a new IEC 60870-5 104 client
let (client, reader) = Client::new("192.168.1.100:2404", Timeouts::default(), 1024).unwrap();
// Get the telegram receiver
let telegram_rx = reader.get_telegram_receiver();
// The reader must be run in a separate thread
std::thread::spawn(move || reader.run());
// Create an optional pinger worker, which can send test/ack frames or
// automatically re-connect the socket
let pinger = client.pinger(PingKind::Test, Duration::from_secs(1));
std::thread::spawn(move || pinger.run());

Handling incoming telegrams

use iec60870_5::{
    telegram104::{Telegram104, Telegram104_I},
    types::{datatype::{DataType, M_EP_TA_1}, COT},
};
use roboplc::prelude::*;
use std::time::Duration;

while let Ok(telegram) = telegram_rx.recv() {
    println!("{:?}", telegram);
    if let Telegram104::I(i) = telegram {
        if i.data_type() == DataType::M_EP_TA_1 && i.cot() == COT::Cyclic {
            for iou in i.iou() {
                let v: M_EP_TA_1 = iou.value().into();
                let dt = Timestamp::try_from(v.time)
                    .unwrap()
                    .try_into_datetime_local()
                    .unwrap();
                dbg!(v.sep.es, Duration::from(v.elapsed), dt);
            }
        }
    }
}

Sending telegrams

The client provides two methods to send telegrams:

use iec60870_5::{
    telegram104::{Telegram104, Telegram104_I},
    types::{datatype::{DataType, SelectExecute, C_RC_TA_1, QU, RCO, RCS}, COT},
};

use roboplc::prelude::*;
let mut telegram: Telegram104_I = Telegram104_I::new(DataType::C_RC_TA_1, COT::Act, 47);
telegram.append_iou(
    12,
    C_RC_TA_1 {
        rco: RCO {
            rcs: RCS::Increment,
            se: SelectExecute::Execute,
            qu: QU::Persistent,
        },
        time: Timestamp::now().try_into().unwrap(),
    },
);
let request: Telegram104 = telegram.into();
let reply = client.command(request).unwrap();
println!("COMMAND REPLY: {:?}", reply);

Locking policy

The crate locking policy is set using the same features as RoboPLC locking policy. The selected policy must be the same.

Dependencies

~9–19MB
~257K SLoC