rust_ethernet_ip_protocol/
values.rs1use 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}