You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by jf...@apache.org on 2022/06/02 21:37:44 UTC
[plc4x] branch feature/plc4rs updated: Added macro to generate enums
This is an automated email from the ASF dual-hosted git repository.
jfeinauer pushed a commit to branch feature/plc4rs
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/plc4rs by this push:
new 64d8fb61db Added macro to generate enums
64d8fb61db is described below
commit 64d8fb61dbc8f63befcbf43abc093278ab182c6d
Author: julian <j....@pragmaticminds.de>
AuthorDate: Thu Jun 2 23:37:34 2022 +0200
Added macro to generate enums
---
plc4rust/Cargo.toml | 2 +
plc4rust/src/enum.rs | 237 +++++++++++++++++++++++++++++++
plc4rust/src/lib.rs | 1 +
plc4rust/src/modbus/mod.rs | 1 +
plc4rust/src/modbus/modbus_error_code.rs | 47 ++++++
plc4rust/src/modbus/modbus_pdu.rs | 16 ++-
plc4rust/src/read_buffer.rs | 6 +-
7 files changed, 308 insertions(+), 2 deletions(-)
diff --git a/plc4rust/Cargo.toml b/plc4rust/Cargo.toml
index 2ea0675b8b..a75b4bf00c 100644
--- a/plc4rust/Cargo.toml
+++ b/plc4rust/Cargo.toml
@@ -23,3 +23,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+paste = "1.0.7"
+mockall = "0.11.1"
\ No newline at end of file
diff --git a/plc4rust/src/enum.rs b/plc4rust/src/enum.rs
new file mode 100644
index 0000000000..9281c790f8
--- /dev/null
+++ b/plc4rust/src/enum.rs
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// [enum uint 8 ModbusErrorCode
+// ['1' ILLEGAL_FUNCTION]
+// ['2' ILLEGAL_DATA_ADDRESS]
+// ['3' ILLEGAL_DATA_VALUE]
+// ['4' SLAVE_DEVICE_FAILURE]
+// ['5' ACKNOWLEDGE]
+// ['6' SLAVE_DEVICE_BUSY]
+// ['7' NEGATIVE_ACKNOWLEDGE]
+// ['8' MEMORY_PARITY_ERROR]
+// ['10' GATEWAY_PATH_UNAVAILABLE]
+// ['11' GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND]
+// ]
+
+// Necessary imports
+// use std::io::{Error, ErrorKind, Read, Write};
+// use crate::{Message, NoOption, ReadBuffer, WriteBuffer};
+#[macro_export]
+macro_rules! plc_enum {
+ (enum $type:ty : $name:ident $([$pat:expr => $branch:ident]),*) => {
+ #[derive(Copy, Clone, PartialEq, Debug)]
+ #[allow(non_camel_case_types)]
+ pub enum $name {
+ $(
+ $branch,
+ )* // Repeat for each `expr` passed to the macro
+ }
+
+ impl TryFrom<$type> for $name {
+ type Error = ();
+
+ fn try_from(value: $type) -> Result<Self, Self::Error> {
+ match value {
+ $(
+ $pat => {
+ Ok($name::$branch)
+ },
+ )* // Repeat for each `expr` passed to the macro
+ _ => {
+ Err(())
+ }
+ }
+ }
+ }
+
+ impl Into<$type> for $name {
+ fn into(self) -> $type {
+ match self {
+ $(
+ $name::$branch => {
+ $pat
+ },
+ )*
+ }
+ }
+ }
+
+ impl Message for $name {
+ type M = $name;
+ type P = NoOption;
+
+ fn get_length_in_bits(&self) -> u32 {
+ todo!()
+ }
+
+ fn serialize<T: Write>(&self, writer: &mut WriteBuffer<T>) -> Result<usize, Error> {
+ use paste::paste;
+ paste! {
+ writer.[<write_ $type>]((*self).into())
+ }
+ }
+
+ fn parse<T: Read>(reader: &mut ReadBuffer<T>, parameter: Option<Self::P>) -> Result<Self::M, Error> {
+ assert!(parameter.is_none());
+ use paste::paste;
+ paste! {
+ let result = reader.[<read_ $type>]()?;
+ }
+ match $name::try_from(result) {
+ Ok(result) => {
+ Ok(result)
+ }
+ Err(_) => {
+ Err(Error::new(ErrorKind::InvalidInput, format!("Cannot parse {}", result)))
+ }
+ }
+ }
+ }
+ };
+ (enum $bits:expr => $type:ty : $name:ident $([$pat:expr => $branch:ident]),*) => {
+ #[derive(Copy, Clone, PartialEq, Debug)]
+ #[allow(non_camel_case_types)]
+ pub enum $name {
+ $(
+ $branch,
+ )* // Repeat for each `expr` passed to the macro
+ }
+
+ impl TryFrom<$type> for $name {
+ type Error = ();
+
+ fn try_from(value: $type) -> Result<Self, Self::Error> {
+ match value {
+ $(
+ $pat => {
+ Ok($name::$branch)
+ },
+ )* // Repeat for each `expr` passed to the macro
+ _ => {
+ Err(())
+ }
+ }
+ }
+ }
+
+ impl Into<$type> for $name {
+ fn into(self) -> $type {
+ match self {
+ $(
+ $name::$branch => {
+ $pat
+ },
+ )*
+ }
+ }
+ }
+
+ impl Message for $name {
+ type M = $name;
+ type P = NoOption;
+
+ fn get_length_in_bits(&self) -> u32 {
+ todo!()
+ }
+
+ fn serialize<T: Write>(&self, writer: &mut WriteBuffer<T>) -> Result<usize, Error> {
+ let x: $type = (*self).into();
+ writer.write_u_n($bits, x as u64)
+ }
+
+ fn parse<T: Read>(reader: &mut ReadBuffer<T>, parameter: Option<Self::P>) -> Result<Self::M, Error> {
+ assert!(parameter.is_none());
+ let result = reader.read_u_n($bits)?;
+ match $name::try_from(result as $type) {
+ Ok(result) => {
+ Ok(result)
+ }
+ Err(_) => {
+ Err(Error::new(ErrorKind::InvalidInput, format!("Cannot parse {}", result)))
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::io::{Error, ErrorKind, Read, Write};
+ use crate::{Endianess, Message, NoOption, ReadBuffer, WriteBuffer};
+
+ #[test]
+ fn test_macro() {
+ plc_enum!
+ [enum u8 : ModbusErrorCode
+ [1 => ILLEGAL_FUNCTION],
+ [2 => ILLEGAL_DATA_ADDRESS],
+ [3 => ILLEGAL_DATA_VALUE],
+ [4 => SLAVE_DEVICE_FAILURE],
+ [5 => ACKNOWLEDGE],
+ [6 => SLAVE_DEVICE_BUSY],
+ [7 => NEGATIVE_ACKNOWLEDGE],
+ [8 => MEMORY_PARITY_ERROR],
+ [10 => GATEWAY_PATH_UNAVAILABLE],
+ [11 => GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND]
+ ];
+
+ plc_enum![enum u16 : ModbusErrorCode2
+ [1 => ILLEGAL_FUNCTION],
+ [2 => ILLEGAL_DATA_ADDRESS],
+ [3 => ILLEGAL_DATA_VALUE],
+ [4 => SLAVE_DEVICE_FAILURE],
+ [5 => ACKNOWLEDGE],
+ [6 => SLAVE_DEVICE_BUSY],
+ [7 => NEGATIVE_ACKNOWLEDGE],
+ [8 => MEMORY_PARITY_ERROR],
+ [10 => GATEWAY_PATH_UNAVAILABLE],
+ [11 => GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND]
+ ];
+
+ let x = ModbusErrorCode2::ILLEGAL_FUNCTION;
+ let y: u16 = x.into();
+
+ assert_eq!(1, y);
+ }
+
+ #[test]
+ fn test_u7() {
+ // [enum uint 7 ModbusDeviceInformationConformityLevel
+ // ['0x01' BASIC_STREAM_ONLY ]
+ // ['0x02' REGULAR_STREAM_ONLY ]
+ // ['0x03' EXTENDED_STREAM_ONLY]
+ // ]
+ plc_enum![enum 7 => u8 : ModbusDeviceInformationConformityLevel
+ [0x01 => BASIC_STREAM_ONLY],
+ [0x02 => REGULAR_STREAM_ONLY ],
+ [0x03 => EXTENDED_STREAM_ONLY]
+ ];
+
+ let x = ModbusDeviceInformationConformityLevel::BASIC_STREAM_ONLY;
+ let y: u8 = x.into();
+ assert_eq!(0x01, y);
+
+ let r: Vec<u8> = vec![];
+ let mut reader = ReadBuffer::new(Endianess::BigEndian, &*r);
+
+ let y = ModbusDeviceInformationConformityLevel::parse(&mut reader, None);
+ }
+}
\ No newline at end of file
diff --git a/plc4rust/src/lib.rs b/plc4rust/src/lib.rs
index 426d5aa129..aabbec7d5c 100644
--- a/plc4rust/src/lib.rs
+++ b/plc4rust/src/lib.rs
@@ -25,6 +25,7 @@ use crate::write_buffer::WriteBuffer;
mod write_buffer;
mod modbus;
mod read_buffer;
+mod r#enum;
#[allow(dead_code)]
pub enum Endianess {
diff --git a/plc4rust/src/modbus/mod.rs b/plc4rust/src/modbus/mod.rs
index c5a38cc1f8..f59f077eac 100644
--- a/plc4rust/src/modbus/mod.rs
+++ b/plc4rust/src/modbus/mod.rs
@@ -27,6 +27,7 @@ mod modbus_adu;
mod modbus_pdu;
mod modbus_pdu_read_file_record_request_item;
mod modbus_pdu_write_file_record_response_item;
+mod modbus_error_code;
pub use driver_type::DriverType;
pub use modbus_adu::ModbusADU;
diff --git a/plc4rust/src/modbus/modbus_error_code.rs b/plc4rust/src/modbus/modbus_error_code.rs
new file mode 100644
index 0000000000..a310ce98f1
--- /dev/null
+++ b/plc4rust/src/modbus/modbus_error_code.rs
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use std::io::{Error, ErrorKind, Read, Write};
+use crate::{Message, NoOption, plc_enum, ReadBuffer, WriteBuffer};
+
+// [enum uint 8 ModbusErrorCode
+// ['1' ILLEGAL_FUNCTION]
+// ['2' ILLEGAL_DATA_ADDRESS]
+// ['3' ILLEGAL_DATA_VALUE]
+// ['4' SLAVE_DEVICE_FAILURE]
+// ['5' ACKNOWLEDGE]
+// ['6' SLAVE_DEVICE_BUSY]
+// ['7' NEGATIVE_ACKNOWLEDGE]
+// ['8' MEMORY_PARITY_ERROR]
+// ['10' GATEWAY_PATH_UNAVAILABLE]
+// ['11' GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND]
+// ]
+plc_enum!
+[enum u8 : ModbusErrorCode
+ [1 => ILLEGAL_FUNCTION],
+ [2 => ILLEGAL_DATA_ADDRESS],
+ [3 => ILLEGAL_DATA_VALUE],
+ [4 => SLAVE_DEVICE_FAILURE],
+ [5 => ACKNOWLEDGE],
+ [6 => SLAVE_DEVICE_BUSY],
+ [7 => NEGATIVE_ACKNOWLEDGE],
+ [8 => MEMORY_PARITY_ERROR],
+ [10 => GATEWAY_PATH_UNAVAILABLE],
+ [11 => GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND]
+];
\ No newline at end of file
diff --git a/plc4rust/src/modbus/modbus_pdu.rs b/plc4rust/src/modbus/modbus_pdu.rs
index 8fc84e9c1f..4d678ff89b 100644
--- a/plc4rust/src/modbus/modbus_pdu.rs
+++ b/plc4rust/src/modbus/modbus_pdu.rs
@@ -18,6 +18,7 @@
*/
use std::io::{Error, Read, Write};
+use std::io::ErrorKind::InvalidInput;
use crate::{Message, NoOption, ReadBuffer, WriteBuffer};
// [discriminatedType ModbusPDU(bit response)
@@ -244,7 +245,20 @@ impl Message for ModbusPDU {
}
fn parse<T: Read>(reader: &mut ReadBuffer<T>, parameter: Option<Self::P>) -> Result<Self::M, Error> {
- todo!()
+ let response = parameter.unwrap().bit_response;
+ let error_flag = reader.read_bit()?;
+ let function_flag = reader.read_u_n(7)? as u8;
+
+ match (error_flag, function_flag, response) {
+ (true, _, _) => {
+ Ok(ModbusPDU::ModbusPDUError(ModbusPDUError {
+
+ }))
+ }
+ (_, _, _) => {
+ Err(Error::new(InvalidInput, "unnable to parse"))
+ }
+ }
}
}
diff --git a/plc4rust/src/read_buffer.rs b/plc4rust/src/read_buffer.rs
index 545dfdfa98..924665a0fb 100644
--- a/plc4rust/src/read_buffer.rs
+++ b/plc4rust/src/read_buffer.rs
@@ -32,7 +32,7 @@ impl<T: Read> ReadBuffer<T> {
ReadBuffer {
position: 0,
endianness: endianess,
- reader: reader
+ reader
}
}
}
@@ -64,6 +64,10 @@ impl<T: Read> ReadBuffer<T> {
})
}
+ pub(crate) fn read_u_n(&self, number_of_bits: u8) -> Result<u64, std::io::Error> {
+ todo!()
+ }
+
pub(crate) fn read_bytes(&mut self, length: usize) -> Result<Vec<u8>, std::io::Error> {
let mut bytes = vec![0_8; length];
self.reader.read(&mut bytes)?;