Skip to content

roboplc/roboplc-io-iec60870-5

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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:

  • [iec104::Client::send] for sending a telegram with no reply expected
  • [iec104::Client::command] for sending a command telegram and waiting for a reply
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.

Releases

No releases published

Packages

No packages published

Languages