s7/
field.rs

1// Copyright 2019 Petar Dambovaliev. All rights reserved.
2// This software may be modified and distributed under the terms
3// of the BSD license. See the LICENSE file for details.
4
5//! Parses bytes from `Area::DataBausteine` to types for easier manipulation
6
7use super::error::Error;
8use byteorder::{BigEndian, ByteOrder};
9
10/// Fields collection type alias for convenience
11/// # Examples
12///
13/// ```
14/// use s7::field::{Float, Bool, Fields};
15///
16/// let float = Float::new(888, 8.0, vec![66, 86, 0, 0]).unwrap();
17/// let boolean = Bool::new(888, 8.0, vec![1u8]).unwrap();
18/// println!("bool: {}", boolean.value());
19/// println!("float: {}", float.value());
20/// let fields: Fields = vec![Box::new(float), Box::new(boolean)];
21///
22/// for field in fields.iter() {
23///     println!(
24///         "saving bytes {:?} to block {} offset {}",
25///         field.to_bytes(),
26///         field.data_block(),
27///         field.offset()
28///     )
29/// }
30/// ```
31pub type Fields = Vec<Box<dyn Field>>;
32
33/// represents a type stored in the hardware
34/// ie `bool`, `real(32 bit float)`
35pub trait Field {
36    /// data block
37    fn data_block(&self) -> i32;
38    /// offset in the data block
39    /// for convenience, we truncate the float
40    /// we don't care about the digits after the decimal point anymore
41    fn offset(&self) -> i32;
42
43    fn to_bytes(&self) -> Vec<u8>;
44}
45
46/// PLC float field
47#[derive(Debug)]
48pub struct Float {
49    data_block: i32,
50    /// offset example 8.1
51    /// left side is index within the block
52    /// right side is the bit position only used for bool, zero for all other types
53    offset: f32,
54    value: f32,
55}
56
57impl Float {
58    pub fn new(data_block: i32, offset: f32, mut bytes: Vec<u8>) -> Result<Float, Error> {
59        let len = bytes.len();
60        if bytes.len() != Float::size() as usize {
61            return Err(Error::TryFrom(
62                bytes,
63                format!("Float.new: expected buf size {} got {}", Float::size(), len),
64            ));
65        }
66
67        let bit_offset = ((offset * 10.0) as usize % 10) as u8;
68        if bit_offset != 0 {
69            return Err(Error::TryFrom(
70                bytes,
71                format!(
72                    "Float.new: float should not have a bit offset got {}",
73                    bit_offset
74                ),
75            ));
76        }
77
78        Ok(Float {
79            data_block,
80            offset,
81            value: BigEndian::read_f32(bytes.as_mut_slice()),
82        })
83    }
84
85    pub fn size() -> i32 {
86        4
87    }
88
89    pub fn value(&self) -> f32 {
90        self.value
91    }
92
93    pub fn set_value(&mut self, v: f32) {
94        self.value = v
95    }
96}
97
98impl Field for Float {
99    fn data_block(&self) -> i32 {
100        self.data_block
101    }
102
103    fn offset(&self) -> i32 {
104        self.offset as i32
105    }
106
107    fn to_bytes(&self) -> Vec<u8> {
108        let mut buf = vec![0u8; Float::size() as usize];
109        BigEndian::write_f32(buf.as_mut_slice(), self.value);
110        return buf;
111    }
112}
113
114/// Bool represents a single bit in a byte from `Area::DataBausteine`
115#[derive(Debug)]
116pub struct Bool {
117    /// index of the block it's stored at
118    data_block: i32,
119    /// example 8.1
120    /// left side is index within the block
121    /// right side is the bit position only used for bool, zero for all other types
122    offset: f32,
123    /// the actual primitive value
124    byte: u8,
125    /// the current value that will be written to the byte
126    value: bool,
127}
128
129impl Bool {
130    pub fn new(data_block: i32, offset: f32, bytes: Vec<u8>) -> Result<Self, Error> {
131        let len = bytes.len();
132        if bytes.len() != Self::size() as usize {
133            return Err(Error::TryFrom(
134                bytes,
135                format!("Bool.new: expected buf size {} got {}", Self::size(), len),
136            ));
137        }
138
139        let bit_offset = ((offset * 10.0) as usize % 10) as u8;
140        if bit_offset > 7 {
141            return Err(Error::TryFrom(
142                bytes,
143                format!("Bool.new: max offset is 7 got {}", offset),
144            ));
145        }
146
147        Ok(Bool {
148            data_block,
149            offset,
150            byte: bytes[0],
151            value: bytes[0] & (1 << bit_offset) != 0,
152        })
153    }
154
155    #[inline(always)]
156    fn set_value_at(b: u8, bit_pos: u8, val: bool) -> u8 {
157        if val {
158            return b | (1 << bit_pos);
159        }
160        return b & !(1 << bit_pos);
161    }
162
163    pub fn size() -> i32 {
164        1
165    }
166
167    /// gets the value at the current offset
168    pub fn value(&self) -> bool {
169        self.value
170    }
171
172    pub fn set_value(&mut self, v: bool) {
173        self.value = v;
174        self.byte = Bool::set_value_at(
175            self.byte,
176            ((self.offset * 10.0) as usize % 10) as u8,
177            self.value,
178        );
179    }
180}
181
182impl Field for Bool {
183    fn data_block(&self) -> i32 {
184        self.data_block
185    }
186
187    fn offset(&self) -> i32 {
188        self.offset as i32
189    }
190
191    fn to_bytes(&self) -> Vec<u8> {
192        vec![self.byte]
193    }
194}
195
196#[test]
197fn test_fields() {
198    let float = Float::new(888, 8.0, vec![66, 86, 0, 0]).unwrap();
199    let boolean = Bool::new(888, 8.0, vec![1u8]).unwrap();
200    assert!(boolean.value());
201    assert_eq!(53.5, float.value());
202    let fields: Fields = vec![Box::new(float), Box::new(boolean)];
203
204    for field in fields.iter() {
205        println!(
206            "saving bytes {:?} to block {} offset {}",
207            field.to_bytes(),
208            field.data_block(),
209            field.offset()
210        )
211    }
212}
213
214#[test]
215fn test_float() {
216    let val: f32 = 53.5;
217    let mut b = vec![0u8; Float::size() as usize];
218    BigEndian::write_f32(b.as_mut_slice(), val);
219    let mut field = Float::new(888, 8.0, b).unwrap();
220    field.set_value(val);
221    let result = field.to_bytes();
222
223    assert_eq!(vec![66, 86, 0, 0], result);
224
225    // test invalid bit offset
226    // float should not have a bit offset
227    match Float::new(888, 8.1, vec![66, 86, 0, 0]) {
228        Ok(_) => {
229            println!("should return an error at invalid bit offset 1. Floats should not have a bit offset");
230            assert!(false)
231        }
232        Err(_) => {}
233    }
234}
235
236#[test]
237fn test_bool() {
238    let b = vec![1u8; 1];
239    let mut field = Bool::new(888, 8.1, b).unwrap();
240    field.set_value(true);
241
242    let mut res: Vec<u8> = field.to_bytes();
243
244    assert_eq!(res.len(), 1);
245    assert_eq!(res[0], 3);
246    assert_eq!(field.value(), true);
247
248    field.set_value(false);
249    res = field.to_bytes();
250
251    assert_eq!(res.len(), 1);
252    assert_eq!(res[0], 1);
253    assert_eq!(field.value(), false);
254
255    let bb = vec![0b00001000u8; 1];
256    field = Bool::new(888, 8.4, bb).unwrap();
257    field.set_value(true);
258
259    res = field.to_bytes();
260
261    assert_eq!(res.len(), 1);
262    assert_eq!(res[0], 24);
263    assert_eq!(field.value(), true);
264
265    // test invalid bit offset
266    match Bool::new(888, 8.8, vec![0b00001000u8; 1]) {
267        Ok(_) => {
268            println!("should return an error at invalid bit offset 8");
269            assert!(false)
270        }
271        Err(_) => {}
272    }
273}