From 2155abf2953c35ce20d777cd2bf9c3ee3cbba8b2 Mon Sep 17 00:00:00 2001 From: Adam Crain Date: Wed, 20 Dec 2023 16:13:22 -0800 Subject: [PATCH 01/28] Retrieve tcp port from server (#331) * ServerHandle can now retrieve the local_addr * use ServerHandle::local_addr and port 0 in the perf example --- dnp3/examples/perf.rs | 27 ++++++++++++--------------- dnp3/src/tcp/outstation.rs | 28 +++++++++++++++++++--------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/dnp3/examples/perf.rs b/dnp3/examples/perf.rs index aee1e374..7d800059 100644 --- a/dnp3/examples/perf.rs +++ b/dnp3/examples/perf.rs @@ -33,10 +33,8 @@ enum Action { #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Cli { - #[clap(short, long, value_parser, default_value_t = 20000)] - port: u16, #[clap(long, value_parser, default_value_t = 1)] - sessions: u16, + sessions: usize, #[clap(short, long, value_parser, default_value_t = 100)] values: usize, #[clap(long, value_parser, default_value_t = 10)] @@ -77,9 +75,7 @@ async fn run(args: Cli) { .init(); } - let port_range: std::ops::Range = args.port..args.port + args.sessions; - - let mut harness = TestHarness::create(port_range, config).await; + let mut harness = TestHarness::create(args.sessions, config).await; println!("settings: {:?}", args); println!("starting up..."); @@ -123,10 +119,10 @@ struct TestHarness { } impl TestHarness { - async fn create(ports: std::ops::Range, config: TestConfig) -> Self { + async fn create(count: usize, config: TestConfig) -> Self { let mut pairs = Vec::new(); - for port in ports { - pairs.push(Pair::spawn(port, config).await) + for _ in 0..count { + pairs.push(Pair::spawn(config).await) } Self { pairs } } @@ -222,7 +218,7 @@ struct Pair { } impl Pair { - const LOCALHOST: std::net::IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); + const LOCALHOST: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); fn update_values(&mut self) { self.outstation.transaction(|db| { @@ -240,9 +236,10 @@ impl Pair { assert_eq!(self.rx.recv().await.unwrap(), self.values.len()); } - async fn spawn(port: u16, config: TestConfig) -> Self { - let (server, outstation) = Self::spawn_outstation(port, config).await; - let (master, assoc, measurements, rx) = Self::spawn_master(port, config).await; + async fn spawn(config: TestConfig) -> Self { + let (server, outstation) = Self::spawn_outstation(config).await; + let assigned_port = server.local_addr().unwrap().port(); + let (master, assoc, measurements, rx) = Self::spawn_master(assigned_port, config).await; Self { values: measurements, @@ -254,9 +251,9 @@ impl Pair { } } - async fn spawn_outstation(port: u16, config: TestConfig) -> (ServerHandle, OutstationHandle) { + async fn spawn_outstation(config: TestConfig) -> (ServerHandle, OutstationHandle) { let mut server = - Server::new_tcp_server(LinkErrorMode::Close, SocketAddr::new(Self::LOCALHOST, port)); + Server::new_tcp_server(LinkErrorMode::Close, SocketAddr::new(Self::LOCALHOST, 0)); let outstation = server .add_outstation( Self::get_outstation_config(config.outstation_level), diff --git a/dnp3/src/tcp/outstation.rs b/dnp3/src/tcp/outstation.rs index 95f89d61..66fac573 100644 --- a/dnp3/src/tcp/outstation.rs +++ b/dnp3/src/tcp/outstation.rs @@ -1,3 +1,4 @@ +use std::net::SocketAddr; use tracing::Instrument; use crate::app::{ConnectStrategy, Listener, Shutdown}; @@ -71,16 +72,26 @@ struct OutstationInfo { pub struct Server { link_error_mode: LinkErrorMode, connection_id: u64, - address: std::net::SocketAddr, + address: SocketAddr, outstations: Vec, connection_handler: ServerConnectionHandler, } /// Handle to a running server. Dropping the handle, shuts down the server. pub struct ServerHandle { + addr: Option, _tx: tokio::sync::oneshot::Sender<()>, } +impl ServerHandle { + /// Returns the local address to which this server is bound. + /// + /// This can be useful, for example, when binding to port 0 to figure out which port was actually bound. + pub fn local_addr(&self) -> Option { + self.addr + } +} + enum ServerConnectionHandler { Tcp, #[cfg(feature = "tls")] @@ -100,7 +111,7 @@ impl ServerConnectionHandler { impl Server { /// create a TCP server builder object that will eventually be bound /// to the specified address - pub fn new_tcp_server(link_error_mode: LinkErrorMode, address: std::net::SocketAddr) -> Self { + pub fn new_tcp_server(link_error_mode: LinkErrorMode, address: SocketAddr) -> Self { Self { link_error_mode, connection_id: 0, @@ -114,7 +125,7 @@ impl Server { #[cfg(feature = "tls")] pub fn new_tls_server( link_error_mode: LinkErrorMode, - address: std::net::SocketAddr, + address: SocketAddr, tls_config: crate::tcp::tls::TlsServerConfig, ) -> Self { Self { @@ -206,6 +217,8 @@ impl Server { ) -> Result<(ServerHandle, impl std::future::Future), tokio::io::Error> { let listener = tokio::net::TcpListener::bind(self.address).await?; + let addr = listener.local_addr().ok(); + let (tx, rx) = tokio::sync::oneshot::channel(); let task = async move { @@ -215,7 +228,7 @@ impl Server { .await }; - let handle = ServerHandle { _tx: tx }; + let handle = ServerHandle { addr, _tx: tx }; Ok((handle, task)) } @@ -224,6 +237,7 @@ impl Server { /// task onto the Tokio runtime. Returns a ServerHandle that will shut down the server and all /// associated outstations when dropped. /// + /// /// This must be called from within the Tokio runtime pub async fn bind(self) -> Result { let (handle, future) = self.bind_no_spawn().await?; @@ -279,11 +293,7 @@ impl Server { } } - async fn process_connection( - &mut self, - stream: tokio::net::TcpStream, - addr: std::net::SocketAddr, - ) { + async fn process_connection(&mut self, stream: tokio::net::TcpStream, addr: SocketAddr) { let id = self.connection_id; self.connection_id = self.connection_id.wrapping_add(1); From b21b9b29bc1b0ddde4053a9d279aec719f239bbb Mon Sep 17 00:00:00 2001 From: Adam Crain Date: Sat, 23 Dec 2023 09:41:19 -0800 Subject: [PATCH 02/28] Master station support for command events: g13 and g43 (#332) --- dnp3/codegen/pom.xml | 4 + .../scala/dev/gridio/dnp3/codegen/Main.scala | 6 +- .../dnp3/codegen/model/ObjectGroup.scala | 4 +- .../modules/PrefixedVariationModule.scala | 4 +- dnp3/src/app/gen/all.rs | 20 + dnp3/src/app/gen/count.rs | 40 ++ dnp3/src/app/gen/prefixed.rs | 120 +++++ dnp3/src/app/measurement.rs | 169 ++++++- dnp3/src/app/variations.rs | 428 ++++++++++++++++++ dnp3/src/master/extract.rs | 103 +++++ dnp3/src/master/handler.rs | 30 +- dnp3/src/outstation/database/read.rs | 22 + .../dotnet/examples/master/Program.cs | 26 ++ ffi/dnp3-ffi/src/handler.rs | 70 +++ ffi/dnp3-ffi/src/lib.rs | 2 +- ffi/dnp3-ffi/src/master/functions.rs | 6 +- ffi/dnp3-ffi/src/request.rs | 22 + ffi/dnp3-schema/src/master/read_handler.rs | 32 ++ ffi/dnp3-schema/src/outstation.rs | 51 +-- ffi/dnp3-schema/src/shared.rs | 114 +++++ ffi/dnp3-schema/src/variation.rs | 20 +- 21 files changed, 1217 insertions(+), 76 deletions(-) diff --git a/dnp3/codegen/pom.xml b/dnp3/codegen/pom.xml index c3de4dad..9f0f1fcb 100644 --- a/dnp3/codegen/pom.xml +++ b/dnp3/codegen/pom.xml @@ -41,6 +41,10 @@ org.apache.maven.plugins maven-compiler-plugin 3.0 + + 7 + 7 + org.apache.maven.plugins diff --git a/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/Main.scala b/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/Main.scala index 6c9e5b41..6024be33 100644 --- a/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/Main.scala +++ b/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/Main.scala @@ -7,10 +7,10 @@ import dev.gridio.dnp3.codegen.render.modules._ object Main { - val appPath: Path = FileSystems.getDefault.getPath("../src/app/") - val implPath: Path = appPath.resolve("gen"); + private val appPath: Path = FileSystems.getDefault.getPath("../src/app/") + private val implPath: Path = appPath.resolve("gen"); - object CommonUseStatements extends Module { + private object CommonUseStatements extends Module { override def lines(implicit indentation: Indentation): Iterator[String] = { "use crate::app::parse::traits::{FixedSize, FixedSizeVariation};".eol ++ "use crate::app::control::{CommandStatus, ControlCode};".eol ++ diff --git a/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/model/ObjectGroup.scala b/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/model/ObjectGroup.scala index 0c6b8b53..bed21de4 100644 --- a/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/model/ObjectGroup.scala +++ b/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/model/ObjectGroup.scala @@ -15,6 +15,7 @@ object ObjectGroup { Group10, Group11, Group12, + Group13, Group20, Group21, Group22, @@ -27,6 +28,7 @@ object ObjectGroup { Group40, Group41, Group42, + Group43, Group50, Group51, Group52, @@ -97,7 +99,5 @@ trait ObjectGroup { def desc: String - def hasSizedObjects: Boolean = variations.exists(x => x.isInstanceOf[FixedSizeField]) - def groupType : GroupType } diff --git a/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/render/modules/PrefixedVariationModule.scala b/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/render/modules/PrefixedVariationModule.scala index 014aa062..296d1630 100644 --- a/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/render/modules/PrefixedVariationModule.scala +++ b/dnp3/codegen/src/main/scala/dev/gridio/dnp3/codegen/render/modules/PrefixedVariationModule.scala @@ -97,8 +97,8 @@ object PrefixedVariationModule extends Module { case GroupType.AnalogEvent => "analog_input" case GroupType.FrozenAnalogEvent => "frozen_analog_input" case GroupType.AnalogOutputEvent => "analog_output_status" - case GroupType.AnalogOutputCommandEvent => "analog_output_command" - case GroupType.BinaryOutputCommandEvent => "binary_output_command" + case GroupType.AnalogOutputCommandEvent => "analog_output_command_event" + case GroupType.BinaryOutputCommandEvent => "binary_output_command_event" case _ => throw new Exception("unhandled variation") } diff --git a/dnp3/src/app/gen/all.rs b/dnp3/src/app/gen/all.rs index 74ec5a91..85c6b05c 100644 --- a/dnp3/src/app/gen/all.rs +++ b/dnp3/src/app/gen/all.rs @@ -36,6 +36,8 @@ pub(crate) enum AllObjectsVariation { Group11Var0, Group11Var1, Group11Var2, + Group13Var1, + Group13Var2, Group20Var0, Group20Var1, Group20Var2, @@ -110,6 +112,14 @@ pub(crate) enum AllObjectsVariation { Group42Var6, Group42Var7, Group42Var8, + Group43Var1, + Group43Var2, + Group43Var3, + Group43Var4, + Group43Var5, + Group43Var6, + Group43Var7, + Group43Var8, Group60Var1, Group60Var2, Group60Var3, @@ -144,6 +154,8 @@ impl AllObjectsVariation { Variation::Group11Var0 => Some(AllObjectsVariation::Group11Var0), Variation::Group11Var1 => Some(AllObjectsVariation::Group11Var1), Variation::Group11Var2 => Some(AllObjectsVariation::Group11Var2), + Variation::Group13Var1 => Some(AllObjectsVariation::Group13Var1), + Variation::Group13Var2 => Some(AllObjectsVariation::Group13Var2), Variation::Group20Var0 => Some(AllObjectsVariation::Group20Var0), Variation::Group20Var1 => Some(AllObjectsVariation::Group20Var1), Variation::Group20Var2 => Some(AllObjectsVariation::Group20Var2), @@ -218,6 +230,14 @@ impl AllObjectsVariation { Variation::Group42Var6 => Some(AllObjectsVariation::Group42Var6), Variation::Group42Var7 => Some(AllObjectsVariation::Group42Var7), Variation::Group42Var8 => Some(AllObjectsVariation::Group42Var8), + Variation::Group43Var1 => Some(AllObjectsVariation::Group43Var1), + Variation::Group43Var2 => Some(AllObjectsVariation::Group43Var2), + Variation::Group43Var3 => Some(AllObjectsVariation::Group43Var3), + Variation::Group43Var4 => Some(AllObjectsVariation::Group43Var4), + Variation::Group43Var5 => Some(AllObjectsVariation::Group43Var5), + Variation::Group43Var6 => Some(AllObjectsVariation::Group43Var6), + Variation::Group43Var7 => Some(AllObjectsVariation::Group43Var7), + Variation::Group43Var8 => Some(AllObjectsVariation::Group43Var8), Variation::Group60Var1 => Some(AllObjectsVariation::Group60Var1), Variation::Group60Var2 => Some(AllObjectsVariation::Group60Var2), Variation::Group60Var3 => Some(AllObjectsVariation::Group60Var3), diff --git a/dnp3/src/app/gen/count.rs b/dnp3/src/app/gen/count.rs index 5e7d9ebd..58703130 100644 --- a/dnp3/src/app/gen/count.rs +++ b/dnp3/src/app/gen/count.rs @@ -42,6 +42,10 @@ pub(crate) enum CountVariation<'a> { Group11Var1, /// Binary Output Event - Output Status With Time Group11Var2, + /// Binary Output Command Event - Without Time + Group13Var1, + /// Binary Output Command Event - With Time + Group13Var2, /// Counter Event - Any Variation Group22Var0, /// Counter Event - 32-bit With Flag @@ -116,6 +120,22 @@ pub(crate) enum CountVariation<'a> { Group42Var7, /// Analog Output Event - Double-precision With Flag and Time Group42Var8, + /// Analog Output Command Event - 32-bit + Group43Var1, + /// Analog Output Command Event - 16-bit + Group43Var2, + /// Analog Output Command Event - 32-bit With Time + Group43Var3, + /// Analog Output Command Event - 16-bit With Time + Group43Var4, + /// Analog Output Command Event - Single-precision + Group43Var5, + /// Analog Output Command Event - Double-precision + Group43Var6, + /// Analog Output Command Event - Single-precision With Time + Group43Var7, + /// Analog Output Command Event - Double-precision With Time + Group43Var8, /// Time and Date - Absolute Time Group50Var1(CountSequence<'a, Group50Var1>), /// Time and Date - Absolute time and interval @@ -157,6 +177,8 @@ impl<'a> CountVariation<'a> { Variation::Group11Var0 => Ok(CountVariation::Group11Var0), Variation::Group11Var1 => Ok(CountVariation::Group11Var1), Variation::Group11Var2 => Ok(CountVariation::Group11Var2), + Variation::Group13Var1 => Ok(CountVariation::Group13Var1), + Variation::Group13Var2 => Ok(CountVariation::Group13Var2), Variation::Group22Var0 => Ok(CountVariation::Group22Var0), Variation::Group22Var1 => Ok(CountVariation::Group22Var1), Variation::Group22Var2 => Ok(CountVariation::Group22Var2), @@ -194,6 +216,14 @@ impl<'a> CountVariation<'a> { Variation::Group42Var6 => Ok(CountVariation::Group42Var6), Variation::Group42Var7 => Ok(CountVariation::Group42Var7), Variation::Group42Var8 => Ok(CountVariation::Group42Var8), + Variation::Group43Var1 => Ok(CountVariation::Group43Var1), + Variation::Group43Var2 => Ok(CountVariation::Group43Var2), + Variation::Group43Var3 => Ok(CountVariation::Group43Var3), + Variation::Group43Var4 => Ok(CountVariation::Group43Var4), + Variation::Group43Var5 => Ok(CountVariation::Group43Var5), + Variation::Group43Var6 => Ok(CountVariation::Group43Var6), + Variation::Group43Var7 => Ok(CountVariation::Group43Var7), + Variation::Group43Var8 => Ok(CountVariation::Group43Var8), Variation::Group50Var1 => Ok(CountVariation::Group50Var1(CountSequence::parse(count, cursor)?)), Variation::Group50Var2 => Ok(CountVariation::Group50Var2(CountSequence::parse(count, cursor)?)), Variation::Group50Var3 => Ok(CountVariation::Group50Var3(CountSequence::parse(count, cursor)?)), @@ -224,6 +254,8 @@ impl<'a> CountVariation<'a> { CountVariation::Group11Var0 => Ok(()), CountVariation::Group11Var1 => Ok(()), CountVariation::Group11Var2 => Ok(()), + CountVariation::Group13Var1 => Ok(()), + CountVariation::Group13Var2 => Ok(()), CountVariation::Group22Var0 => Ok(()), CountVariation::Group22Var1 => Ok(()), CountVariation::Group22Var2 => Ok(()), @@ -261,6 +293,14 @@ impl<'a> CountVariation<'a> { CountVariation::Group42Var6 => Ok(()), CountVariation::Group42Var7 => Ok(()), CountVariation::Group42Var8 => Ok(()), + CountVariation::Group43Var1 => Ok(()), + CountVariation::Group43Var2 => Ok(()), + CountVariation::Group43Var3 => Ok(()), + CountVariation::Group43Var4 => Ok(()), + CountVariation::Group43Var5 => Ok(()), + CountVariation::Group43Var6 => Ok(()), + CountVariation::Group43Var7 => Ok(()), + CountVariation::Group43Var8 => Ok(()), CountVariation::Group50Var1(seq) => format_count_of_items(f, seq.iter()), CountVariation::Group50Var2(seq) => format_count_of_items(f, seq.iter()), CountVariation::Group50Var3(seq) => format_count_of_items(f, seq.iter()), diff --git a/dnp3/src/app/gen/prefixed.rs b/dnp3/src/app/gen/prefixed.rs index e3cc45bd..037bc4d7 100644 --- a/dnp3/src/app/gen/prefixed.rs +++ b/dnp3/src/app/gen/prefixed.rs @@ -44,6 +44,10 @@ pub(crate) enum PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt: Group11Var2(CountSequence<'a, Prefix>), /// Binary Command - Control Relay Output Block Group12Var1(CountSequence<'a, Prefix>), + /// Binary Output Command Event - Without Time + Group13Var1(CountSequence<'a, Prefix>), + /// Binary Output Command Event - With Time + Group13Var2(CountSequence<'a, Prefix>), /// Counter Event - 32-bit With Flag Group22Var1(CountSequence<'a, Prefix>), /// Counter Event - 16-bit With Flag @@ -122,6 +126,22 @@ pub(crate) enum PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt: Group42Var7(CountSequence<'a, Prefix>), /// Analog Output Event - Double-precision With Flag and Time Group42Var8(CountSequence<'a, Prefix>), + /// Analog Output Command Event - 32-bit + Group43Var1(CountSequence<'a, Prefix>), + /// Analog Output Command Event - 16-bit + Group43Var2(CountSequence<'a, Prefix>), + /// Analog Output Command Event - 32-bit With Time + Group43Var3(CountSequence<'a, Prefix>), + /// Analog Output Command Event - 16-bit With Time + Group43Var4(CountSequence<'a, Prefix>), + /// Analog Output Command Event - Single-precision + Group43Var5(CountSequence<'a, Prefix>), + /// Analog Output Command Event - Double-precision + Group43Var6(CountSequence<'a, Prefix>), + /// Analog Output Command Event - Single-precision With Time + Group43Var7(CountSequence<'a, Prefix>), + /// Analog Output Command Event - Double-precision With Time + Group43Var8(CountSequence<'a, Prefix>), /// Octet String Event - Sized by variation Group111VarX(u8, PrefixedBytesSequence<'a, I>), } @@ -139,6 +159,8 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis Variation::Group11Var1 => Ok(PrefixedVariation::Group11Var1(CountSequence::parse(count, cursor)?)), Variation::Group11Var2 => Ok(PrefixedVariation::Group11Var2(CountSequence::parse(count, cursor)?)), Variation::Group12Var1 => Ok(PrefixedVariation::Group12Var1(CountSequence::parse(count, cursor)?)), + Variation::Group13Var1 => Ok(PrefixedVariation::Group13Var1(CountSequence::parse(count, cursor)?)), + Variation::Group13Var2 => Ok(PrefixedVariation::Group13Var2(CountSequence::parse(count, cursor)?)), Variation::Group22Var1 => Ok(PrefixedVariation::Group22Var1(CountSequence::parse(count, cursor)?)), Variation::Group22Var2 => Ok(PrefixedVariation::Group22Var2(CountSequence::parse(count, cursor)?)), Variation::Group22Var5 => Ok(PrefixedVariation::Group22Var5(CountSequence::parse(count, cursor)?)), @@ -178,6 +200,14 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis Variation::Group42Var6 => Ok(PrefixedVariation::Group42Var6(CountSequence::parse(count, cursor)?)), Variation::Group42Var7 => Ok(PrefixedVariation::Group42Var7(CountSequence::parse(count, cursor)?)), Variation::Group42Var8 => Ok(PrefixedVariation::Group42Var8(CountSequence::parse(count, cursor)?)), + Variation::Group43Var1 => Ok(PrefixedVariation::Group43Var1(CountSequence::parse(count, cursor)?)), + Variation::Group43Var2 => Ok(PrefixedVariation::Group43Var2(CountSequence::parse(count, cursor)?)), + Variation::Group43Var3 => Ok(PrefixedVariation::Group43Var3(CountSequence::parse(count, cursor)?)), + Variation::Group43Var4 => Ok(PrefixedVariation::Group43Var4(CountSequence::parse(count, cursor)?)), + Variation::Group43Var5 => Ok(PrefixedVariation::Group43Var5(CountSequence::parse(count, cursor)?)), + Variation::Group43Var6 => Ok(PrefixedVariation::Group43Var6(CountSequence::parse(count, cursor)?)), + Variation::Group43Var7 => Ok(PrefixedVariation::Group43Var7(CountSequence::parse(count, cursor)?)), + Variation::Group43Var8 => Ok(PrefixedVariation::Group43Var8(CountSequence::parse(count, cursor)?)), Variation::Group111(0) => Err(ObjectParseError::ZeroLengthOctetData), Variation::Group111(x) => Ok(PrefixedVariation::Group111VarX(x, PrefixedBytesSequence::parse(x, count, cursor)?)), _ => Err(ObjectParseError::InvalidQualifierForVariation(v, I::COUNT_AND_PREFIX_QUALIFIER)), @@ -196,6 +226,8 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis PrefixedVariation::Group11Var1(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group11Var2(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group12Var1(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group13Var1(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group13Var2(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group22Var1(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group22Var2(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group22Var5(seq) => format_prefixed_items(f, seq.iter()), @@ -235,6 +267,14 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis PrefixedVariation::Group42Var6(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group42Var7(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group42Var8(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var1(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var2(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var3(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var4(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var5(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var6(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var7(seq) => format_prefixed_items(f, seq.iter()), + PrefixedVariation::Group43Var8(seq) => format_prefixed_items(f, seq.iter()), PrefixedVariation::Group111VarX(_, seq) => format_indexed_items(f, seq.iter().map(|(x, i)| (Bytes::new(x), i))), } } @@ -305,6 +345,20 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis PrefixedVariation::Group12Var1(_) => { false // command } + PrefixedVariation::Group13Var1(seq) => { + handler.handle_binary_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group13Var2(seq) => { + handler.handle_binary_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } PrefixedVariation::Group22Var1(seq) => { handler.handle_counter( self.get_header_info(), @@ -550,6 +604,62 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis ); true } + PrefixedVariation::Group43Var1(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var2(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var3(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var4(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var5(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var6(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var7(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } + PrefixedVariation::Group43Var8(seq) => { + handler.handle_analog_output_command_event( + self.get_header_info(), + &mut seq.iter().map(|x| (x.value.into(), x.index.widen_to_u16())) + ); + true + } PrefixedVariation::Group111VarX(_, seq) => { handler.handle_octet_string( self.get_header_info(), @@ -572,6 +682,8 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis PrefixedVariation::Group11Var1(_) => HeaderInfo::new(Variation::Group11Var1, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group11Var2(_) => HeaderInfo::new(Variation::Group11Var2, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group12Var1(_) => HeaderInfo::new(Variation::Group12Var1, I::COUNT_AND_PREFIX_QUALIFIER, false, false), + PrefixedVariation::Group13Var1(_) => HeaderInfo::new(Variation::Group13Var1, I::COUNT_AND_PREFIX_QUALIFIER, true, true), + PrefixedVariation::Group13Var2(_) => HeaderInfo::new(Variation::Group13Var2, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group22Var1(_) => HeaderInfo::new(Variation::Group22Var1, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group22Var2(_) => HeaderInfo::new(Variation::Group22Var2, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group22Var5(_) => HeaderInfo::new(Variation::Group22Var5, I::COUNT_AND_PREFIX_QUALIFIER, true, true), @@ -611,6 +723,14 @@ impl<'a, I> PrefixedVariation<'a, I> where I : FixedSize + Index + std::fmt::Dis PrefixedVariation::Group42Var6(_) => HeaderInfo::new(Variation::Group42Var6, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group42Var7(_) => HeaderInfo::new(Variation::Group42Var7, I::COUNT_AND_PREFIX_QUALIFIER, true, true), PrefixedVariation::Group42Var8(_) => HeaderInfo::new(Variation::Group42Var8, I::COUNT_AND_PREFIX_QUALIFIER, true, true), + PrefixedVariation::Group43Var1(_) => HeaderInfo::new(Variation::Group43Var1, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var2(_) => HeaderInfo::new(Variation::Group43Var2, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var3(_) => HeaderInfo::new(Variation::Group43Var3, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var4(_) => HeaderInfo::new(Variation::Group43Var4, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var5(_) => HeaderInfo::new(Variation::Group43Var5, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var6(_) => HeaderInfo::new(Variation::Group43Var6, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var7(_) => HeaderInfo::new(Variation::Group43Var7, I::COUNT_AND_PREFIX_QUALIFIER, true, false), + PrefixedVariation::Group43Var8(_) => HeaderInfo::new(Variation::Group43Var8, I::COUNT_AND_PREFIX_QUALIFIER, true, false), PrefixedVariation::Group111VarX(x, _) => HeaderInfo::new(Variation::Group111(*x), I::COUNT_AND_PREFIX_QUALIFIER, true, false), } } diff --git a/dnp3/src/app/measurement.rs b/dnp3/src/app/measurement.rs index d61ec634..0443a0f3 100644 --- a/dnp3/src/app/measurement.rs +++ b/dnp3/src/app/measurement.rs @@ -1,7 +1,11 @@ +use crate::app::control::CommandStatus; use std::time::Duration; use crate::app::types::Timestamp; -use crate::app::variations::{Group34Var1, Group34Var2, Group34Var3}; +use crate::app::variations::{ + Group13Var1, Group13Var2, Group34Var1, Group34Var2, Group34Var3, Group43Var1, Group43Var2, + Group43Var3, Group43Var4, Group43Var5, Group43Var6, Group43Var7, Group43Var8, +}; use crate::util::bit::bits; use crate::util::bit::BitMask; use crate::util::bit::Bitfield; @@ -284,6 +288,67 @@ impl FrozenAnalogInput { } } +/// Event transferred from master to outstation when the outstation receives a control. +/// The primary use case of these objects are that they allow one master see that commands +/// were issued to an outstation from another master. +/// +/// Maps to group 13 variations 1 and 2. +/// +/// These objects are part of subset level 4 and are not commonly used. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct BinaryOutputCommandEvent { + /// commanded state of the binary output + /// + /// From the spec: + /// + /// 0 = Latch Off / Trip / NULL, 1 = Latch On / Close. Where the + /// commanded state is unknown, the commanded state flag shall be 0. + pub commanded_state: bool, + /// status from processing the command that triggered this event + pub status: CommandStatus, + /// associated time + pub time: Option