Skip to content

Commit

Permalink
Add support for group 102 in the master (#335)
Browse files Browse the repository at this point in the history
* Add g102 to rust

* add tests for g102 in ReadHandler

* Add g102 to the FFI
  • Loading branch information
jadamcrain authored Dec 28, 2023
1 parent dc6ffee commit d07ef57
Show file tree
Hide file tree
Showing 17 changed files with 250 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ object FixedSizeField {
//enums
val commandStatus = FixedSizeField("status", EnumFieldType(CommandStatus))

// 8-bit integer
val unsignedByte = FixedSizeField("value", UInt8Field, Some(FieldAttribute.Value))
}

object VariableFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ object ObjectGroup {
Group60,
Group70,
Group80,
Group102,
Group110,
Group111,
)
Expand Down Expand Up @@ -66,6 +67,8 @@ object GroupType {
object StaticAnalogOutputStatus extends Static
object StaticOctetString extends Static

object StaticUnsignedInteger extends Static

object BinaryEvent extends Event
object BinaryOutputEvent extends Event
object BinaryOutputCommandEvent extends Event
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.gridio.dnp3.codegen.model.groups

import dev.gridio.dnp3.codegen.model._
import dev.gridio.dnp3.codegen.model.FixedSizeField._
import dev.gridio.dnp3.codegen.model.VariationNames._

object Group102 extends ObjectGroup {
def variations: List[Variation] = List(Group102Var0, Group102Var1)

def group: Byte = 102

def desc: String = "Unsigned Integer"

override def groupType: GroupType = GroupType.StaticUnsignedInteger
}

object Group102Var0 extends AnyVariation(Group102, 0)

object Group102Var1 extends FixedSize(Group102, 1, "8-bit")(unsignedByte)

Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ object RangedVariationModule extends Module {
case GroupType.StaticCounter => "counter"
case GroupType.StaticFrozenCounter => "frozen_counter"
case GroupType.AnalogInputDeadband => "analog_input_dead_band"
case GroupType.StaticUnsignedInteger => "unsigned_integer"
case _ => throw new Exception("unhandled variation")
}
}
Expand Down
4 changes: 4 additions & 0 deletions dnp3/src/app/gen/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ pub(crate) enum AllObjectsVariation {
Group60Var3,
Group60Var4,
Group80Var1,
Group102Var0,
Group102Var1,
Group110Var0,
Group111Var0,
}
Expand Down Expand Up @@ -243,6 +245,8 @@ impl AllObjectsVariation {
Variation::Group60Var3 => Some(AllObjectsVariation::Group60Var3),
Variation::Group60Var4 => Some(AllObjectsVariation::Group60Var4),
Variation::Group80Var1 => Some(AllObjectsVariation::Group80Var1),
Variation::Group102Var0 => Some(AllObjectsVariation::Group102Var0),
Variation::Group102Var1 => Some(AllObjectsVariation::Group102Var1),
Variation::Group110(0) => Some(AllObjectsVariation::Group110Var0),
Variation::Group111(0) => Some(AllObjectsVariation::Group111Var0),
_ => None,
Expand Down
20 changes: 20 additions & 0 deletions dnp3/src/app/gen/ranged.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ pub(crate) enum RangedVariation<'a> {
Group40Var4(RangedSequence<'a, Group40Var4>),
/// Internal Indications - Packed Format
Group80Var1(BitSequence<'a>),
/// Unsigned Integer - Any Variation
Group102Var0,
/// Unsigned Integer - 8-bit
Group102Var1(RangedSequence<'a, Group102Var1>),
/// Octet String - Sized by variation
Group110Var0,
Group110VarX(u8, RangedBytesSequence<'a>),
Expand Down Expand Up @@ -176,6 +180,8 @@ impl<'a> RangedVariation<'a> {
Variation::Group40Var3 => Ok(RangedVariation::Group40Var3(RangedSequence::parse(range, cursor)?)),
Variation::Group40Var4 => Ok(RangedVariation::Group40Var4(RangedSequence::parse(range, cursor)?)),
Variation::Group80Var1 => Ok(RangedVariation::Group80Var1(BitSequence::parse(range, cursor)?)),
Variation::Group102Var0 => Ok(RangedVariation::Group102Var0),
Variation::Group102Var1 => Ok(RangedVariation::Group102Var1(RangedSequence::parse(range, cursor)?)),
Variation::Group110(0) => Err(ObjectParseError::ZeroLengthOctetData),
Variation::Group110(x) => {
Ok(RangedVariation::Group110VarX(x, RangedBytesSequence::parse(x, range.get_start(), range.get_count(), cursor)?))
Expand Down Expand Up @@ -234,6 +240,8 @@ impl<'a> RangedVariation<'a> {
Variation::Group40Var3 => Ok(RangedVariation::Group40Var3(RangedSequence::empty())),
Variation::Group40Var4 => Ok(RangedVariation::Group40Var4(RangedSequence::empty())),
Variation::Group80Var1 => Ok(RangedVariation::Group80Var1(BitSequence::empty())),
Variation::Group102Var0 => Ok(RangedVariation::Group102Var0),
Variation::Group102Var1 => Ok(RangedVariation::Group102Var1(RangedSequence::empty())),
Variation::Group110(0) => Ok(RangedVariation::Group110Var0),
_ => Err(ObjectParseError::InvalidQualifierForVariation(v, qualifier)),
}
Expand Down Expand Up @@ -289,6 +297,8 @@ impl<'a> RangedVariation<'a> {
RangedVariation::Group40Var3(seq) => format_indexed_items(f, seq.iter()),
RangedVariation::Group40Var4(seq) => format_indexed_items(f, seq.iter()),
RangedVariation::Group80Var1(seq) => format_indexed_items(f, seq.iter()),
RangedVariation::Group102Var0 => Ok(()),
RangedVariation::Group102Var1(seq) => format_indexed_items(f, seq.iter()),
RangedVariation::Group110Var0 => Ok(()),
RangedVariation::Group110VarX(_, seq) => format_indexed_items(f, seq.iter().map(|(x, i)| (Bytes::new(x), i))),
}
Expand Down Expand Up @@ -589,6 +599,16 @@ impl<'a> RangedVariation<'a> {
RangedVariation::Group80Var1(_) => {
false // internal indications
}
RangedVariation::Group102Var0 => {
false // extraction not supported
}
RangedVariation::Group102Var1(seq) => {
handler.handle_unsigned_integer(
HeaderInfo::new(var, qualifier, false, false),
&mut seq.iter().map(|(v,i)| (v.into(), i))
);
true
}
RangedVariation::Group110Var0 => {
false
}
Expand Down
17 changes: 15 additions & 2 deletions dnp3/src/app/measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::time::Duration;

use crate::app::types::Timestamp;
use crate::app::variations::{
Group13Var1, Group13Var2, Group34Var1, Group34Var2, Group34Var3, Group43Var1, Group43Var2,
Group43Var3, Group43Var4, Group43Var5, Group43Var6, Group43Var7, Group43Var8,
Group102Var1, Group13Var1, Group13Var2, Group34Var1, Group34Var2, Group34Var3, Group43Var1,
Group43Var2, Group43Var3, Group43Var4, Group43Var5, Group43Var6, Group43Var7, Group43Var8,
};
use crate::util::bit::bits;
use crate::util::bit::BitMask;
Expand Down Expand Up @@ -360,6 +360,13 @@ pub enum AnalogInputDeadBand {
F32(f32),
}

/// Type corresponding to g102v1
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct UnsignedInteger {
/// Value of the type
pub value: u8,
}

impl From<Group34Var1> for AnalogInputDeadBand {
fn from(value: Group34Var1) -> Self {
Self::U16(value.value)
Expand Down Expand Up @@ -480,6 +487,12 @@ impl From<Group43Var8> for AnalogOutputCommandEvent {
}
}

impl From<Group102Var1> for UnsignedInteger {
fn from(obj: Group102Var1) -> Self {
Self { value: obj.value }
}
}

impl std::ops::BitOr<Flags> for Flags {
type Output = Flags;

Expand Down
45 changes: 45 additions & 0 deletions dnp3/src/app/variations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ pub enum Variation {
Group70Var8,
/// Internal Indications - Packed Format
Group80Var1,
/// Unsigned Integer - Any Variation
Group102Var0,
/// Unsigned Integer - 8-bit
Group102Var1,
/// Octet String - Sized by variation
Group110(u8),
/// Octet String Event - Sized by variation
Expand Down Expand Up @@ -506,6 +510,11 @@ impl Variation {
1 => Some(Variation::Group80Var1),
_ => None,
},
102 => match var {
0 => Some(Variation::Group102Var0),
1 => Some(Variation::Group102Var1),
_ => None,
},
110 => Some(Variation::Group110(var)),
111 => Some(Variation::Group111(var)),
_ => None,
Expand Down Expand Up @@ -645,6 +654,8 @@ impl Variation {
Variation::Group70Var7 => (70, 7),
Variation::Group70Var8 => (70, 8),
Variation::Group80Var1 => (80, 1),
Variation::Group102Var0 => (102, 0),
Variation::Group102Var1 => (102, 1),
Variation::Group110(x) => (110, x),
Variation::Group111(x) => (111, x),
}
Expand Down Expand Up @@ -783,12 +794,21 @@ impl Variation {
Variation::Group70Var7 => "File-control - file descriptor",
Variation::Group70Var8 => "File-control - file specification string",
Variation::Group80Var1 => "Internal Indications - Packed Format",
Variation::Group102Var0 => "Unsigned Integer - Any Variation",
Variation::Group102Var1 => "Unsigned Integer - 8-bit",
Variation::Group110(_) => "Octet String - Sized by variation",
Variation::Group111(_) => "Octet String Event - Sized by variation",
}
}
}

/// Unsigned Integer - 8-bit
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct Group102Var1 {
/// value field of the variation
pub(crate) value: u8,
}

/// Time Delay - Fine
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct Group52Var2 {
Expand Down Expand Up @@ -1671,6 +1691,21 @@ pub(crate) struct Group1Var2 {
}


impl FixedSize for Group102Var1 {
const SIZE: u8 = 1;
fn read(cursor: &mut ReadCursor) -> Result<Self, ReadError> {
Ok(
Group102Var1 {
value: cursor.read_u8()?,
}
)
}
fn write(&self, cursor: &mut WriteCursor) -> Result<(), WriteError> {
cursor.write_u8(self.value)?;
Ok(())
}
}

impl FixedSize for Group52Var2 {
const SIZE: u8 = 2;
fn read(cursor: &mut ReadCursor) -> Result<Self, ReadError> {
Expand Down Expand Up @@ -3329,6 +3364,12 @@ impl FixedSize for Group1Var2 {
}


impl std::fmt::Display for Group102Var1 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "value: {}", self.value)
}
}

impl std::fmt::Display for Group52Var2 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "time: {}", self.time)
Expand Down Expand Up @@ -3912,6 +3953,10 @@ impl std::fmt::Display for Group1Var2 {
}


impl FixedSizeVariation for Group102Var1 {
const VARIATION : Variation = Variation::Group102Var1;
}

impl FixedSizeVariation for Group52Var2 {
const VARIATION : Variation = Variation::Group52Var2;
}
Expand Down
35 changes: 35 additions & 0 deletions dnp3/src/master/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ mod test {
UnknownAttr(AttrSet, u8, String),
BinaryCommandEvent(Vec<(BinaryOutputCommandEvent, u16)>),
AnalogCommandEvent(Vec<(AnalogOutputCommandEvent, u16)>),
G102(Vec<(UnsignedInteger, u16)>),
}

#[derive(Default)]
Expand Down Expand Up @@ -246,6 +247,14 @@ mod test {
self.received.push(Header::AnalogCommandEvent(x.collect()))
}

fn handle_unsigned_integer(
&mut self,
_info: HeaderInfo,
x: &mut dyn Iterator<Item = (UnsignedInteger, u16)>,
) {
self.received.push(Header::G102(x.collect()))
}

fn handle_octet_string<'a>(
&mut self,
_info: HeaderInfo,
Expand Down Expand Up @@ -583,4 +592,30 @@ mod test {
)])]
);
}

#[test]
fn handles_g102v1() {
let mut handler = MockHandler::new();
let objects = HeaderCollection::parse(
FunctionCode::UnsolicitedResponse,
&[
102, // g43v2
1, 0x00, // 1 byte start/stop
0x07, // index 7,
0x08, // index 8
0xFE, 0xCA,
],
)
.unwrap();

extract_measurements_inner(objects, &mut handler);

assert_eq!(
&handler.pop(),
&[Header::G102(vec![
(UnsignedInteger { value: 0xFE }, 7),
(UnsignedInteger { value: 0xCA }, 8)
])]
);
}
}
8 changes: 8 additions & 0 deletions dnp3/src/master/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,14 @@ pub trait ReadHandler: Send + Sync {
) {
}

/// Process an object header of `UnsignedInteger` values
fn handle_unsigned_integer(
&mut self,
info: HeaderInfo,
iter: &mut dyn Iterator<Item = (UnsignedInteger, u16)>,
) {
}

/// Process an object header of octet string values
fn handle_octet_string<'a>(
&mut self,
Expand Down
6 changes: 6 additions & 0 deletions dnp3/src/outstation/database/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,9 @@ impl ReadHeader {
AllObjectsVariation::Group60Var4 => Some(EventReadHeader::Class3(None).into()),
// group 80
AllObjectsVariation::Group80Var1 => None,
// group 102
AllObjectsVariation::Group102Var0 => None,
AllObjectsVariation::Group102Var1 => None,
// group 110
AllObjectsVariation::Group110Var0 => Some(StaticReadHeader::OctetString(None).into()),
// group 111
Expand Down Expand Up @@ -1199,6 +1202,9 @@ impl ReadHeader {
),
// group 80
RangedVariation::Group80Var1(_) => None,
// group 102
RangedVariation::Group102Var0 => None,
RangedVariation::Group102Var1(_) => None,
// group 110
RangedVariation::Group110Var0 => {
Some(StaticReadHeader::OctetString(Some(range)).into())
Expand Down
12 changes: 12 additions & 0 deletions ffi/bindings/dotnet/examples/master/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,18 @@ void IReadHandler.HandleAnalogOutputCommandEvent(HeaderInfo info, ICollection<An
}
}

void IReadHandler.HandleUnsignedInteger(HeaderInfo info, ICollection<UnsignedInteger> values)
{
Console.WriteLine("Unsigned Integers:");
Console.WriteLine("Qualifier: " + info.Qualifier);
Console.WriteLine("Variation: " + info.Variation);

foreach (var val in values)
{
Console.WriteLine($"{val.Index}: Value={val.Value}");
}
}

public void HandleOctetString(HeaderInfo info, ICollection<OctetString> values)
{
Console.WriteLine("Octet Strings:");
Expand Down
Loading

0 comments on commit d07ef57

Please sign in to comment.