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;
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}