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