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;
22pub const STANDARD_STRING_HANDLE: u16 = 0x0FCE;
23pub const STANDARD_STRING_DATA_LEN: usize = 82;
24pub const STANDARD_STRING_PAD_LEN: usize = 2;
25pub const STANDARD_STRING_PAYLOAD_LEN: usize =
26    4 + STANDARD_STRING_DATA_LEN + STANDARD_STRING_PAD_LEN;
27
28pub fn write_data_type(value: &PlcValue) -> u16 {
29    if let PlcValue::Udt(_) = value {
30        value.known_data_type().unwrap_or(UDT)
31    } else {
32        value.get_data_type()
33    }
34}
35
36pub fn write_data_type_bytes(value: &PlcValue) -> Vec<u8> {
37    if matches!(value, PlcValue::String(_)) {
38        let mut bytes = Vec::with_capacity(4);
39        bytes.extend_from_slice(&AB_UDT.to_le_bytes());
40        bytes.extend_from_slice(&STANDARD_STRING_HANDLE.to_le_bytes());
41        bytes
42    } else {
43        write_data_type(value).to_le_bytes().to_vec()
44    }
45}
46
47pub fn encode_payload(value: &PlcValue, buf: &mut BytesMut) {
48    match value {
49        PlcValue::Bool(v) => buf.put_u8(if *v { 0xFF } else { 0x00 }),
50        PlcValue::Sint(v) => buf.put_i8(*v),
51        PlcValue::Int(v) => buf.put_i16_le(*v),
52        PlcValue::Dint(v) => buf.put_i32_le(*v),
53        PlcValue::Lint(v) => buf.put_i64_le(*v),
54        PlcValue::Usint(v) => buf.put_u8(*v),
55        PlcValue::Uint(v) => buf.put_u16_le(*v),
56        PlcValue::Udint(v) => buf.put_u32_le(*v),
57        PlcValue::Ulint(v) => buf.put_u64_le(*v),
58        PlcValue::Real(v) => buf.put_slice(&v.to_le_bytes()),
59        PlcValue::Lreal(v) => buf.put_slice(&v.to_le_bytes()),
60        PlcValue::String(v) => encode_standard_string_payload(v, buf),
61        PlcValue::Udt(udt_data) => buf.put_slice(&udt_data.data),
62    }
63}
64
65pub fn encode_type_prefixed(value: &PlcValue, buf: &mut BytesMut) {
66    buf.put_slice(&write_data_type_bytes(value));
67    match value {
68        PlcValue::String(v) => encode_standard_string_payload(v, buf),
69        PlcValue::Udt(udt_data) => buf.put_slice(&udt_data.data),
70        _ => encode_payload(value, buf),
71    }
72}
73
74pub fn decode_payload(data_type: u16, value_data: &[u8]) -> Result<PlcValue> {
75    match data_type {
76        BOOL => {
77            require_len(value_data, 1, "BOOL")?;
78            Ok(PlcValue::Bool(value_data[0] != 0))
79        }
80        SINT => {
81            require_len(value_data, 1, "SINT")?;
82            Ok(PlcValue::Sint(value_data[0] as i8))
83        }
84        INT => {
85            require_len(value_data, 2, "INT")?;
86            Ok(PlcValue::Int(i16::from_le_bytes([
87                value_data[0],
88                value_data[1],
89            ])))
90        }
91        DINT => {
92            require_len(value_data, 4, "DINT")?;
93            Ok(PlcValue::Dint(i32::from_le_bytes([
94                value_data[0],
95                value_data[1],
96                value_data[2],
97                value_data[3],
98            ])))
99        }
100        LINT => {
101            require_len(value_data, 8, "LINT")?;
102            Ok(PlcValue::Lint(i64::from_le_bytes(
103                value_data[..8]
104                    .try_into()
105                    .expect("length checked before fixed-width LINT decode"),
106            )))
107        }
108        USINT => {
109            require_len(value_data, 1, "USINT")?;
110            Ok(PlcValue::Usint(value_data[0]))
111        }
112        UINT => {
113            require_len(value_data, 2, "UINT")?;
114            Ok(PlcValue::Uint(u16::from_le_bytes([
115                value_data[0],
116                value_data[1],
117            ])))
118        }
119        UDINT => {
120            require_len(value_data, 4, "UDINT")?;
121            Ok(PlcValue::Udint(u32::from_le_bytes([
122                value_data[0],
123                value_data[1],
124                value_data[2],
125                value_data[3],
126            ])))
127        }
128        ULINT => {
129            require_len(value_data, 8, "ULINT")?;
130            Ok(PlcValue::Ulint(u64::from_le_bytes(
131                value_data[..8]
132                    .try_into()
133                    .expect("length checked before fixed-width ULINT decode"),
134            )))
135        }
136        REAL => {
137            require_len(value_data, 4, "REAL")?;
138            Ok(PlcValue::Real(f32::from_le_bytes([
139                value_data[0],
140                value_data[1],
141                value_data[2],
142                value_data[3],
143            ])))
144        }
145        LREAL => {
146            require_len(value_data, 8, "LREAL")?;
147            Ok(PlcValue::Lreal(f64::from_le_bytes(
148                value_data[..8]
149                    .try_into()
150                    .expect("length checked before fixed-width LREAL decode"),
151            )))
152        }
153        STRING => decode_dint_string(value_data),
154        ALT_STRING => decode_short_string(value_data),
155        AB_UDT | UDT => decode_structure_payload(value_data),
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 encode_standard_string_payload(value: &str, buf: &mut BytesMut) {
181    let string_bytes = value.as_bytes();
182    let data_len = string_bytes.len().min(STANDARD_STRING_DATA_LEN);
183    buf.put_u32_le(data_len as u32);
184    buf.put_slice(&string_bytes[..data_len]);
185    buf.resize(buf.len() + (STANDARD_STRING_DATA_LEN - data_len), 0);
186    buf.resize(buf.len() + STANDARD_STRING_PAD_LEN, 0);
187}
188
189fn decode_structure_payload(value_data: &[u8]) -> Result<PlcValue> {
190    if value_data.len() >= 2 {
191        let handle = u16::from_le_bytes([value_data[0], value_data[1]]);
192        if handle == STANDARD_STRING_HANDLE {
193            return decode_standard_string_structure(value_data);
194        }
195    }
196
197    Ok(PlcValue::Udt(UdtData {
198        symbol_id: 0,
199        data: value_data.to_vec(),
200    }))
201}
202
203fn decode_standard_string_structure(value_data: &[u8]) -> Result<PlcValue> {
204    let required = 2 + STANDARD_STRING_PAYLOAD_LEN;
205    if value_data.len() < required {
206        return Err(ProtocolError::new(format!(
207            "Insufficient data for standard STRING structure: need {required} bytes, have {} bytes",
208            value_data.len()
209        )));
210    }
211
212    let payload = &value_data[2..];
213    let length = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]) as usize;
214    if length > STANDARD_STRING_DATA_LEN {
215        return Err(ProtocolError::new(format!(
216            "Invalid standard STRING length: {length} > {STANDARD_STRING_DATA_LEN}"
217        )));
218    }
219
220    Ok(PlcValue::String(
221        String::from_utf8_lossy(&payload[4..4 + length]).to_string(),
222    ))
223}
224
225fn decode_dint_string(value_data: &[u8]) -> Result<PlcValue> {
226    if value_data.len() < 4 {
227        return Err(ProtocolError::new(
228            "Insufficient data for STRING length field".to_string(),
229        ));
230    }
231
232    let length =
233        u32::from_le_bytes([value_data[0], value_data[1], value_data[2], value_data[3]]) as usize;
234    if value_data.len() - 4 < length {
235        return Err(ProtocolError::new(format!(
236            "Insufficient data for STRING value: need {} bytes, have {} bytes",
237            4 + length,
238            value_data.len()
239        )));
240    }
241    Ok(PlcValue::String(
242        String::from_utf8_lossy(&value_data[4..4 + length]).to_string(),
243    ))
244}
245
246fn decode_short_string(value_data: &[u8]) -> Result<PlcValue> {
247    if value_data.is_empty() {
248        return Ok(PlcValue::String(String::new()));
249    }
250    let length = value_data[0] as usize;
251    if value_data.len() < 1 + length {
252        return Err(ProtocolError::new(
253            "Insufficient data for STRING value".to_string(),
254        ));
255    }
256    Ok(PlcValue::String(
257        String::from_utf8_lossy(&value_data[1..1 + length]).to_string(),
258    ))
259}
260
261fn require_len(value_data: &[u8], min_len: usize, name: &str) -> Result<()> {
262    if value_data.len() < min_len {
263        let msg = if min_len == 1 {
264            format!("No data for {name} value")
265        } else {
266            format!("Insufficient data for {name} value")
267        };
268        Err(ProtocolError::new(msg))
269    } else {
270        Ok(())
271    }
272}
273
274impl Encode for PlcValue {
275    fn encode(&self, buf: &mut BytesMut) {
276        encode_type_prefixed(self, buf);
277    }
278}
279
280impl Decode for PlcValue {
281    fn decode(buf: &mut impl Buf) -> Result<Self> {
282        if buf.remaining() < 2 {
283            return Err(ProtocolError::new("Data too short for type".to_string()));
284        }
285        let data_type = buf.get_u16_le();
286        let remaining = buf.copy_to_bytes(buf.remaining());
287        decode_payload(data_type, &remaining)
288    }
289}
290
291impl Encode for UdtData {
292    fn encode(&self, buf: &mut BytesMut) {
293        buf.put_slice(&self.data);
294    }
295}
296
297impl Decode for UdtData {
298    fn decode(buf: &mut impl Buf) -> Result<Self> {
299        let data = buf.copy_to_bytes(buf.remaining()).to_vec();
300        Ok(Self { symbol_id: 0, data })
301    }
302}