rust_ethernet_ip_protocol/
values.rs1use bytes::{Buf, BufMut, BytesMut};
4
5use crate::{Decode, Encode, ProtocolError, Result};
6use rust_ethernet_ip_types::{PlcValue, UdtData};
7
8pub const BOOL: u16 = 0x00C1;
10pub const SINT: u16 = 0x00C2;
12pub const INT: u16 = 0x00C3;
14pub const DINT: u16 = 0x00C4;
16pub const LINT: u16 = 0x00C5;
18pub const USINT: u16 = 0x00C6;
20pub const UINT: u16 = 0x00C7;
22pub const UDINT: u16 = 0x00C8;
24pub const ULINT: u16 = 0x00C9;
26pub const REAL: u16 = 0x00CA;
28pub const LREAL: u16 = 0x00CB;
30pub const STRING: u16 = 0x00CE;
32pub const ALT_STRING: u16 = 0x00DA;
34pub const BOOL_ARRAY_DWORD: u16 = 0x00D3;
36pub const UDT: u16 = 0x00A0;
38pub const AB_UDT: u16 = 0x02A0;
40pub const STANDARD_STRING_HANDLE: u16 = 0x0FCE;
42pub const STANDARD_STRING_DATA_LEN: usize = 82;
44pub const STANDARD_STRING_PAD_LEN: usize = 2;
46pub const STANDARD_STRING_PAYLOAD_LEN: usize =
48 4 + STANDARD_STRING_DATA_LEN + STANDARD_STRING_PAD_LEN;
49
50pub fn write_data_type(value: &PlcValue) -> u16 {
52 if let PlcValue::Udt(_) = value {
53 value.known_data_type().unwrap_or(UDT)
54 } else {
55 value.get_data_type()
56 }
57}
58
59pub fn write_data_type_bytes(value: &PlcValue) -> Vec<u8> {
61 if matches!(value, PlcValue::String(_)) {
62 let mut bytes = Vec::with_capacity(4);
63 bytes.extend_from_slice(&AB_UDT.to_le_bytes());
64 bytes.extend_from_slice(&STANDARD_STRING_HANDLE.to_le_bytes());
65 bytes
66 } else {
67 write_data_type(value).to_le_bytes().to_vec()
68 }
69}
70
71pub fn encode_payload(value: &PlcValue, buf: &mut BytesMut) {
73 match value {
74 PlcValue::Bool(v) => buf.put_u8(if *v { 0xFF } else { 0x00 }),
75 PlcValue::Sint(v) => buf.put_i8(*v),
76 PlcValue::Int(v) => buf.put_i16_le(*v),
77 PlcValue::Dint(v) => buf.put_i32_le(*v),
78 PlcValue::Lint(v) => buf.put_i64_le(*v),
79 PlcValue::Usint(v) => buf.put_u8(*v),
80 PlcValue::Uint(v) => buf.put_u16_le(*v),
81 PlcValue::Udint(v) => buf.put_u32_le(*v),
82 PlcValue::Ulint(v) => buf.put_u64_le(*v),
83 PlcValue::Real(v) => buf.put_slice(&v.to_le_bytes()),
84 PlcValue::Lreal(v) => buf.put_slice(&v.to_le_bytes()),
85 PlcValue::String(v) => encode_standard_string_payload(v, buf),
86 PlcValue::Udt(udt_data) => buf.put_slice(&udt_data.data),
87 }
88}
89
90pub fn encode_type_prefixed(value: &PlcValue, buf: &mut BytesMut) {
92 buf.put_slice(&write_data_type_bytes(value));
93 match value {
94 PlcValue::String(v) => encode_standard_string_payload(v, buf),
95 PlcValue::Udt(udt_data) => buf.put_slice(&udt_data.data),
96 _ => encode_payload(value, buf),
97 }
98}
99
100pub fn decode_payload(data_type: u16, value_data: &[u8]) -> Result<PlcValue> {
102 match data_type {
103 BOOL => {
104 require_len(value_data, 1, "BOOL")?;
105 Ok(PlcValue::Bool(value_data[0] != 0))
106 }
107 SINT => {
108 require_len(value_data, 1, "SINT")?;
109 Ok(PlcValue::Sint(value_data[0] as i8))
110 }
111 INT => {
112 require_len(value_data, 2, "INT")?;
113 Ok(PlcValue::Int(i16::from_le_bytes([
114 value_data[0],
115 value_data[1],
116 ])))
117 }
118 DINT => {
119 require_len(value_data, 4, "DINT")?;
120 Ok(PlcValue::Dint(i32::from_le_bytes([
121 value_data[0],
122 value_data[1],
123 value_data[2],
124 value_data[3],
125 ])))
126 }
127 LINT => {
128 require_len(value_data, 8, "LINT")?;
129 Ok(PlcValue::Lint(i64::from_le_bytes(
130 value_data[..8]
131 .try_into()
132 .expect("length checked before fixed-width LINT decode"),
133 )))
134 }
135 USINT => {
136 require_len(value_data, 1, "USINT")?;
137 Ok(PlcValue::Usint(value_data[0]))
138 }
139 UINT => {
140 require_len(value_data, 2, "UINT")?;
141 Ok(PlcValue::Uint(u16::from_le_bytes([
142 value_data[0],
143 value_data[1],
144 ])))
145 }
146 UDINT => {
147 require_len(value_data, 4, "UDINT")?;
148 Ok(PlcValue::Udint(u32::from_le_bytes([
149 value_data[0],
150 value_data[1],
151 value_data[2],
152 value_data[3],
153 ])))
154 }
155 ULINT => {
156 require_len(value_data, 8, "ULINT")?;
157 Ok(PlcValue::Ulint(u64::from_le_bytes(
158 value_data[..8]
159 .try_into()
160 .expect("length checked before fixed-width ULINT decode"),
161 )))
162 }
163 REAL => {
164 require_len(value_data, 4, "REAL")?;
165 Ok(PlcValue::Real(f32::from_le_bytes([
166 value_data[0],
167 value_data[1],
168 value_data[2],
169 value_data[3],
170 ])))
171 }
172 LREAL => {
173 require_len(value_data, 8, "LREAL")?;
174 Ok(PlcValue::Lreal(f64::from_le_bytes(
175 value_data[..8]
176 .try_into()
177 .expect("length checked before fixed-width LREAL decode"),
178 )))
179 }
180 STRING => decode_dint_string(value_data),
181 ALT_STRING => decode_short_string(value_data),
182 AB_UDT | UDT => decode_structure_payload(value_data),
183 BOOL_ARRAY_DWORD => {
184 if value_data.len() >= 4 {
185 Ok(PlcValue::Udint(u32::from_le_bytes([
186 value_data[0],
187 value_data[1],
188 value_data[2],
189 value_data[3],
190 ])))
191 } else {
192 Err(ProtocolError::new(
193 "Insufficient data for DWORD value".to_string(),
194 ))
195 }
196 }
197 _ => Err(ProtocolError::new(format!(
198 "Unsupported data type: 0x{data_type:04X}"
199 ))),
200 }
201}
202
203pub fn decode_array_element(data_type: u16, chunk: &[u8]) -> Result<PlcValue> {
205 decode_payload(data_type, chunk)
206}
207
208fn encode_standard_string_payload(value: &str, buf: &mut BytesMut) {
209 let string_bytes = value.as_bytes();
210 let data_len = string_bytes.len().min(STANDARD_STRING_DATA_LEN);
211 buf.put_u32_le(data_len as u32);
212 buf.put_slice(&string_bytes[..data_len]);
213 buf.resize(buf.len() + (STANDARD_STRING_DATA_LEN - data_len), 0);
214 buf.resize(buf.len() + STANDARD_STRING_PAD_LEN, 0);
215}
216
217fn decode_structure_payload(value_data: &[u8]) -> Result<PlcValue> {
218 if value_data.len() >= 2 {
219 let handle = u16::from_le_bytes([value_data[0], value_data[1]]);
220 if handle == STANDARD_STRING_HANDLE {
221 return decode_standard_string_structure(value_data);
222 }
223 }
224
225 Ok(PlcValue::Udt(UdtData {
226 symbol_id: 0,
227 data: value_data.to_vec(),
228 }))
229}
230
231fn decode_standard_string_structure(value_data: &[u8]) -> Result<PlcValue> {
232 let required = 2 + STANDARD_STRING_PAYLOAD_LEN;
233 if value_data.len() < required {
234 return Err(ProtocolError::new(format!(
235 "Insufficient data for standard STRING structure: need {required} bytes, have {} bytes",
236 value_data.len()
237 )));
238 }
239
240 let payload = &value_data[2..];
241 let length = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]) as usize;
242 if length > STANDARD_STRING_DATA_LEN {
243 return Err(ProtocolError::new(format!(
244 "Invalid standard STRING length: {length} > {STANDARD_STRING_DATA_LEN}"
245 )));
246 }
247
248 Ok(PlcValue::String(
249 String::from_utf8_lossy(&payload[4..4 + length]).to_string(),
250 ))
251}
252
253fn decode_dint_string(value_data: &[u8]) -> Result<PlcValue> {
254 if value_data.len() < 4 {
255 return Err(ProtocolError::new(
256 "Insufficient data for STRING length field".to_string(),
257 ));
258 }
259
260 let length =
261 u32::from_le_bytes([value_data[0], value_data[1], value_data[2], value_data[3]]) as usize;
262 if value_data.len() - 4 < length {
263 return Err(ProtocolError::new(format!(
264 "Insufficient data for STRING value: need {} bytes, have {} bytes",
265 4 + length,
266 value_data.len()
267 )));
268 }
269 Ok(PlcValue::String(
270 String::from_utf8_lossy(&value_data[4..4 + length]).to_string(),
271 ))
272}
273
274fn decode_short_string(value_data: &[u8]) -> Result<PlcValue> {
275 if value_data.is_empty() {
276 return Ok(PlcValue::String(String::new()));
277 }
278 let length = value_data[0] as usize;
279 if value_data.len() < 1 + length {
280 return Err(ProtocolError::new(
281 "Insufficient data for STRING value".to_string(),
282 ));
283 }
284 Ok(PlcValue::String(
285 String::from_utf8_lossy(&value_data[1..1 + length]).to_string(),
286 ))
287}
288
289fn require_len(value_data: &[u8], min_len: usize, name: &str) -> Result<()> {
290 if value_data.len() < min_len {
291 let msg = if min_len == 1 {
292 format!("No data for {name} value")
293 } else {
294 format!("Insufficient data for {name} value")
295 };
296 Err(ProtocolError::new(msg))
297 } else {
298 Ok(())
299 }
300}
301
302impl Encode for PlcValue {
303 fn encode(&self, buf: &mut BytesMut) {
304 encode_type_prefixed(self, buf);
305 }
306}
307
308impl Decode for PlcValue {
309 fn decode(buf: &mut impl Buf) -> Result<Self> {
310 if buf.remaining() < 2 {
311 return Err(ProtocolError::new("Data too short for type".to_string()));
312 }
313 let data_type = buf.get_u16_le();
314 let remaining = buf.copy_to_bytes(buf.remaining());
315 decode_payload(data_type, &remaining)
316 }
317}
318
319impl Encode for UdtData {
320 fn encode(&self, buf: &mut BytesMut) {
321 buf.put_slice(&self.data);
322 }
323}
324
325impl Decode for UdtData {
326 fn decode(buf: &mut impl Buf) -> Result<Self> {
327 let data = buf.copy_to_bytes(buf.remaining()).to_vec();
328 Ok(Self { symbol_id: 0, data })
329 }
330}