A high-performance Rust library for encoding and decoding IEC 61850 GOOSE and Sampled Values (SMV) messages. !The library is under development!
This library provides efficient Rust implementations for IEC 61850-8-1 (GOOSE) and IEC 61850-9-2 LE (Sampled Values) protocols. It features:
- sampled value encoding
- sampled value decoding
- GOOSE encoding
- GOOSE decoding
Add this to your Cargo.toml:
[dependencies]
iec_61850_lib = { git = "https://github.com/OpenEnergyTools/iec61850lib.git" }Or clone and build locally:
git clone https://github.com/OpenEnergyTools/iec61850lib.git
cd iec61850lib
cargo build --releaseThe library exposes four main functions for working with IEC 61850 protocols:
Encode a GOOSE PDU with Ethernet header into a complete frame:
use iec_61850_lib::encode_goose::encode_goose;
use iec_61850_lib::types::{EthernetHeader, IECGoosePdu, IECData};
// Create Ethernet header
let header = EthernetHeader {
dst_addr: [0x01, 0x0c, 0xcd, 0x01, 0x00, 0x00],
src_addr: [0x00, 0x11, 0x22, 0x33, 0x44, 0x55],
tci: Some([0x81, 0x00, 0xa0, 0x00]), // VLAN tag (optional)
ether_type: [0x88, 0xb8], // GOOSE EtherType
appid: [0x00, 0x01],
length: [0x00, 0x00], // Will be set automatically
};
// Create GOOSE PDU
let pdu = IECGoosePdu {
go_cb_ref: "IED1$GO$GoCB01".to_string(),
time_allowed_to_live: 2000,
dat_set: "IED1$Dataset1".to_string(),
go_id: "IED1_GOOSE1".to_string(),
t: Default::default(), // Timestamp
st_num: 1,
sq_num: 0,
test: false,
conf_rev: 1,
nds_com: false,
num_dat_set_entries: 2,
all_data: vec![
IECData::Boolean(true),
IECData::Int32(12345),
],
simulation: false,
security: None,
};
// Encode to complete Ethernet frame
match encode_goose(&header, &pdu) {
Ok(frame) => {
println!("Encoded GOOSE frame: {} bytes", frame.len());
// Send frame to network...
}
Err(e) => eprintln!("Encoding failed: {:?}", e),
}Decode a GOOSE message from a raw Ethernet frame:
use iec_61850_lib::decode_goose::decode_goose_pdu;
use iec_61850_lib::decode_basics::decode_ethernet_header;
use iec_61850_lib::types::EthernetHeader;
// Raw packet buffer from network
let packet: &[u8] = &[/* ... raw bytes ... */];
// First decode the Ethernet header
let mut header = EthernetHeader::default();
let pos = decode_ethernet_header(&mut header, packet);
// Then decode the GOOSE PDU
match decode_goose_pdu(packet, pos) {
Ok(pdu) => {
println!("GOOSE ID: {}", pdu.go_id);
println!("State Number: {}", pdu.st_num);
println!("Sequence Number: {}", pdu.sq_num);
println!("Data entries: {}", pdu.all_data.len());
// Process data
for data in &pdu.all_data {
println!(" {:?}", data);
}
}
Err(e) => eprintln!("Decoding failed: {:?}", e),
}Encode Sampled Values PDU with zero-copy performance:
use iec_61850_lib::encode_smv::encode_smv;
use iec_61850_lib::types::{EthernetHeader, SavPdu, SavAsdu, Sample};
// Create Ethernet header for SMV
let header = EthernetHeader {
dst_addr: [0x01, 0x0c, 0xcd, 0x04, 0x00, 0x01],
src_addr: [0x00, 0x11, 0x22, 0x33, 0x44, 0x55],
tci: None,
ether_type: [0x88, 0xba], // SMV EtherType
appid: [0x40, 0x00],
length: [0x00, 0x00],
};
// Create sample data (8-bit integer samples with quality)
let samples = vec![
Sample::new(1000, 0), // value, quality
Sample::new(2000, 0),
Sample::new(3000, 0),
];
// Create ASDU
let asdu = SavAsdu {
sv_id: "IED1_SMV1".to_string(),
smp_cnt: 0,
conf_rev: 1,
smp_synch: Some(0),
smp_rate: Some(4800),
sample: samples,
smp_mod: None,
gm_identity: None,
ref_r_tm: None,
};
// Create SMV PDU
let pdu = SavPdu {
no_asdu: 1,
sav_asdu: vec![asdu],
security: None,
};
// Encode with zero-copy performance
match encode_smv(&header, &pdu) {
Ok(frame) => {
println!("Encoded SMV frame: {} bytes", frame.len());
// Send frame to network...
}
Err(e) => eprintln!("Encoding failed: {:?}", e),
}Decode Sampled Values messages from raw packets:
use iec_61850_lib::decode_smv::decode_smv;
use iec_61850_lib::decode_basics::decode_ethernet_header;
use iec_61850_lib::types::EthernetHeader;
// Raw packet buffer from network
let packet: &[u8] = &[/* ... raw bytes ... */];
// Decode Ethernet header
let mut header = EthernetHeader::default();
let pos = decode_ethernet_header(&mut header, packet);
// Decode SMV PDU
match decode_smv(packet, pos) {
Ok(pdu) => {
println!("Number of ASDUs: {}", pdu.no_asdu);
for asdu in &pdu.sav_asdu {
println!("SV ID: {}", asdu.sv_id);
println!("Sample Count: {}", asdu.smp_cnt);
println!("Number of samples: {}", asdu.sample.len());
// Process samples
for (i, sample) in asdu.sample.iter().enumerate() {
println!(" Sample {}: value={}, quality={}",
i, sample.value, sample.quality);
}
}
}
Err(e) => eprintln!("Decoding failed: {:?}", e),
}The library is optimized for high-performance industrial applications:
GOOSE encoding and decoding use rasn, a well-defined Rust implementation of ASN.1:
- GOOSE Encoding: ~2-5µs per message (depending on data complexity)
- GOOSE Decoding: ~1-3µs per message
- Round-trip (encode + decode): ~5-8µs
SMV encoding uses zero-copy techniques with exact buffer preallocation, achieving 3-4x performance improvement over naive implementations:
- Small packets (1 ASDU × 8 samples): ~418ns
- Realistic packets (8 ASDUs × 12 samples): ~4.35µs
- Large packets (8 ASDUs × 32 samples): ~10.98µs
- Small packets (1 ASDU × 8 samples): ~235ns
- Realistic packets (8 ASDUs × 12 samples): ~2.37µs
- Large packets (8 ASDUs × 32 samples): ~4.20µs
- Small packets: ~757ns
- Realistic packets: ~7.22µs
- Large packets: ~15.80µs
See benchmarking documentation for detailed performance analysis.
You can check performance with your hardware:
# Run all benchmarks
cargo bench
# Run specific benchmark
cargo bench --bench smv_decode
cargo bench --bench goose_codecThe library includes comprehensive unit tests (68+ tests):
# Run all tests
cargo test
# Run specific test
cargo test test_roundtrip_extreme_values
# Run with verbose output
cargo test -- --nocaptureCopyright © 2025 Jakob Vogelsang. All rights reserved.