initial commit

This commit is contained in:
Hlars 2021-10-16 17:17:09 +02:00
commit 8636825388
15 changed files with 931 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "s7_datatypes"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = [ "derive" ] }
anyhow = "1.0"
bit = "0.1"

13
src/errors.rs Normal file
View File

@ -0,0 +1,13 @@
use anyhow::{anyhow, Error};
pub fn serial_error(name: String, pos: u32) -> Error {
anyhow!(
"Cannot convert Serial data for type '{}' at position {}.",
name,
pos
)
}
pub fn s7_read_error(name: String, pos: u32) -> Error {
anyhow!("Cannot read Byte for type '{}' at position {}.", name, pos)
}

11
src/lib.rs Normal file
View File

@ -0,0 +1,11 @@
mod errors;
pub mod sps_datatypes;
pub mod types;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

105
src/sps_datatypes.rs Normal file
View File

@ -0,0 +1,105 @@
use super::types::boolean::*;
use super::types::date_time::*;
use super::types::dint::*;
use super::types::int::*;
use super::types::real::*;
use super::types::string::*;
use super::types::time::*;
use super::types::udint::*;
use super::types::uint::*;
use serde::{Deserialize, Serialize};
use anyhow::Result;
#[derive(Debug, Serialize, Copy, Clone)]
pub enum SPSDataTypes {
Boolean(BooleanType),
Int(IntType),
DInt(DIntType),
Real(RealType),
Time(TimeType),
DateTime(DateTimeType),
UDInt(UDIntType),
UInt(UIntType),
String(StringType),
}
impl SPSDataTypes {
fn into(self) -> Box<dyn DataEvaluation> {
match self {
Self::Boolean(raw) => Box::new(raw),
Self::DInt(raw) => Box::new(raw),
Self::Int(raw) => Box::new(raw),
Self::Real(raw) => Box::new(raw),
Self::Time(raw) => Box::new(raw),
Self::DateTime(raw) => Box::new(raw),
Self::UDInt(raw) => Box::new(raw),
Self::UInt(raw) => Box::new(raw),
Self::String(raw) => Box::new(raw),
}
}
pub fn get_end_byte(self) -> u32 {
self.into().get_end_byte()
}
pub fn get_byte_positon(self) -> u32 {
self.into().get_byte_positon()
}
pub fn get_length(self) -> u32 {
self.into().get_length()
}
pub fn parse_serial_value(self, data: &[&str]) -> Result<String> {
self.into().parse_serial(data)
}
pub fn parse_s7_value(self, data: &[u8]) -> Result<String> {
self.into().parse_s7(data)
}
pub fn create_sql_data_type(self) -> SQLDataType {
self.into().create_sql_data_type()
}
}
impl Into<UnparsedSPSDataType> for SPSDataTypes {
fn into(self) -> UnparsedSPSDataType {
self.into().into_unparsed()
}
}
pub struct UnparsedSPSDataType {
pub data_type: String,
pub data_byte: u32,
pub data_bit: Option<u32>,
pub data_length: Option<u32>,
}
pub trait DataEvaluation {
fn into_unparsed(&self) -> UnparsedSPSDataType;
fn get_end_byte(&self) -> u32;
fn get_byte_positon(&self) -> u32;
fn get_length(&self) -> u32;
fn parse_serial(&self, data: &[&str]) -> Result<String>;
fn parse_s7(&self, data: &[u8]) -> Result<String>;
fn sql_equivalent(&self) -> &str;
fn create_sql_data_type(&self) -> SQLDataType {
SQLDataType {
mysql_type: self.sql_equivalent().to_owned(),
}
}
}
#[derive(Debug)]
pub struct SQLDataType {
pub mysql_type: String,
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct BitPosition {
pub byte: u32,
pub bit: Option<u32>,
}

95
src/types/boolean.rs Normal file
View File

@ -0,0 +1,95 @@
use anyhow::{anyhow, Result};
use bit::BitIndex;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct BooleanType {
position: BitPosition,
}
impl BooleanType {
pub fn new(byte: u32, bit: Option<u32>) -> Self {
BooleanType {
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"boolean".to_string()
}
}
impl DataEvaluation for BooleanType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
1
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let byte = data
.get((self.position.byte) as usize)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
Ok(match byte.bit(self.position.bit.ok_or_else(|| {
anyhow!(
"Cannot read Bit {}.{}.",
self.position.byte,
self.position.bit.unwrap()
)
})? as usize)
{
true => 1,
false => 0,
}
.to_string())
}
fn sql_equivalent(&self) -> &str {
"BOOLEAN DEFAULT false"
}
}
#[test]
fn test() {
const BYTEPOS: u32 = 5;
const BITPOS: u32 = 4;
const VAL: bool = false;
let test_item = BooleanType::new(BYTEPOS, Some(BITPOS));
let raw_data: [u8; 50] = [0; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec[BYTEPOS as usize].set_bit(BITPOS as usize, VAL);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(
result,
match VAL {
true => 1,
false => 0,
}
.to_string()
)
}

125
src/types/date_time.rs Normal file
View File

@ -0,0 +1,125 @@
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct DateTimeType {
length: u32,
position: BitPosition,
}
impl DateTimeType {
const LEN: usize = 8;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
DateTimeType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"datetime".to_string()
}
fn assert_range_inclusive(&self, input: u8, min: u8, max: u8, field: &str) -> Result<u8> {
if input < min {
bail!(
"Value '{}' is lower than the minimum '{}' allowed for {}.",
input,
min,
field
)
}
if input > max {
bail!(
"Value '{}' is higher than the maximum '{}' allowed for {}.",
input,
max,
field
)
}
Ok(input)
}
fn parse(&self, data: &[u8]) -> Result<String> {
let year: Result<u32> = match data[0] {
e if e < 90 => Ok(e as u32 + 2000),
e if e < 100 => Ok(e as u32 + 1900),
e => bail!(
"Value '{}' is higher than the maximum '99' of S7 date and time representation.",
e
),
};
let month = self.assert_range_inclusive(data[1], 1, 12, "month")?;
let day = self.assert_range_inclusive(data[2], 1, 31, "day of month")?;
let hour = self.assert_range_inclusive(data[3], 0, 23, "hour")?;
let minute = self.assert_range_inclusive(data[4], 0, 59, "minute")?;
let second = self.assert_range_inclusive(data[5], 0, 59, "second")?;
let _sec = self.assert_range_inclusive(data[6], 0, 99, "first two millisecond digits")?;
let _msec = self.assert_range_inclusive(data[7] >> 4, 0, 9, "third millisecond digit")?;
let _day_of_week =
self.assert_range_inclusive(data[7] & 0b00001111, 1, 7, "day of week")?;
Ok(format!(
"{}-{:02}-{:02} {:02}:{:02}:{:02}",
year?, month, day, hour, minute, second
))
}
}
impl DataEvaluation for DateTimeType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let bytes = data
.get((self.position.byte) as usize..(self.position.byte + self.length) as usize)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
self.parse(&bytes.to_vec())
}
fn sql_equivalent(&self) -> &str {
r"DATETIME DEFAULT NULL"
}
}
#[test]
fn test() {
const DATEPOS: u32 = 5;
const LEN: u32 = 8;
const VAL: &str = "1993-12-25 08:12:34";
let test_item = DateTimeType::new(DATEPOS, None);
let raw_data: [u8; 50] = [255; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec.splice(
DATEPOS as usize..DATEPOS as usize + LEN as usize,
[93, 12, 25, 8, 12, 34, 56, 7 << 4 | 7],
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, VAL.to_string())
}

85
src/types/dint.rs Normal file
View File

@ -0,0 +1,85 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct DIntType {
length: u32,
position: BitPosition,
}
impl DIntType {
const LEN: usize = 4;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
DIntType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"dint".to_string()
}
}
impl DataEvaluation for DIntType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let bytes = data
.get((self.position.byte) as usize..self.position.byte as usize + Self::LEN)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
let mut slice: [u8; Self::LEN] = Default::default();
slice.copy_from_slice(bytes);
let parsed = i32::from_be_bytes(slice);
Ok(parsed.to_string())
}
fn sql_equivalent(&self) -> &str {
r"BIGINT DEFAULT 0"
}
}
#[test]
fn test() {
const INTPOS: u32 = 5;
const INT: i32 = -2589090;
const LEN: usize = 4;
let test_item = DIntType::new(INTPOS, None);
let raw_data: [u8; 50] = [255; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec.splice(
INTPOS as usize..INTPOS as usize + LEN,
INT.to_be_bytes().iter().cloned(),
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, INT.to_string())
}

83
src/types/int.rs Normal file
View File

@ -0,0 +1,83 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct IntType {
length: u32,
position: BitPosition,
}
impl IntType {
const LEN: usize = 2;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
IntType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"int".to_string()
}
}
impl DataEvaluation for IntType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let bytes = data
.get((self.position.byte) as usize..self.position.byte as usize + Self::LEN)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
let mut slice: [u8; Self::LEN] = Default::default();
slice.copy_from_slice(bytes);
let parsed = i16::from_be_bytes(slice);
Ok(parsed.to_string())
}
fn sql_equivalent(&self) -> &str {
r"INT DEFAULT 0"
}
}
#[test]
fn test() {
const INTPOS: u32 = 3;
const INT: i16 = -12;
const LEN: usize = 2;
let test_item = IntType::new(INTPOS, None);
let raw_data: [u8; 50] = [255; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec.splice(
INTPOS as usize..INTPOS as usize + LEN,
INT.to_be_bytes().iter().cloned(),
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, INT.to_string())
}

9
src/types/mod.rs Normal file
View File

@ -0,0 +1,9 @@
pub mod boolean;
pub mod date_time;
pub mod dint;
pub mod int;
pub mod real;
pub mod string;
pub mod time;
pub mod udint;
pub mod uint;

83
src/types/real.rs Normal file
View File

@ -0,0 +1,83 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct RealType {
length: u32,
position: BitPosition,
}
impl RealType {
const LEN: usize = 4;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
RealType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"real".to_string()
}
}
impl DataEvaluation for RealType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let bytes = data
.get((self.position.byte) as usize..(self.position.byte + self.length) as usize)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
let mut slice: [u8; 4] = Default::default();
slice.copy_from_slice(bytes);
let parsed = f32::from_be_bytes(slice);
Ok(parsed.to_string())
}
fn sql_equivalent(&self) -> &str {
r"DOUBLE DEFAULT 0.00"
}
}
#[test]
fn test() {
const INTPOS: u32 = 5;
const INT: f32 = 2.522529;
const LEN: usize = 4;
let test_item = RealType::new(INTPOS, None);
let raw_data: [u8; 50] = [255; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec.splice(
INTPOS as usize..INTPOS as usize + LEN,
INT.to_be_bytes().iter().cloned(),
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, INT.to_string())
}

92
src/types/string.rs Normal file
View File

@ -0,0 +1,92 @@
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct StringType {
length: u32,
position: BitPosition,
}
impl StringType {
pub fn new(length: u32, byte: u32, bit: Option<u32>) -> Self {
StringType {
length,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"string".to_string()
}
}
impl DataEvaluation for StringType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: Some(self.length),
}
}
fn get_end_byte(&self) -> u32 {
// first two bytes are header bytes
self.position.byte + self.length + 2
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
// Headerbyte 1: Reservierte Stringlänge in Byte [0…254]
// Headerbyte 2: Anzahl der zu interpretierenden Zeichen im String ab Byte 3
// Byte 3: 1. Nutzzeichen
let bytes = data
.get((self.position.byte) as usize..(self.position.byte + self.length) as usize)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
match String::from_utf8(bytes[2..bytes[1] as usize + 2].to_vec()) {
Ok(string) => Ok(string),
Err(err) => Err(anyhow!(
"Could not convert data at byte {} with length {} into string: {}.",
self.position.byte,
self.length,
err
)),
}
}
fn sql_equivalent(&self) -> &str {
r"VARCHAR(255)"
}
}
#[test]
fn test() {
const BYTEPOS: u32 = 5;
const LEN: u32 = 20;
const VAL: &str = "ich bin ein pfau";
let test_item = StringType::new(LEN + 2, BYTEPOS, None);
let raw_data: [u8; 50] = [0; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec[BYTEPOS as usize] = LEN as u8 + 2;
test_vec[BYTEPOS as usize + 1] = VAL.len() as u8;
test_vec.splice(
BYTEPOS as usize + 2..BYTEPOS as usize + LEN as usize,
VAL.as_bytes().iter().cloned(),
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, VAL.to_string())
}

55
src/types/time.rs Normal file
View File

@ -0,0 +1,55 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct TimeType {
length: u32,
position: BitPosition,
}
impl TimeType {
const LEN: usize = 4;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
TimeType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"time".to_string()
}
}
impl DataEvaluation for TimeType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, _data: &[u8]) -> Result<String> {
Ok("time".to_string())
}
fn sql_equivalent(&self) -> &str {
r"BIGINT UNSIGNED DEFAULT 0"
}
}

