1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::proto::error::ProtoError;
4use crate::proto::s7::read_var::{AddressItem, ADDR_ANY, ITEM_LEN, ITEM_SPEC};
5
6pub const FUNC_WRITE_VAR: u8 = 0x05;
7
8#[derive(Debug, Clone)]
9pub struct WriteItem {
10 pub address: AddressItem,
11 pub data: Bytes,
12}
13
14#[derive(Debug, Clone)]
15pub struct WriteVarRequest {
16 pub items: Vec<WriteItem>,
17}
18
19#[derive(Debug, Clone)]
20pub struct WriteVarResponse {
21 pub return_codes: Vec<u8>,
22}
23
24impl WriteVarRequest {
25 pub fn decode(buf: &mut Bytes) -> Result<Self, ProtoError> {
26 use crate::proto::s7::header::{Area, TransportSize};
27
28 if buf.len() < 2 {
29 return Err(ProtoError::BufferTooShort {
30 need: 2,
31 have: buf.len(),
32 });
33 }
34 let func = buf.get_u8();
35 if func != FUNC_WRITE_VAR {
36 return Err(ProtoError::UnsupportedFunction(func));
37 }
38 let count = buf.get_u8() as usize;
39
40 let mut addresses = Vec::with_capacity(count);
42 for _ in 0..count {
43 if buf.len() < 12 {
44 return Err(ProtoError::BufferTooShort {
45 need: 12,
46 have: buf.len(),
47 });
48 }
49 let spec = buf.get_u8();
50 if spec != ITEM_SPEC {
51 return Err(ProtoError::InvalidMagic {
52 expected: ITEM_SPEC,
53 got: spec,
54 });
55 }
56 buf.get_u8(); let addr_type = buf.get_u8();
58 if addr_type != ADDR_ANY {
59 return Err(ProtoError::InvalidMagic {
60 expected: ADDR_ANY,
61 got: addr_type,
62 });
63 }
64 let transport = TransportSize::try_from(buf.get_u8())?;
65 let length = buf.get_u16();
66 let db_number = buf.get_u16();
67 let area = Area::try_from(buf.get_u8())?;
68 let b0 = buf.get_u8() as u32;
69 let b1 = buf.get_u8() as u32;
70 let b2 = buf.get_u8() as u32;
71 let addr_bits = (b0 << 16) | (b1 << 8) | b2;
72 let start = addr_bits >> 3;
73 let bit_offset = (addr_bits & 0x07) as u8;
74 addresses.push(AddressItem {
75 area,
76 db_number,
77 start,
78 bit_offset,
79 length,
80 transport,
81 });
82 }
83
84 let mut items = Vec::with_capacity(count);
86 for address in addresses {
87 if buf.len() < 4 {
88 return Err(ProtoError::BufferTooShort {
89 need: 4,
90 have: buf.len(),
91 });
92 }
93 buf.get_u8(); buf.get_u8(); let bit_len = buf.get_u16() as usize;
96 let byte_len = bit_len.div_ceil(8);
97 if buf.len() < byte_len {
98 return Err(ProtoError::BufferTooShort {
99 need: byte_len,
100 have: buf.len(),
101 });
102 }
103 let data = buf.copy_to_bytes(byte_len);
104 if !byte_len.is_multiple_of(2) && buf.has_remaining() {
106 buf.advance(1);
107 }
108 items.push(WriteItem { address, data });
109 }
110
111 Ok(WriteVarRequest { items })
112 }
113
114 pub fn encode(&self, buf: &mut BytesMut) {
115 buf.put_u8(FUNC_WRITE_VAR);
116 buf.put_u8(self.items.len() as u8);
117 for item in &self.items {
119 let addr = &item.address;
120 buf.put_u8(ITEM_SPEC);
121 buf.put_u8(ITEM_LEN);
122 buf.put_u8(ADDR_ANY);
123 buf.put_u8(addr.transport as u8);
124 buf.put_u16(addr.length);
125 buf.put_u16(addr.db_number);
126 buf.put_u8(addr.area as u8);
127 let addr_bits = (addr.start * 8) | (addr.bit_offset as u32);
128 buf.put_u8(((addr_bits >> 16) & 0xFF) as u8);
129 buf.put_u8(((addr_bits >> 8) & 0xFF) as u8);
130 buf.put_u8((addr_bits & 0xFF) as u8);
131 }
132 for item in &self.items {
134 buf.put_u8(0x00); buf.put_u8(item.address.transport as u8);
136 let bit_len = (item.data.len() * 8) as u16;
137 buf.put_u16(bit_len);
138 buf.put_slice(&item.data);
139 if !item.data.len().is_multiple_of(2) {
140 buf.put_u8(0x00); }
142 }
143 }
144}
145
146impl WriteVarResponse {
147 pub fn decode(buf: &mut Bytes, item_count: usize) -> Result<Self, ProtoError> {
148 let mut return_codes = Vec::with_capacity(item_count);
149 for _ in 0..item_count {
150 if !buf.has_remaining() {
151 return Err(ProtoError::BufferTooShort { need: 1, have: 0 });
152 }
153 return_codes.push(buf.get_u8());
154 }
155 Ok(WriteVarResponse { return_codes })
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use bytes::Bytes;
163
164 use crate::proto::s7::header::{Area, TransportSize};
165 use crate::proto::s7::read_var::AddressItem;
166
167 fn make_write_item(db: u16, start: u32, data: &[u8]) -> WriteItem {
168 WriteItem {
169 address: AddressItem {
170 area: Area::DataBlock,
171 db_number: db,
172 start,
173 bit_offset: 0,
174 length: data.len() as u16,
175 transport: TransportSize::Byte,
176 },
177 data: Bytes::copy_from_slice(data),
178 }
179 }
180
181 #[test]
182 fn write_var_response_decode_ok() {
183 let raw: &[u8] = &[0xFF];
184 let mut b = Bytes::copy_from_slice(raw);
185 let resp = WriteVarResponse::decode(&mut b, 1).unwrap();
186 assert_eq!(resp.return_codes[0], 0xFF);
187 }
188
189 #[test]
190 fn write_var_response_decode_multi() {
191 let raw: &[u8] = &[0xFF, 0xFF, 0x05]; let mut b = Bytes::copy_from_slice(raw);
193 let resp = WriteVarResponse::decode(&mut b, 3).unwrap();
194 assert_eq!(resp.return_codes, vec![0xFF, 0xFF, 0x05]);
195 }
196
197 #[test]
198 fn write_var_request_encode_structure() {
199 let req = WriteVarRequest {
200 items: vec![make_write_item(1, 0, &[0xDE, 0xAD])],
201 };
202 let mut buf = BytesMut::new();
203 req.encode(&mut buf);
204 assert_eq!(buf[0], FUNC_WRITE_VAR);
206 assert_eq!(buf[1], 1);
207 assert_eq!(buf[14], 0x00); assert_eq!(buf[15], TransportSize::Byte as u8);
211 assert_eq!(buf[16], 0x00);
212 assert_eq!(buf[17], 0x10); assert_eq!(buf[18], 0xDE);
214 assert_eq!(buf[19], 0xAD);
215 }
216
217 #[test]
218 fn write_var_request_odd_data_padded() {
219 let req = WriteVarRequest {
220 items: vec![make_write_item(1, 0, &[0xAB])], };
222 let mut buf = BytesMut::new();
223 req.encode(&mut buf);
224 assert_eq!(buf.len(), 20);
226 assert_eq!(buf[19], 0x00); }
228
229 #[test]
230 fn write_var_response_truncated_returns_err() {
231 let mut b = Bytes::new();
232 assert!(WriteVarResponse::decode(&mut b, 1).is_err());
233 }
234
235 #[test]
236 fn write_var_request_encode_decode_roundtrip() {
237 let item = make_write_item(3, 4, &[0x11, 0x22, 0x33, 0x44]);
238 let req = WriteVarRequest { items: vec![item] };
239 let mut buf = BytesMut::new();
240 req.encode(&mut buf);
241 let mut b = buf.freeze();
242 let decoded = WriteVarRequest::decode(&mut b).unwrap();
243 assert_eq!(decoded.items.len(), 1);
244 assert_eq!(decoded.items[0].address.db_number, 3);
245 assert_eq!(decoded.items[0].address.start, 4);
246 assert_eq!(decoded.items[0].data.as_ref(), &[0x11, 0x22, 0x33, 0x44]);
247 }
248
249 #[test]
250 fn write_var_request_decode_wrong_func_returns_err() {
251 let raw: &[u8] = &[0x04, 0x01];
253 let mut b = Bytes::copy_from_slice(raw);
254 assert!(WriteVarRequest::decode(&mut b).is_err());
255 }
256}