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)?;