81
src/types/udint.rs Normal file
View File

@ -0,0 +1,81 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct UDIntType {
length: u32,
position: BitPosition,
}
impl UDIntType {
const LEN: usize = 4;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
UDIntType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"udint".to_string()
}
}
impl DataEvaluation for UDIntType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let bytes = data
.get((self.position.byte) as usize..(self.position.byte + self.length) as usize)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
let mut slice: [u8; Self::LEN] = Default::default();
slice.copy_from_slice(bytes);
let parsed = u32::from_be_bytes(slice);
Ok(parsed.to_string())
}
fn sql_equivalent(&self) -> &str {
r"BIGINT UNSIGNED DEFAULT 0"
}
}
#[test]
fn test() {
const INTPOS: u32 = 5;
const INT: u32 = 25890920;
const LEN: usize = 4;
let test_item = UDIntType::new(INTPOS, None);
let raw_data: [u8; 50] = [255; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec.splice(
INTPOS as usize..INTPOS as usize + LEN,
INT.to_be_bytes().iter().cloned(),
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, INT.to_string())
}

81
src/types/uint.rs Normal file
View File

@ -0,0 +1,81 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::super::errors::*;
use super::super::sps_datatypes::{BitPosition, DataEvaluation, UnparsedSPSDataType};
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct UIntType {
length: u32,
position: BitPosition,
}
impl UIntType {
const LEN: usize = 2;
pub fn new(byte: u32, bit: Option<u32>) -> Self {
UIntType {
length: Self::LEN as u32,
position: BitPosition { byte, bit },
}
}
fn into_string(self) -> String {
"uint".to_string()
}
}
impl DataEvaluation for UIntType {
fn into_unparsed(&self) -> UnparsedSPSDataType {
UnparsedSPSDataType {
data_type: self.into_string(),
data_byte: self.position.byte,
data_bit: self.position.bit,
data_length: None,
}
}
fn get_end_byte(&self) -> u32 {
self.position.byte + self.length
}
fn get_byte_positon(&self) -> u32 {
self.position.byte
}
fn get_length(&self) -> u32 {
self.length
}
fn parse_serial(&self, data: &[&str]) -> Result<String> {
Ok(data
.get((self.position.byte) as usize)
.ok_or_else(|| serial_error(self.into_string(), self.position.byte))?
.to_string())
}
fn parse_s7(&self, data: &[u8]) -> Result<String> {
let bytes = data
.get((self.position.byte) as usize..(self.position.byte + self.length) as usize)
.ok_or_else(|| s7_read_error(self.into_string(), self.position.byte))?;
let mut slice: [u8; Self::LEN] = Default::default();
slice.copy_from_slice(bytes);
let parsed = u16::from_be_bytes(slice);
Ok(parsed.to_string())
}
fn sql_equivalent(&self) -> &str {
r"INT UNSIGNED DEFAULT 0"
}
}
#[test]
fn test() {
const INTPOS: u32 = 15;
const INT: u16 = 8209;
const LEN: usize = 2;
let test_item = UIntType::new(INTPOS, None);
let raw_data: [u8; 50] = [255; 50];
let mut test_vec: Vec<u8> = raw_data.to_vec();
test_vec.splice(
INTPOS as usize..INTPOS as usize + LEN,
INT.to_be_bytes().iter().cloned(),
);
let result = match test_item.parse_s7(&test_vec) {
Ok(res) => res,
Err(_) => "Error".to_string(),
};
assert_eq!(result, INT.to_string())
}