Skip to main content

snap7_client/proto/s7commplus/
multivar.rs

1use crate::proto::error::ProtoError;
2use crate::proto::s7commplus::data::DataArea;
3use crate::proto::s7commplus::session::{FC_GET_MULTI_VAR, FC_SET_MULTI_VAR, OPCODE_REQUEST};
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5
6#[derive(Debug)]
7pub struct GetVarRequest {
8    pub seqnum: u16,
9    pub session_id: u32,
10    pub crc: u32,
11    pub lid: u32,
12}
13
14impl GetVarRequest {
15    pub fn encode(&self, buf: &mut BytesMut) {
16        let mut payload = BytesMut::with_capacity(9);
17        payload.put_u8(0x01); // one variable
18        payload.put_u32(self.crc);
19        payload.put_u32(self.lid);
20        DataArea {
21            opcode: OPCODE_REQUEST,
22            function_code: FC_GET_MULTI_VAR,
23            seqnum: self.seqnum,
24            session_id: self.session_id,
25            transport_flags: 0,
26            payload: payload.freeze(),
27        }
28        .encode(buf);
29    }
30}
31
32#[derive(Debug)]
33pub struct GetVarResponse {
34    pub return_code: u8,
35    pub value: Bytes,
36}
37
38impl GetVarResponse {
39    pub fn decode(buf: &mut Bytes) -> Result<Self, ProtoError> {
40        let da = DataArea::decode(buf)?;
41        let mut payload = da.payload;
42        if payload.is_empty() {
43            return Err(ProtoError::BufferTooShort { need: 3, have: 0 });
44        }
45        let return_code = payload.get_u8();
46        if payload.len() < 2 {
47            return Err(ProtoError::BufferTooShort {
48                need: 2,
49                have: payload.len(),
50            });
51        }
52        let data_len = payload.get_u16() as usize;
53        if payload.len() < data_len {
54            return Err(ProtoError::BufferTooShort {
55                need: data_len,
56                have: payload.len(),
57            });
58        }
59        let value = payload.copy_to_bytes(data_len);
60        Ok(GetVarResponse { return_code, value })
61    }
62}
63
64#[derive(Debug)]
65pub struct SetVarRequest {
66    pub seqnum: u16,
67    pub session_id: u32,
68    pub crc: u32,
69    pub lid: u32,
70    pub value: Bytes,
71}
72
73impl SetVarRequest {
74    pub fn encode(&self, buf: &mut BytesMut) {
75        let mut payload = BytesMut::with_capacity(9 + 2 + self.value.len());
76        payload.put_u8(0x01); // one variable
77        payload.put_u32(self.crc);
78        payload.put_u32(self.lid);
79        payload.put_u16(self.value.len() as u16);
80        payload.put_slice(&self.value);
81        DataArea {
82            opcode: OPCODE_REQUEST,
83            function_code: FC_SET_MULTI_VAR,
84            seqnum: self.seqnum,
85            session_id: self.session_id,
86            transport_flags: 0,
87            payload: payload.freeze(),
88        }
89        .encode(buf);
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use bytes::{Bytes, BytesMut};
97
98    #[test]
99    fn get_var_request_function_code() {
100        let req = GetVarRequest {
101            seqnum: 1,
102            session_id: 0xDEAD0001,
103            crc: 0xABCD1234,
104            lid: 2,
105        };
106        let mut buf = BytesMut::new();
107        req.encode(&mut buf);
108        assert_eq!(u16::from_be_bytes([buf[3], buf[4]]), 0x054C);
109    }
110
111    #[test]
112    fn get_var_request_session_id_position() {
113        let req = GetVarRequest {
114            seqnum: 2,
115            session_id: 0x12345678,
116            crc: 0xAABBCCDD,
117            lid: 1,
118        };
119        let mut buf = BytesMut::new();
120        req.encode(&mut buf);
121        // session_id at bytes 9-12 (opcode=1 + res=2 + fc=2 + res=2 + seq=2)
122        let sid = u32::from_be_bytes([buf[9], buf[10], buf[11], buf[12]]);
123        assert_eq!(sid, 0x12345678);
124    }
125
126    #[test]
127    fn set_var_request_function_code() {
128        let req = SetVarRequest {
129            seqnum: 3,
130            session_id: 5,
131            crc: 0x11223344,
132            lid: 1,
133            value: Bytes::from_static(&[0x3F, 0x80, 0x00, 0x00]),
134        };
135        let mut buf = BytesMut::new();
136        req.encode(&mut buf);
137        assert_eq!(u16::from_be_bytes([buf[3], buf[4]]), 0x0542);
138    }
139
140    #[test]
141    fn get_var_response_decode() {
142        use bytes::BufMut;
143        let mut buf = BytesMut::new();
144        buf.put_u8(0x32);
145        buf.put_u16(0x0000);
146        buf.put_u16(0x054C);
147        buf.put_u16(0x0000);
148        buf.put_u16(0x0001);
149        buf.put_u32(0x00000005);
150        buf.put_u8(0x00);
151        buf.put_u8(0x0A); // return_code OK
152        buf.put_u16(4);
153        buf.put_slice(&[0x3F, 0x80, 0x00, 0x00]);
154        let mut b = buf.freeze();
155        let resp = GetVarResponse::decode(&mut b).unwrap();
156        assert_eq!(resp.return_code, 0x0A);
157        assert_eq!(&resp.value[..], &[0x3F, 0x80, 0x00, 0x00]);
158    }
159}