Skip to main content

rust_ethernet_ip_protocol/
values.rs

1use bytes::{Buf, BufMut, BytesMut};
2
3use crate::{Decode, Encode, ProtocolError, Result};
4use rust_ethernet_ip_types::{PlcValue, UdtData};
5
6pub const BOOL: u16 = 0x00C1;
7pub const SINT: u16 = 0x00C2;
8pub const INT: u16 = 0x00C3;
9pub const DINT: u16 = 0x00C4;
10pub const LINT: u16 = 0x00C5;
11pub const USINT: u16 = 0x00C6;
12pub const UINT: u16 = 0x00C7;
13pub const UDINT: u16 = 0x00C8;
14pub const ULINT: u16 = 0x00C9;
15pub const REAL: u16 = 0x00CA;
16pub const LREAL: u16 = 0x00CB;
17pub const STRING: u16 = 0x00CE;
18pub const ALT_STRING: u16 = 0x00DA;
19pub const BOOL_ARRAY_DWORD: u16 = 0x00D3;
20pub const UDT: u16 = 0x00A0;
21pub const AB_UDT: u16 = 0x02A0;
22
23pub fn write_data_type(value: &PlcValue) -> u16 {
24    if let PlcValue::Udt(_) = value {
25        value.known_data_type().unwrap_or(UDT)
26    } else {
27        value.get_data_type()
28    }
29}
30
31pub fn encode_payload(value: &PlcValue, buf: &mut BytesMut) {
32    match value {
33        PlcValue::Bool(v) => buf.put_u8(if *v { 0xFF } else { 0x00 }),
34        PlcValue::Sint(v) => buf.put_i8(*v),
35        PlcValue::Int(v) => buf.put_i16_le(*v),
36        PlcValue::Dint(v) => buf.put_i32_le(*v),
37        PlcValue::Lint(v) => buf.put_i64_le(*v),
38        PlcValue::Usint(v) => buf.put_u8(*v),
39        PlcValue::Uint(v) => buf.put_u16_le(*v),
40        PlcValue::Udint(v) => buf.put_u32_le(*v),
41        PlcValue::Ulint(v) => buf.put_u64_le(*v),
42        PlcValue::Real(v) => buf.put_slice(&v.to_le_bytes()),
43        PlcValue::Lreal(v) => buf.put_slice(&v.to_le_bytes()),
44        PlcValue::String(v) => {
45            let length = v.len().min(82) as u32;
46            buf.put_u32_le(length);
47            let string_bytes = v.as_bytes();
48            let data_len = string_bytes.len().min(82);
49            buf.put_slice(&string_bytes[..data_len]);
50        }
51        PlcValue::Udt(udt_data) => buf.put_slice(&udt_data.data),
52    }
53}
54
55pub fn encode_type_prefixed(value: &PlcValue, buf: &mut BytesMut) {
56    buf.put_u16_le(write_data_type(value));
57    match value {
58        PlcValue::String(v) => {
59            let length = v.len().min(82) as u32;
60            buf.put_u32_le(length);
61            let string_bytes = v.as_bytes();
62            let data_len = string_bytes.len().min(82);
63            buf.put_slice(&string_bytes[..data_len]);
64            buf.resize(buf.len() + (82 - data_len), 0);
65        }
66        PlcValue::Udt(udt_data) => buf.put_slice(&udt_data.data),
67        _ => encode_payload(value, buf),
68    }
69}
70
71pub fn decode_payload(data_type: u16, value_data: &[u8]) -> Result<PlcValue> {
72    match data_type {
73        BOOL => {
74            require_len(value_data, 1, "BOOL")?;
75            Ok(PlcValue::Bool(value_data[0] != 0))
76        }
77        SINT => {
78            require_len(value_data, 1, "SINT")?;
79            Ok(PlcValue::Sint(value_data[0] as i8))
80        }
81        INT => {
82            require_len(value_data, 2, "INT")?;
83            Ok(PlcValue::Int(i16::from_le_bytes([
84                value_data[0],
85                value_data[1],
86            ])))
87        }
88        DINT => {
89            require_len(value_data, 4, "DINT")?;
90            Ok(PlcValue::Dint(i32::from_le_bytes([
91                value_data[0],
92                value_data[1],
93                value_data[2],
94                value_data[3],
95            ])))
96        }
97        LINT => {
98            require_len(value_data, 8, "LINT")?;
99            Ok(PlcValue::Lint(i64::from_le_bytes(
100                value_data[..8]
101                    .try_into()
102                    .expect("length checked before fixed-width LINT decode"),
103            )))
104        }
105        USINT => {
106            require_len(value_data, 1, "USINT")?;
107            Ok(PlcValue::Usint(value_data[0]))
108        }
109        UINT => {
110            require_len(value_data, 2, "UINT")?;
111            Ok(PlcValue::Uint(u16::from_le_bytes([
112                value_data[0],
113                value_data[1],
114            ])))
115        }
116        UDINT => {
117            require_len(value_data, 4, "UDINT")?;
118            Ok(PlcValue::Udint(u32::from_le_bytes([
119                value_data[0],
120                value_data[1],
121                value_data[2],
122                value_data[3],
123            ])))
124        }
125        ULINT => {
126            require_len(value_data, 8, "ULINT")?;
127            Ok(PlcValue::Ulint(u64::from_le_bytes(
128                value_data[..8]
129                    .try_into()
130                    .expect("length checked before fixed-width ULINT decode"),
131            )))
132        }
133        REAL => {
134            require_len(value_data, 4, "REAL")?;
135            Ok(PlcValue::Real(f32::from_le_bytes([
136                value_data[0],
137                value_data[1],
138                value_data[2],
139                value_data[3],
140            ])))
141        }
142        LREAL => {
143            require_len(value_data, 8, "LREAL")?;
144            Ok(PlcValue::Lreal(f64::from_le_bytes(
145                value_data[..8]
146                    .try_into()
147                    .expect("length checked before fixed-width LREAL decode"),
148            )))
149        }
150        STRING => decode_dint_string(value_data),
151        ALT_STRING => decode_short_string(value_data),
152        AB_UDT | UDT => Ok(PlcValue::Udt(UdtData {
153            symbol_id: 0,
154            data: value_data.to_vec(),
155        })),
156        BOOL_ARRAY_DWORD => {
157            if value_data.len() >= 4 {
158                Ok(PlcValue::Udint(u32::from_le_bytes([
159                    value_data[0],
160                    value_data[1],
161                    value_data[2],
162                    value_data[3],
163                ])))
164            } else {
165                Err(ProtocolError::new(
166                    "Insufficient data for DWORD value".to_string(),
167                ))
168            }
169        }
170        _ => Err(ProtocolError::new(format!(
171            "Unsupported data type: 0x{data_type:04X}"
172        ))),
173    }
174}
175
176pub fn decode_array_element(data_type: u16, chunk: &[u8]) -> Result<PlcValue> {
177    decode_payload(data_type, chunk)
178}
179
180fn decode_dint_string(value_data: &[u8]) -> Result<PlcValue> {
181    if value_data.len() < 4 {
182        return Err(ProtocolError::new(
183            "Insufficient data for STRING length field".to_string(),
184        ));
185    }
186
187    let length =
188        u32::from_le_bytes([value_data[0], value_data[1], value_data[2], value_data[3]]) as usize;
189    if value_data.len() - 4 < length {
190        return Err(ProtocolError::new(format!(
191            "Insufficient data for STRING value: need {} bytes, have {} bytes",
192            4 + length,
193            value_data.len()
194        )));
195    }
196    Ok(PlcValue::String(
197        String::from_utf8_lossy(&value_data[4..4 + length]).to_string(),
198    ))
199}
200
201fn decode_short_string(value_data: &[u8]) -> Result<PlcValue> {
202    if value_data.is_empty() {
203        return Ok(PlcValue::String(String::new()));
204    }
205    let length = value_data[0] as usize;
206    if value_data.len() < 1 + length {
207        return Err(ProtocolError::new(
208            "Insufficient data for STRING value".to_string(),
209        ));
210    }
211    Ok(PlcValue::String(
212        String::from_utf8_lossy(&value_data[1..1 + length]).to_string(),
213    ))
214}
215
216fn require_len(value_data: &[u8], min_len: usize, name: &str) -> Result<()> {
217    if value_data.len() < min_len {
218        let msg = if min_len == 1 {
219            format!("No data for {name} value")
220        } else {
221            format!("Insufficient data for {name} value")
222        };
223        Err(ProtocolError::new(msg))
224    } else {
225        Ok(())
226    }
227}
228
229impl Encode for PlcValue {
230    fn encode(&self, buf: &mut BytesMut) {
231        encode_type_prefixed(self, buf);
232    }
233}
234
235impl Decode for PlcValue {
236    fn decode(buf: &mut impl Buf) -> Result<Self> {
237        if buf.remaining() < 2 {
238            return Err(ProtocolError::new("Data too short for type".to_string()));
239        }
240        let data_type = buf.get_u16_le();
241        let remaining = buf.copy_to_bytes(buf.remaining());
242        decode_payload(data_type, &remaining)
243    }
244}
245
246impl Encode for UdtData {
247    fn encode(&self, buf: &mut BytesMut) {
248        buf.put_slice(&self.data);
249    }
250}
251
252impl Decode for UdtData {
253    fn decode(buf: &mut impl Buf) -> Result<Self> {
254        let data = buf.copy_to_bytes(buf.remaining()).to_vec();
255        Ok(Self { symbol_id: 0, data })
256    }
257}