ads_client/ads_read.rs
1use bytes::{Bytes, BytesMut};
2use log::info;
3use crate::{Client, Result, AdsCommand, AdsError, AdsErrorCode, HEADER_SIZE, LEN_READ_REQ, misc::HandleData};
4
5impl Client {
6
7 fn pre_read(&self, idx_grp: u32, idx_offs: u32, rd_len : usize, invoke_id: u32) -> Bytes {
8 let ams_header = self.c_init_ams_header(invoke_id, Some(LEN_READ_REQ as u32), AdsCommand::Read);
9 let mut read_header : [u8; LEN_READ_REQ] = [0; LEN_READ_REQ];
10
11 let read_length = rd_len as u32;
12
13 read_header[0..4].copy_from_slice(&idx_grp.to_ne_bytes());
14 read_header[4..8].copy_from_slice(&idx_offs.to_ne_bytes());
15 read_header[8..12].copy_from_slice(&read_length.to_ne_bytes());
16
17 // Assemble read request: Create two iterators and chain them
18 let iter_ams_header = ams_header.into_iter();
19 let iter_read = read_header.into_iter();
20
21 let mut _read_request = BytesMut::with_capacity(HEADER_SIZE + LEN_READ_REQ);
22 _read_request = iter_ams_header.chain(iter_read).collect();
23
24 _read_request.freeze()
25 }
26
27 fn post_read(read_response : HandleData, data: &mut [u8]) -> Result<u32> {
28
29
30 let payload = read_response.payload
31 .ok_or_else(|| AdsError{n_error : AdsErrorCode::ADSERR_DEVICE_INVALIDDATA.into(), s_msg : String::from("Invalid data values.")})?;
32
33 Client::eval_ams_error(read_response.ams_err)?;
34 Client::eval_return_code(payload.as_ref())?;
35
36 // Copy payload to destination argument
37 // Payload starts at offset 8
38 let iter_payload = payload[8..].into_iter();
39 let iter_read_data = data.iter_mut();
40
41 // Zip payload and destination together
42 let iter_data = iter_read_data.zip(iter_payload);
43
44 // Iterate till the first iterator is exhausted
45 for data in iter_data {
46 let (rd, pl) = data;
47 *rd = *pl; // Copy from response to data
48 }
49
50 Ok(payload[8..].len() as u32)
51 }
52 /// Submit an asynchronous [ADS Read](https://infosys.beckhoff.com/content/1033/tc3_ads_intro/115876875.html?id=4960931295000833536) request.
53 ///
54 ///
55 /// # Example
56 ///
57 /// ```rust
58 ///use ads_client::{ClientBuilder, Result};
59 ///
60 ///#[tokio::main]
61 ///async fn main() -> Result<()> {
62 /// let ads_client = ClientBuilder::new("5.80.201.232.1.1", 851).build().await?;
63 ///
64 /// // Get symbol handle
65 /// let mut hdl : [u8; 4] = [0; 4];
66 /// let symbol = b"MAIN.n_cnt_a";
67 ///
68 /// if let Err(err) = ads_client.read_write(0xF003, 0, &mut hdl, symbol).await{
69 /// println!("Error: {}", err.to_string());
70 /// }
71 ///
72 /// let n_hdl = u32::from_ne_bytes(hdl.try_into().unwrap());
73 ///
74 /// if n_hdl != 0 {
75 /// println!("Got handle!");
76 ///
77 /// let mut plc_n_cnt_a : [u8; 2] = [0; 2];
78 ///
79 ///
80 /// let read_hdl = ads_client.read(0xF005, n_hdl, &mut plc_n_cnt_a).await;
81 ///
82 /// match read_hdl {
83 /// Ok(_bytes_read) => {
84 /// let n_cnt_a = u16::from_ne_bytes(plc_n_cnt_a.try_into().unwrap());
85 /// println!("MAIN.n_cnt_a: {}", n_cnt_a);
86 /// },
87 /// Err(err) => println!("Read failed: {}", err.to_string())
88 /// }
89 /// }
90 /// Ok(())
91 ///}
92 /// ```
93 /// Checkout the examples [read_symbol](https://github.com/hANSIc99/ads_client/blob/main/examples/read_symbol.rs)
94 /// and [read_symbol_async](https://github.com/hANSIc99/ads_client/blob/main/examples/read_symbol_async.rs).
95 pub async fn read(&self, idx_grp: u32, idx_offs: u32, data: &mut [u8]) -> Result<u32> {
96 // Preprocessing
97 let invoke_id = self.create_invoke_id();
98 let _read_req = self.pre_read(idx_grp, idx_offs, data.len(), invoke_id);
99
100 info!("Submit Read Request: Invoke ID: {}, Read length: {}", invoke_id, data.len());
101
102 // Create handle
103 self.register_command_handle(invoke_id, AdsCommand::Read);
104
105 // Launch the CommandManager future
106 let cmd_man_future = self.create_cmd_man_future(invoke_id);
107
108 // Launch socket future
109 let socket_future = self.socket_write(&_read_req);
110
111 // https://docs.rs/tokio/latest/tokio/macro.try_join.html
112 // INFO https://stackoverflow.com/questions/69031447/tokiotry-join-doesnt-return-the-err-variant-when-one-of-the-tasks-returns-er
113
114 tokio::try_join!(cmd_man_future, socket_future).and_then(| (rd_response, _) | Client::post_read(rd_response, data))
115 }
116}