rust_snap7/
client.rs

1//
2// snap.rs
3// Copyright (C) 2021 gmg137 <gmg137 AT live.com>
4// snap7-rs is licensed under Mulan PSL v2.
5// You can use this software according to the terms and conditions of the Mulan PSL v2.
6// You may obtain a copy of Mulan PSL v2 at:
7//          http://license.coscl.org.cn/MulanPSL2
8// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11// See the Mulan PSL v2 for more details.
12//
13use crate::{ffi::*, model::*};
14use anyhow::*;
15use std::{
16    ffi::{CStr, CString},
17    os::raw::*,
18};
19
20/// S7 客户端
21///
22/// # Examples
23/// ```ignore
24/// use snap7_rs::S7Client;
25/// use std::ffi::*;
26/// use std::os::raw::*;
27///
28/// // 创建 S7 客户端
29/// let client = S7Client::create();
30/// // 连接到 PLC
31/// if client.connect_to("192.168.1.123", 0, 1).is_ok() {
32///     // 创建一个数据缓冲区
33///     let mut buff = [0u8; 2];
34///     // 从 PLC 读取 DB1.WDB20 的值
35///     if client.db_read(1, 20, 2, &mut buff).is_ok() {
36///         println!("DB1.W20: {}", u16::from_be_bytes([buff[0], buff[1]]));
37///     } else {
38///         println!("读取 DB 失败!");
39///     }
40/// } else {
41///     println!("连接 PLC 失败!");
42/// }
43/// ```
44pub struct S7Client {
45    handle: usize,
46}
47
48impl Drop for S7Client {
49    fn drop(&mut self) {
50        unsafe {
51            Cli_Destroy(&mut self.handle as *mut S7Object);
52        }
53    }
54}
55
56impl Default for S7Client {
57    fn default() -> Self {
58        Self::create()
59    }
60}
61
62impl S7Client {
63    /// 创建 S7 客户端。
64    pub fn create() -> S7Client {
65        S7Client {
66            handle: unsafe { Cli_Create() },
67        }
68    }
69
70    ///
71    /// 设置客户端连接参数。
72    ///
73    /// **输入参数:**
74    ///
75    ///  - value: 连接类型
76    ///
77    /// **返回值:**
78    ///
79    ///  - Ok: 设置成功
80    ///  - Err: 设置失败
81    ///
82    pub fn set_connection_type(&self, value: ConnType) -> Result<()> {
83        let value = match value {
84            ConnType::PG => 0x01,
85            ConnType::OP => 0x02,
86            ConnType::S7Basic(v) => v,
87        };
88        unsafe {
89            let res = Cli_SetConnectionType(self.handle, value);
90            if res == 0 {
91                return Ok(());
92            }
93            bail!("{}", Self::error_text(res))
94        };
95    }
96
97    ///
98    /// 通过指定 IP 和机架号、插槽号连接到 PLC。
99    ///
100    /// **输入参数:**
101    ///
102    ///  - address: PLC 地址
103    ///  - rack: 机架号(0..7)
104    ///  - slot: 插槽号(1..31)
105    ///
106    /// **返回值:**
107    ///
108    ///  - Ok: 设置成功
109    ///  - Err: 设置失败
110    ///
111    /// **机架号和插槽号规则:**
112    ///
113    /// |    CPU    | 机架 | 插槽 |
114    /// | --------- | ---- | ---- |
115    /// |   S7 200  |   0  |   1  |
116    /// |   S7 300  |   0  |   2  |
117    /// |   S7 1200 |   0  | 0(1) |
118    /// |   S7 1500 |   0  | 0(1) |
119    ///
120    /// `注:其它 CPU 按硬件配置设置`
121    ///
122    pub fn connect_to(&self, address: &str, rack: i32, slot: i32) -> Result<()> {
123        let address = CString::new(address).unwrap();
124        let res =
125            unsafe { Cli_ConnectTo(self.handle, address.as_ptr(), rack as c_int, slot as c_int) };
126        if res == 0 {
127            return Ok(());
128        }
129        bail!("{}", Self::error_text(res))
130    }
131
132    ///
133    /// 设置内部(IP,本地TSAP,远程TSAP)地址。
134    ///
135    /// **输入参数:**
136    ///
137    ///  - address: PLC 地址
138    ///  - local_tsap: 本地 TSAP
139    ///  - remote_tsap: 远程 TSAP
140    ///
141    /// **返回值:**
142    ///
143    ///  - Ok: 设置成功
144    ///  - Err: 设置失败
145    ///
146    /// `注:此函数必须在 connect() 之前调用。`
147    ///
148    pub fn set_connection_params(
149        &self,
150        address: &str,
151        local_tsap: u16,
152        remote_tsap: u16,
153    ) -> Result<()> {
154        let address = CString::new(address).unwrap();
155        let res = unsafe {
156            Cli_SetConnectionParams(self.handle, address.as_ptr(), local_tsap, remote_tsap)
157        };
158        if res == 0 {
159            return Ok(());
160        }
161        bail!("{}", Self::error_text(res))
162    }
163
164    ///
165    /// 通过调用 connect_to() 或 set_connection_params() 中指定的参数,将客户端连接到PLC。
166    ///
167    /// **返回值:**
168    ///
169    ///  - Ok: 操作成功
170    ///  - Err: 操作失败
171    ///
172    /// `注: 只有在调用 connect_to() 或 set_connection_params() 后才能调用该函数。`
173    ///
174    pub fn connect(&self) -> Result<()> {
175        let res = unsafe { Cli_Connect(self.handle) };
176        if res == 0 {
177            return Ok(());
178        }
179        bail!("{}", Self::error_text(res))
180    }
181
182    ///
183    /// "优雅地"从 PLC 上断开客户端的连接。
184    ///
185    /// **返回值:**
186    ///
187    ///  - Ok: 操作成功
188    ///  - Err: 操作失败
189    ///
190    /// `注: 如果客户端参数是一个有效的句柄,这个函数总是返回 true,它可以被安全地多次调用。这个函数在 S7Client drop 时也会被调用。`
191    ///
192    pub fn disconnect(&self) -> Result<()> {
193        let res = unsafe { Cli_Disconnect(self.handle) };
194        if res == 0 {
195            return Ok(());
196        }
197        bail!("{}", Self::error_text(res))
198    }
199
200    ///
201    /// 读取客户端的内部参数。
202    ///
203    /// **输入参数:**
204    ///
205    ///  - param: 内部参数类型
206    ///  - value: 参数值
207    ///
208    /// **返回值:**
209    ///
210    ///  - Ok: 操作成功
211    ///  - Err: 操作失败
212    ///
213    pub fn get_param(&self, param: InternalParam, value: &mut InternalParamValue) -> Result<()> {
214        match param {
215            InternalParam::KeepAliveTime | InternalParam::RecoveryTime => unsafe {
216                let mut buff = [0u8; 4];
217                let res = Cli_GetParam(
218                    self.handle,
219                    param as c_int,
220                    &mut buff as *mut [u8] as *mut c_void,
221                );
222                if res == 0 {
223                    *value = InternalParamValue::U32(u32::from_le_bytes(buff));
224                    Ok(())
225                } else {
226                    bail!("{}", Self::error_text(res))
227                }
228            },
229            InternalParam::LocalPort
230            | InternalParam::RemotePort
231            | InternalParam::DstRef
232            | InternalParam::SrcTSap
233            | InternalParam::SrcRef => unsafe {
234                let mut buff = [0u8; 2];
235                let res = Cli_GetParam(
236                    self.handle,
237                    param as c_int,
238                    &mut buff as *mut [u8] as *mut c_void,
239                );
240                if res == 0 {
241                    *value = InternalParamValue::U16(u16::from_le_bytes(buff));
242                    Ok(())
243                } else {
244                    bail!("{}", Self::error_text(res))
245                }
246            },
247            _ => unsafe {
248                let mut buff = [0u8; 4];
249                let res = Cli_GetParam(
250                    self.handle,
251                    param as c_int,
252                    &mut buff as *mut [u8] as *mut c_void,
253                );
254                if res == 0 {
255                    *value = InternalParamValue::I32(i32::from_le_bytes(buff));
256                    Ok(())
257                } else {
258                    bail!("{}", Self::error_text(res))
259                }
260            },
261        }
262    }
263
264    ///
265    /// 设置客户端的内部参数。
266    ///
267    /// **输入参数:**
268    ///
269    ///  - param: 内部参数类型
270    ///  - value: 内部参数值
271    ///
272    /// **返回值:**
273    ///
274    ///  - Ok: 操作成功
275    ///  - Err: 操作失败
276    ///
277    pub fn set_param(&self, param: InternalParam, value: InternalParamValue) -> Result<()> {
278        match param {
279            InternalParam::KeepAliveTime | InternalParam::RecoveryTime => unsafe {
280                if let InternalParamValue::U32(v) = value {
281                    let mut buff = v.to_le_bytes();
282                    let res = Cli_SetParam(
283                        self.handle,
284                        param as c_int,
285                        &mut buff as *mut [u8] as *mut c_void,
286                    );
287                    if res == 0 {
288                        return Ok(());
289                    }
290                    bail!("{}", Self::error_text(res))
291                } else {
292                    bail!("{}", Self::error_text(-1))
293                }
294            },
295            InternalParam::LocalPort
296            | InternalParam::RemotePort
297            | InternalParam::DstRef
298            | InternalParam::SrcTSap
299            | InternalParam::SrcRef => unsafe {
300                if let InternalParamValue::U16(v) = value {
301                    let mut buff = v.to_le_bytes();
302                    let res = Cli_SetParam(
303                        self.handle,
304                        param as c_int,
305                        &mut buff as *mut [u8] as *mut c_void,
306                    );
307                    if res == 0 {
308                        return Ok(());
309                    }
310                    bail!("{}", Self::error_text(res))
311                } else {
312                    bail!("{}", Self::error_text(-1))
313                }
314            },
315            _ => unsafe {
316                if let InternalParamValue::I32(v) = value {
317                    let mut buff = v.to_le_bytes();
318                    let res = Cli_SetParam(
319                        self.handle,
320                        param as c_int,
321                        &mut buff as *mut [u8] as *mut c_void,
322                    );
323                    if res == 0 {
324                        return Ok(());
325                    }
326                    bail!("{}", Self::error_text(res))
327                } else {
328                    bail!("{}", Self::error_text(-1))
329                }
330            },
331        }
332    }
333
334    ///
335    /// 从 PLC 中读取数据, 你可以读取数据块(DB)、输入、输出、内部标志位(Merkers)、定时器和计数器。
336    ///
337    /// **输入参数:**
338    ///
339    ///  - area: 要读取的区域
340    ///  - db_number: 要读取的数据块(DB)编号。如果区域不为 S7AreaDB 则被忽略,值为 0。
341    ///  - start: 开始读取的字节索引
342    ///  - size: 要读取的字节长度
343    ///  - buff: 待读取数据缓冲区
344    ///
345    /// **返回值:**
346    ///
347    ///  - Ok: 操作成功
348    ///  - Err: 操作失败
349    ///
350    /// `注:
351    /// (1) 如果你需要传输一个大的数据,你可以考虑使用异步的 as_read_area()。
352    /// (2) 当 word_len=S7WLBit 时,Offset(start) 必须以比特表示。
353    /// 示例: DB4.DBX 10.3 的起点是 (10*8)+3=83`
354    ///
355    pub fn read_area(
356        &self,
357        area: AreaTable,
358        db_number: i32,
359        start: i32,
360        size: i32,
361        word_len: WordLenTable,
362        buff: &mut [u8],
363    ) -> Result<()> {
364        let res = unsafe {
365            Cli_ReadArea(
366                self.handle,
367                area as c_int,
368                db_number as c_int,
369                start as c_int,
370                size as c_int,
371                word_len as c_int,
372                buff as *mut [u8] as *mut c_void,
373            )
374        };
375        if res == 0 {
376            return Ok(());
377        }
378        bail!("{}", Self::error_text(res))
379    }
380
381    ///
382    /// 将数据写入到 PLC, 这是 read_area() 的补充函数。
383    ///
384    /// **输入参数:**
385    ///
386    ///  - area: 要读取的区域
387    ///  - db_number: 要读取的数据块(DB)编号。如果区域不为 S7AreaDB 则被忽略,值为 0。
388    ///  - start: 开始读取的字节索引
389    ///  - size: 要读取的字节长度
390    ///  - buff: 待写入数据缓冲区
391    ///
392    /// **返回值:**
393    ///
394    ///  - Ok: 操作成功
395    ///  - Err: 操作失败
396    ///
397    /// `注:
398    /// (1) 如果你需要传输一个大的数据,你可以考虑使用异步的 as_write_area()。
399    /// (2) 当 word_len=S7WLBit 时,Offset(start) 必须以比特表示。
400    /// 示例: DB4.DBX 10.3 的起点是 (10*8)+3=83`
401    ///
402    pub fn write_area(
403        &self,
404        area: AreaTable,
405        db_number: i32,
406        start: i32,
407        size: i32,
408        word_len: WordLenTable,
409        buff: &mut [u8],
410    ) -> Result<()> {
411        let res = unsafe {
412            Cli_WriteArea(
413                self.handle,
414                area as c_int,
415                db_number as c_int,
416                start as c_int,
417                size as c_int,
418                word_len as c_int,
419                buff as *mut [u8] as *mut c_void,
420            )
421        };
422        if res == 0 {
423            return Ok(());
424        }
425        bail!("{}", Self::error_text(res))
426    }
427
428    ///
429    /// 从 PLC DB 区读取数据。
430    ///
431    /// ```text
432    /// 这是 read_area() 的一个精简函数,它从内部调用了 read_area(), 其内容为:
433    ///     area = S7AreaDB.
434    ///     word_len = S7WLBytes.
435    /// ```
436    ///
437    /// **输入参数:**
438    ///
439    ///  - db_number: 要读取的数据块(DB)编号
440    ///  - start: 开始读取的字节索引
441    ///  - size: 要读取的字节长度
442    ///  - buff: 待写入数据缓冲区
443    ///
444    /// **返回值:**
445    ///
446    ///  - Ok: 操作成功
447    ///  - Err: 操作失败
448    ///
449    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_db_read()。`
450    ///
451    pub fn db_read(&self, db_number: i32, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
452        let res = unsafe {
453            Cli_DBRead(
454                self.handle,
455                db_number as c_int,
456                start as c_int,
457                size as c_int,
458                buff as *mut [u8] as *mut c_void,
459            )
460        };
461        if res == 0 {
462            return Ok(());
463        }
464        bail!("{}", Self::error_text(res))
465    }
466
467    ///
468    /// 向 PLC DB 区写入数据。
469    ///
470    /// ```markdown
471    /// 这是 write_area() 的一个精简函数,它从内部调用了 write_area(), 其内容为:
472    ///     area = S7AreaDB.
473    ///     word_len = S7WLBytes.
474    /// ```
475    ///
476    /// **输入参数:**
477    ///
478    ///  - db_number: 要读取的数据块(DB)编号
479    ///  - start: 开始读取的字节索引
480    ///  - size: 要读取的字节长度
481    ///  - buff: 待写入数据缓冲区
482    ///
483    /// **返回值:**
484    ///
485    ///  - Ok: 操作成功
486    ///  - Err: 操作失败
487    ///
488    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_db_write()。`
489    ///
490    pub fn db_write(&self, db_number: i32, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
491        let res = unsafe {
492            Cli_DBWrite(
493                self.handle,
494                db_number as c_int,
495                start as c_int,
496                size as c_int,
497                buff as *mut [u8] as *mut c_void,
498            )
499        };
500        if res == 0 {
501            return Ok(());
502        }
503        bail!("{}", Self::error_text(res))
504    }
505
506    ///
507    /// 从 PLC 输出区读取数据。
508    ///
509    /// ```text
510    /// 这是 read_area() 的一个精简函数,它从内部调用了 read_area(), 其内容为:
511    ///     area = S7AreaPA.
512    ///     word_len = S7WLBytes.
513    /// ```
514    ///
515    /// **输入参数:**
516    ///
517    ///  - start: 开始读取的字节索引
518    ///  - size: 要读取的字节长度
519    ///  - buff: 待写入数据缓冲区
520    ///
521    /// **返回值:**
522    ///
523    ///  - Ok: 操作成功
524    ///  - Err: 操作失败
525    ///
526    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_ab_read()。`
527    ///
528    pub fn ab_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
529        let res = unsafe {
530            Cli_ABRead(
531                self.handle,
532                start as c_int,
533                size as c_int,
534                buff as *mut [u8] as *mut c_void,
535            )
536        };
537        if res == 0 {
538            return Ok(());
539        }
540        bail!("{}", Self::error_text(res))
541    }
542
543    ///
544    /// 向 PLC 输出区写入数据。
545    ///
546    /// ```markdown
547    /// 这是 write_area() 的一个精简函数,它从内部调用了 write_area(), 其内容为:
548    ///     area = S7AreaPA.
549    ///     word_len = S7WLBytes.
550    /// ```
551    ///
552    /// **输入参数:**
553    ///
554    ///  - start: 开始读取的字节索引
555    ///  - size: 要读取的字节长度
556    ///  - buff: 待写入数据缓冲区
557    ///
558    /// **返回值:**
559    ///
560    ///  - Ok: 操作成功
561    ///  - Err: 操作失败
562    ///
563    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_ab_write()。`
564    ///
565    pub fn ab_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
566        let res = unsafe {
567            Cli_ABWrite(
568                self.handle,
569                start as c_int,
570                size as c_int,
571                buff as *mut [u8] as *mut c_void,
572            )
573        };
574        if res == 0 {
575            return Ok(());
576        }
577        bail!("{}", Self::error_text(res))
578    }
579
580    ///
581    /// 从 PLC 输入区读取数据。
582    ///
583    /// ```text
584    /// 这是 read_area() 的一个精简函数,它从内部调用了 read_area(), 其内容为:
585    ///     area = S7AreaPE.
586    ///     word_len = S7WLBytes.
587    /// ```
588    ///
589    /// **输入参数:**
590    ///
591    ///  - start: 开始读取的字节索引
592    ///  - size: 要读取的字节长度
593    ///  - buff: 待写入数据缓冲区
594    ///
595    /// **返回值:**
596    ///
597    ///  - Ok: 操作成功
598    ///  - Err: 操作失败
599    ///
600    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_eb_read()。`
601    ///
602    pub fn eb_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
603        let res = unsafe {
604            Cli_EBRead(
605                self.handle,
606                start as c_int,
607                size as c_int,
608                buff as *mut [u8] as *mut c_void,
609            )
610        };
611        if res == 0 {
612            return Ok(());
613        }
614        bail!("{}", Self::error_text(res))
615    }
616
617    ///
618    /// 向 PLC 输入区写入数据。
619    ///
620    /// ```markdown
621    /// 这是 write_area() 的一个精简函数,它从内部调用了 write_area(), 其内容为:
622    ///     area = S7AreaPE.
623    ///     word_len = S7WLBytes.
624    /// ```
625    ///
626    /// **输入参数:**
627    ///
628    ///  - start: 开始读取的字节索引
629    ///  - size: 要读取的字节长度
630    ///  - buff: 待写入数据缓冲区
631    ///
632    /// **返回值:**
633    ///
634    ///  - Ok: 操作成功
635    ///  - Err: 操作失败
636    ///
637    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_eb_write()。`
638    ///
639    pub fn eb_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
640        let res = unsafe {
641            Cli_EBWrite(
642                self.handle,
643                start as c_int,
644                size as c_int,
645                buff as *mut [u8] as *mut c_void,
646            )
647        };
648        if res == 0 {
649            return Ok(());
650        }
651        bail!("{}", Self::error_text(res))
652    }
653
654    ///
655    /// 从 PLC 内部标志位(Merkers)读取数据。
656    ///
657    /// ```text
658    /// 这是 read_area() 的一个精简函数,它从内部调用了 read_area(), 其内容为:
659    ///     area = S7AreaMK.
660    ///     word_len = S7WLBytes.
661    /// ```
662    ///
663    /// **输入参数:**
664    ///
665    ///  - start: 开始读取的字节索引
666    ///  - size: 要读取的字节长度
667    ///  - buff: 待写入数据缓冲区
668    ///
669    /// **返回值:**
670    ///
671    ///  - Ok: 操作成功
672    ///  - Err: 操作失败
673    ///
674    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_mb_read()。`
675    ///
676    pub fn mb_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
677        let res = unsafe {
678            Cli_MBRead(
679                self.handle,
680                start as c_int,
681                size as c_int,
682                buff as *mut [u8] as *mut c_void,
683            )
684        };
685        if res == 0 {
686            return Ok(());
687        }
688        bail!("{}", Self::error_text(res))
689    }
690
691    ///
692    /// 向 PLC 内部标志位(Merkers)写入数据。
693    ///
694    /// ```markdown
695    /// 这是 write_area() 的一个精简函数,它从内部调用了 write_area(), 其内容为:
696    ///     area = S7AreaMK.
697    ///     word_len = S7WLBytes.
698    /// ```
699    ///
700    /// **输入参数:**
701    ///
702    ///  - start: 开始读取的字节索引
703    ///  - size: 要读取的字节长度
704    ///  - buff: 待写入数据缓冲区
705    ///
706    /// **返回值:**
707    ///
708    ///  - Ok: 操作成功
709    ///  - Err: 操作失败
710    ///
711    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_mb_write()。`
712    ///
713    pub fn mb_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
714        let res = unsafe {
715            Cli_MBWrite(
716                self.handle,
717                start as c_int,
718                size as c_int,
719                buff as *mut [u8] as *mut c_void,
720            )
721        };
722        if res == 0 {
723            return Ok(());
724        }
725        bail!("{}", Self::error_text(res))
726    }
727
728    ///
729    /// 读取 PLC 定时器数据。
730    ///
731    /// ```text
732    /// 这是 read_area() 的一个精简函数,它从内部调用了 read_area(), 其内容为:
733    ///     area = S7AreaTM.
734    ///     word_len = S7WLTimer.
735    /// ```
736    ///
737    /// **输入参数:**
738    ///
739    ///  - start: 开始读取的字节索引
740    ///  - size: 要读取的字节长度
741    ///  - buff: 待写入数据缓冲区
742    ///
743    /// **返回值:**
744    ///
745    ///  - Ok: 操作成功
746    ///  - Err: 操作失败
747    ///
748    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_tm_read()。`
749    /// `    缓冲区大小 = size * 2`
750    ///
751    pub fn tm_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
752        let res = unsafe {
753            Cli_TMRead(
754                self.handle,
755                start as c_int,
756                size as c_int,
757                buff as *mut [u8] as *mut c_void,
758            )
759        };
760        if res == 0 {
761            return Ok(());
762        }
763        bail!("{}", Self::error_text(res))
764    }
765
766    ///
767    /// 向 PLC 定时器写入数据。
768    ///
769    /// ```markdown
770    /// 这是 write_area() 的一个精简函数,它从内部调用了 write_area(), 其内容为:
771    ///     area = S7AreaTM.
772    ///     word_len = S7WLTimer.
773    /// ```
774    ///
775    /// **输入参数:**
776    ///
777    ///  - start: 开始读取的字节索引
778    ///  - size: 要读取的字节长度
779    ///  - buff: 待写入数据缓冲区
780    ///
781    /// **返回值:**
782    ///
783    ///  - Ok: 操作成功
784    ///  - Err: 操作失败
785    ///
786    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_tm_write()。`
787    /// `    缓冲区大小 = size * 2`
788    ///
789    pub fn tm_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
790        let res = unsafe {
791            Cli_TMWrite(
792                self.handle,
793                start as c_int,
794                size as c_int,
795                buff as *mut [u8] as *mut c_void,
796            )
797        };
798        if res == 0 {
799            return Ok(());
800        }
801        bail!("{}", Self::error_text(res))
802    }
803
804    ///
805    /// 读取 PLC 计数器数据。
806    ///
807    /// ```text
808    /// 这是 read_area() 的一个精简函数,它从内部调用了 read_area(), 其内容为:
809    ///     area = S7AreaCT.
810    ///     word_len = S7WLCounter.
811    /// ```
812    ///
813    /// **输入参数:**
814    ///
815    ///  - start: 开始读取的字节索引
816    ///  - size: 要读取的字节长度
817    ///  - buff: 待写入数据缓冲区
818    ///
819    /// **返回值:**
820    ///
821    ///  - Ok: 操作成功
822    ///  - Err: 操作失败
823    ///
824    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_ct_read()。`
825    /// `    缓冲区大小 = size * 2`
826    ///
827    pub fn ct_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
828        let res = unsafe {
829            Cli_CTRead(
830                self.handle,
831                start as c_int,
832                size as c_int,
833                buff as *mut [u8] as *mut c_void,
834            )
835        };
836        if res == 0 {
837            return Ok(());
838        }
839        bail!("{}", Self::error_text(res))
840    }
841
842    ///
843    /// 向 PLC 计数器写入数据。
844    ///
845    /// ```markdown
846    /// 这是 write_area() 的一个精简函数,它从内部调用了 write_area(), 其内容为:
847    ///     area = S7AreaCT.
848    ///     word_len = S7WLCounter.
849    /// ```
850    ///
851    /// **输入参数:**
852    ///
853    ///  - start: 开始读取的字节索引
854    ///  - size: 要读取的字节长度
855    ///  - buff: 待写入数据缓冲区
856    ///
857    /// **返回值:**
858    ///
859    ///  - Ok: 操作成功
860    ///  - Err: 操作失败
861    ///
862    /// `注:如果你需要传输一个大的数据,你可以考虑使用异步的 as_ct_write()。`
863    /// `    缓冲区大小 = size * 2`
864    ///
865    pub fn ct_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
866        let res = unsafe {
867            Cli_CTWrite(
868                self.handle,
869                start as c_int,
870                size as c_int,
871                buff as *mut [u8] as *mut c_void,
872            )
873        };
874        if res == 0 {
875            return Ok(());
876        }
877        bail!("{}", Self::error_text(res))
878    }
879
880    ///
881    /// 在一次调用中从 PLC 读取不同区域的数据。
882    ///
883    /// **输入参数:**
884    ///
885    ///  - item: TS7DataItem 数组
886    ///  - items_count: 要读取的区域数量
887    ///
888    /// **返回值:**
889    ///
890    ///  - Ok: 操作成功
891    ///  - Err: 操作失败
892    ///
893    /// `注:由于涉及到不同区域的变量,这个函数没有分割功能,所以最大数据量不能超过PDU的大小。`
894    /// `因此,这个函数没有对应的异步函数。当你有许多非连续的小变量需要读取时,这个函数的优势就会变得很大。`
895    ///
896    /// # Examples
897    /// ```ignore
898    /// use std::os::raw::*;
899    ///
900    /// let mut db1 = [0u8; 2];
901    /// let mut in1 = [0u8; 1];
902    /// let item0 = TS7DataItem {
903    ///     Area: AreaTable::S7AreaDB as c_int,
904    ///     WordLen: WordLenTable::S7WLByte as c_int,
905    ///     Result: 0,
906    ///     DBNumber: 1,
907    ///     Start: 0,
908    ///     Amount: 2,
909    ///     pdata: &mut db1 as *mut [u8] as *mut c_void,
910    /// };
911    /// let item1 = TS7DataItem {
912    ///     Area: AreaTable::S7AreaPA as c_int,
913    ///     WordLen: WordLenTable::S7WLBit as c_int,
914    ///     Result: 0,
915    ///     DBNumber: 0,
916    ///     Start: 0,
917    ///     Amount: 1,
918    ///     pdata: &mut in1 as *mut [u8] as *mut c_void,
919    /// };
920    /// let mut item = [item0, item1];
921    /// client.read_multi_vars(&mut item, 2);
922    /// ```
923    pub fn read_multi_vars(&self, item: &mut [TS7DataItem], items_count: i32) -> Result<()> {
924        let res = unsafe {
925            Cli_ReadMultiVars(
926                self.handle,
927                &mut item[0] as *mut TS7DataItem,
928                items_count as c_int,
929            )
930        };
931        if res == 0 {
932            return Ok(());
933        }
934        bail!("{}", Self::error_text(res))
935    }
936
937    ///
938    /// 在一次调用中向 PLC 的不同区域写入数据。
939    ///
940    /// **输入参数:**
941    ///
942    ///  - item: TS7DataItem 数组
943    ///  - items_count: 要写入的区域数量
944    ///
945    /// **返回值:**
946    ///
947    ///  - Ok: 操作成功
948    ///  - Err: 操作失败
949    ///
950    pub fn write_multi_vars(&self, item: &mut [TS7DataItem], items_count: i32) -> Result<()> {
951        let res = unsafe {
952            Cli_WriteMultiVars(
953                self.handle,
954                &mut item[0] as *mut TS7DataItem,
955                items_count as c_int,
956            )
957        };
958        if res == 0 {
959            return Ok(());
960        }
961        bail!("{}", Self::error_text(res))
962    }
963
964    ///
965    /// 该函数返回按类型划分的 AG 块数量。
966    ///
967    /// **输入参数:**
968    ///
969    ///  - ts7_blocks_list: TS7BlocksList 结构体
970    ///
971    /// **返回值:**
972    ///
973    ///  - Ok: 操作成功
974    ///  - Err: 操作失败
975    ///
976    pub fn list_blocks(&self, ts7_blocks_list: &mut TS7BlocksList) -> Result<()> {
977        let res = unsafe { Cli_ListBlocks(self.handle, ts7_blocks_list as *mut TS7BlocksList) };
978        if res == 0 {
979            return Ok(());
980        }
981        bail!("{}", Self::error_text(res))
982    }
983
984    ///
985    /// 该函数返回指定区块类型的 AG 列表。
986    ///
987    /// **输入参数:**
988    ///
989    ///  - block_type: 要获取的区块类型
990    ///  - buff: 待写入数据缓冲区
991    ///  - items_count: 在输入中表示用户缓冲区的容量,在输出中表示找到了多少个项目
992    ///
993    /// **返回值:**
994    ///
995    ///  - Ok: 操作成功
996    ///  - Err: 操作失败
997    ///
998    /// # Examples
999    /// ```ignore
1000    ///    let mut buff: TS7BlocksOfType = [0; 8192];
1001    ///    let mut items_count = buff.len() as i32;
1002    ///    client.list_blocks_of_type(BlockType::BlockDB, &mut buff, &mut items_count);
1003    /// ```
1004    ///
1005    pub fn list_blocks_of_type(
1006        &self,
1007        block_type: BlockType,
1008        buff: &mut TS7BlocksOfType,
1009        items_count: &mut i32,
1010    ) -> Result<()> {
1011        let res = unsafe {
1012            Cli_ListBlocksOfType(
1013                self.handle,
1014                block_type as c_int,
1015                buff as *mut TS7BlocksOfType,
1016                items_count as *mut c_int,
1017            )
1018        };
1019        if res == 0 {
1020            return Ok(());
1021        }
1022        bail!("{}", Self::error_text(res))
1023    }
1024
1025    ///
1026    /// 返回一个给定 AG 块的详细信息。如果你需要在一个事先不知道大小的 DB 中读或写数据,这个函数就非常有用(见 MC7Size 字段)。
1027    ///
1028    /// **输入参数:**
1029    ///
1030    ///  - block_type: 要获取的区块类型
1031    ///  - block_num: 要获取的区块数量
1032    ///  - ts7_block_info: TS7BlockInfo 结构体
1033    ///
1034    /// **返回值:**
1035    ///
1036    ///  - Ok: 操作成功
1037    ///  - Err: 操作失败
1038    ///
1039    pub fn get_ag_block_info(
1040        &self,
1041        block_type: BlockType,
1042        block_num: i32,
1043        ts7_block_info: &mut TS7BlockInfo,
1044    ) -> Result<()> {
1045        let res = unsafe {
1046            Cli_GetAgBlockInfo(
1047                self.handle,
1048                block_type as c_int,
1049                block_num as c_int,
1050                ts7_block_info as *mut TS7BlockInfo,
1051            )
1052        };
1053        if res == 0 {
1054            return Ok(());
1055        }
1056        bail!("{}", Self::error_text(res))
1057    }
1058
1059    ///
1060    /// 返回一个区块的详细信息到用户缓冲区中。这个函数通常与 full_upload() 一起使用。
1061    ///
1062    /// **输入参数:**
1063    ///
1064    ///  - buff: 用户缓冲区
1065    ///  - ts7_block_info: TS7BlockInfo 结构体
1066    ///  - size: 缓冲区大小(字节)
1067    ///
1068    /// **返回值:**
1069    ///
1070    ///  - Ok: 操作成功
1071    ///  - Err: 操作失败
1072    ///
1073    pub fn get_pg_block_info(
1074        &self,
1075        buff: &mut [u8],
1076        ts7_block_info: &mut TS7BlockInfo,
1077        size: i32,
1078    ) -> Result<()> {
1079        let res = unsafe {
1080            Cli_GetPgBlockInfo(
1081                self.handle,
1082                buff as *mut [u8] as *mut c_void,
1083                ts7_block_info as *mut TS7BlockInfo,
1084                size as c_int,
1085            )
1086        };
1087        if res == 0 {
1088            return Ok(());
1089        }
1090        bail!("{}", Self::error_text(res))
1091    }
1092
1093    ///
1094    /// 从 AG 上传一个区块。将整个区块复制到用户缓冲区。
1095    ///
1096    /// **输入参数:**
1097    ///
1098    ///  - block_type: 要获取的区块类型
1099    ///  - block_num: 要获取的区块号
1100    ///  - buff: 用户缓冲区
1101    ///  - size: 在输入中表示缓冲区大小,在输出中表示上传的字节数
1102    ///
1103    /// **返回值:**
1104    ///
1105    ///  - Ok: 操作成功
1106    ///  - Err: 操作失败
1107    ///
1108    /// # Examples
1109    /// ```ignore
1110    ///    let mut buff = [0; 4096];
1111    ///    let mut size = buff.len() as i32;
1112    ///    client.full_upload(BlockType::BlockSDB, 0, &mut buff, &mut size);
1113    /// ```
1114    ///
1115    pub fn full_upload(
1116        &self,
1117        block_type: BlockType,
1118        block_num: i32,
1119        buff: &mut [u8],
1120        size: &mut i32,
1121    ) -> Result<()> {
1122        let res = unsafe {
1123            Cli_FullUpload(
1124                self.handle,
1125                block_type as c_int,
1126                block_num as c_int,
1127                buff as *mut [u8] as *mut c_void,
1128                size as *mut c_int,
1129            )
1130        };
1131        if res == 0 {
1132            return Ok(());
1133        }
1134        bail!("{}", Self::error_text(res))
1135    }
1136
1137    ///
1138    /// 从 AG 上传一个区块主体。只将区块主体复制到用户缓冲区。
1139    ///
1140    /// **输入参数:**
1141    ///
1142    ///  - block_type: 要获取的区块类型
1143    ///  - block_num: 要获取的区块号
1144    ///  - buff: 用户缓冲区
1145    ///  - size: 在输入中表示缓冲区大小,在输出中表示上传的字节数
1146    ///
1147    /// **返回值:**
1148    ///
1149    ///  - Ok: 操作成功
1150    ///  - Err: 操作失败
1151    ///
1152    /// # Examples
1153    /// ```ignore
1154    ///    let mut buff = [0; 4096];
1155    ///    let mut size = buff.len() as i32;
1156    ///    client.upload(BlockType::BlockSDB, 0, &mut buff, &mut size);
1157    /// ```
1158    ///
1159    pub fn upload(
1160        &self,
1161        block_type: BlockType,
1162        block_num: i32,
1163        buff: &mut [u8],
1164        size: &mut i32,
1165    ) -> Result<()> {
1166        let res = unsafe {
1167            Cli_Upload(
1168                self.handle,
1169                block_type as c_int,
1170                block_num as c_int,
1171                buff as *mut [u8] as *mut c_void,
1172                size as *mut c_int,
1173            )
1174        };
1175        if res == 0 {
1176            return Ok(());
1177        }
1178        bail!("{}", Self::error_text(res))
1179    }
1180
1181    ///
1182    /// 从 AG 下载一个区块。将用户缓冲区复制到整个区块。
1183    ///
1184    /// **输入参数:**
1185    ///
1186    ///  - block_num: 新区块编号,或 -1
1187    ///  - buff: 用户缓冲区
1188    ///  - size: 缓冲区大小
1189    ///
1190    /// **返回值:**
1191    ///
1192    ///  - Ok: 操作成功
1193    ///  - Err: 操作失败
1194    ///
1195    /// `注:一个准备被下载的区块已经包含了关于区块类型和区块编号的信息。 如果参数 block_num 为 -1,则区块编号不会被改变,否则区块将以设置的编号被下载。`
1196    ///
1197    pub fn download(&self, block_num: i32, buff: &mut [u8], size: i32) -> Result<()> {
1198        let res = unsafe {
1199            Cli_Download(
1200                self.handle,
1201                block_num as c_int,
1202                buff as *mut [u8] as *mut c_void,
1203                size as c_int,
1204            )
1205        };
1206        if res == 0 {
1207            return Ok(());
1208        }
1209        bail!("{}", Self::error_text(res))
1210    }
1211
1212    ///
1213    /// 从 AG 删除一个区块。
1214    ///
1215    /// **警告: 一旦执行无法撤销!!!**
1216    ///
1217    /// **输入参数:**
1218    ///
1219    ///  - block_type: 要删除的区块类型
1220    ///  - block_num: 要删除的区块编号
1221    ///
1222    /// **返回值:**
1223    ///
1224    ///  - Ok: 操作成功
1225    ///  - Err: 操作失败
1226    ///
1227    pub fn delete(&self, block_type: BlockType, block_num: i32) -> Result<()> {
1228        let res = unsafe { Cli_Delete(self.handle, block_type as c_int, block_num as c_int) };
1229        if res == 0 {
1230            return Ok(());
1231        }
1232        bail!("{}", Self::error_text(res))
1233    }
1234
1235    ///
1236    /// 从 AG 上传一个 DB,这个函数等同于 upload() 的参数 block_type = Block_DB,
1237    /// 但是它使用了一个不同的方法,所以它不受安全级别设置的限制。这个方法只上传数据。
1238    ///
1239    /// **输入参数:**
1240    ///
1241    ///  - block_num: 要上传的 DB 块编号
1242    ///  - buff: 用户缓冲区
1243    ///  - size: 在输入中表示缓冲区大小,在输出中表示上传的字节数
1244    ///
1245    /// **返回值:**
1246    ///
1247    ///  - Ok: 操作成功
1248    ///  - Err: 操作失败
1249    ///
1250    pub fn db_get(&self, block_num: i32, buff: &mut [u8], size: &mut i32) -> Result<()> {
1251        let res = unsafe {
1252            Cli_DBGet(
1253                self.handle,
1254                block_num as c_int,
1255                buff as *mut [u8] as *mut c_void,
1256                size as *mut c_int,
1257            )
1258        };
1259        if res == 0 {
1260            return Ok(());
1261        }
1262        bail!("{}", Self::error_text(res))
1263    }
1264
1265    ///
1266    /// 用一个给定的字节填充 AG 中的一个 DB,而不需要指定其大小。
1267    ///
1268    /// **输入参数:**
1269    ///
1270    ///  - block_num: 要填充的 DB 块编号
1271    ///  - fill_char: 要填充的字节
1272    ///
1273    /// **返回值:**
1274    ///
1275    ///  - Ok: 操作成功
1276    ///  - Err: 操作失败
1277    ///
1278    ///  `注:出于效率考虑,fill_char 是一个整数,且只有最低的字节被使用`
1279    ///
1280    pub fn db_fill(&self, block_num: i32, fill_char: i32) -> Result<()> {
1281        let res = unsafe { Cli_DBFill(self.handle, block_num as c_int, fill_char as c_int) };
1282        if res == 0 {
1283            return Ok(());
1284        }
1285        bail!("{}", Self::error_text(res))
1286    }
1287
1288    ///
1289    /// 读取 PLC 的日期和时间。
1290    ///
1291    /// **输入参数:**
1292    ///
1293    ///  - date_time: DateTime 结构体
1294    ///
1295    /// **返回值:**
1296    ///
1297    ///  - Ok: 操作成功
1298    ///  - Err: 操作失败
1299    ///
1300    pub fn get_plc_date_time(&self, date_time: &mut DateTime) -> Result<()> {
1301        let res = unsafe { Cli_GetPlcDateTime(self.handle, date_time as *mut DateTime) };
1302        if res == 0 {
1303            return Ok(());
1304        }
1305        bail!("{}", Self::error_text(res))
1306    }
1307
1308    ///
1309    /// 设置 PLC 的日期和时间。
1310    ///
1311    /// **输入参数:**
1312    ///
1313    ///  - date_time: DateTime 结构体
1314    ///
1315    /// **返回值:**
1316    ///
1317    ///  - Ok: 操作成功
1318    ///  - Err: 操作失败
1319    ///
1320    pub fn set_plc_date_time(&self, date_time: &mut DateTime) -> Result<()> {
1321        let res = unsafe { Cli_SetPlcDateTime(self.handle, date_time as *mut DateTime) };
1322        if res == 0 {
1323            return Ok(());
1324        }
1325        bail!("{}", Self::error_text(res))
1326    }
1327
1328    ///
1329    /// 设置 PLC 的日期和时间与 PC 一致。
1330    ///
1331    /// **返回值:**
1332    ///
1333    ///  - Ok: 操作成功
1334    ///  - Err: 操作失败
1335    ///
1336    pub fn set_plc_system_date_time(&self) -> Result<()> {
1337        let res = unsafe { Cli_SetPlcSystemDateTime(self.handle) };
1338        if res == 0 {
1339            return Ok(());
1340        }
1341        bail!("{}", Self::error_text(res))
1342    }
1343
1344    ///
1345    /// 通过一个给定 ID 和 INDEX 读取局部系统状态列表。
1346    ///
1347    /// **输入参数:**
1348    ///
1349    /// - id: 列表 ID
1350    /// - index: 列表 INDEX
1351    /// - ts7szl: TS7SZL 结构体
1352    /// - size: 输入时为缓冲区大小,输出时为读取到的字节数
1353    ///
1354    /// **返回值:**
1355    ///
1356    ///  - Ok: 操作成功
1357    ///  - Err: 操作失败
1358    ///
1359    pub fn read_szl(&self, id: i32, index: i32, ts7szl: &mut TS7SZL, size: &mut i32) -> Result<()> {
1360        let res = unsafe {
1361            Cli_ReadSZL(
1362                self.handle,
1363                id,
1364                index,
1365                ts7szl as *mut TS7SZL,
1366                size as *mut c_int,
1367            )
1368        };
1369        if res == 0 {
1370            return Ok(());
1371        }
1372        bail!("{}", Self::error_text(res))
1373    }
1374
1375    ///
1376    /// 读取局部系统状态列表的目录。
1377    ///
1378    /// **输入参数:**
1379    ///
1380    /// - ts7szl_list: TS7SZLList 结构体
1381    /// - items_count: 输入时为缓冲区大小,输出时为发现的项目数量
1382    ///
1383    /// **返回值:**
1384    ///
1385    ///  - Ok: 操作成功
1386    ///  - Err: 操作失败
1387    ///
1388    pub fn read_szl_list(&self, ts7szl_list: &mut TS7SZLList, items_count: &mut i32) -> Result<()> {
1389        let res = unsafe {
1390            Cli_ReadSZLList(
1391                self.handle,
1392                ts7szl_list as *mut TS7SZLList,
1393                items_count as *mut c_int,
1394            )
1395        };
1396        if res == 0 {
1397            return Ok(());
1398        }
1399        bail!("{}", Self::error_text(res))
1400    }
1401
1402    ///
1403    /// 获取 CPU 商品码和版本信息。
1404    ///
1405    /// **输入参数:**
1406    ///
1407    /// - ts7_order_code: TS7OrderCode 结构体
1408    ///
1409    /// **返回值:**
1410    ///
1411    ///  - Ok: 操作成功
1412    ///  - Err: 操作失败
1413    ///
1414    pub fn get_order_code(&self, ts7_order_code: &mut TS7OrderCode) -> Result<()> {
1415        let res = unsafe { Cli_GetOrderCode(self.handle, ts7_order_code as *mut TS7OrderCode) };
1416        if res == 0 {
1417            return Ok(());
1418        }
1419        bail!("{}", Self::error_text(res))
1420    }
1421
1422    ///
1423    /// 获取 CPU 模块名称、序列号和其他信息。
1424    ///
1425    /// **输入参数:**
1426    ///
1427    /// - ts7_cpu_info: TS7CpuInfo 结构体
1428    ///
1429    /// **返回值:**
1430    ///
1431    ///  - Ok: 操作成功
1432    ///  - Err: 操作失败
1433    ///
1434    pub fn get_cpu_info(&self, ts7_cpu_info: &mut TS7CpuInfo) -> Result<()> {
1435        let res = unsafe { Cli_GetCpuInfo(self.handle, ts7_cpu_info as *mut TS7CpuInfo) };
1436        if res == 0 {
1437            return Ok(());
1438        }
1439        bail!("{}", Self::error_text(res))
1440    }
1441
1442    ///
1443    /// 获取 CP(通信处理器)信息。
1444    ///
1445    /// **输入参数:**
1446    ///
1447    /// - ts7_cp_info: TS7CpInfo 结构体
1448    ///
1449    /// **返回值:**
1450    ///
1451    ///  - Ok: 操作成功
1452    ///  - Err: 操作失败
1453    ///
1454    pub fn get_cp_info(&self, ts7_cp_info: &mut TS7CpInfo) -> Result<()> {
1455        let res = unsafe { Cli_GetCpInfo(self.handle, ts7_cp_info as *mut TS7CpInfo) };
1456        if res == 0 {
1457            return Ok(());
1458        }
1459        bail!("{}", Self::error_text(res))
1460    }
1461
1462    ///
1463    /// 将 CPU 置于 RUN 模式,执行热启动。
1464    ///
1465    /// **返回值:**
1466    ///
1467    ///  - Ok: 操作成功
1468    ///  - Err: 操作失败
1469    ///
1470    ///  `注:该功能受制于设定的安全级别。`
1471    ///
1472    pub fn plc_hot_start(&self) -> Result<()> {
1473        let res = unsafe { Cli_PlcHotStart(self.handle) };
1474        if res == 0 {
1475            return Ok(());
1476        }
1477        bail!("{}", Self::error_text(res))
1478    }
1479
1480    ///
1481    /// 将 CPU 置于 RUN 模式,执行冷启动。
1482    ///
1483    /// **返回值:**
1484    ///
1485    ///  - Ok: 操作成功
1486    ///  - Err: 操作失败
1487    ///
1488    ///  `注:该功能受制于设定的安全级别。`
1489    ///
1490    pub fn plc_cold_start(&self) -> Result<()> {
1491        let res = unsafe { Cli_PlcColdStart(self.handle) };
1492        if res == 0 {
1493            return Ok(());
1494        }
1495        bail!("{}", Self::error_text(res))
1496    }
1497
1498    ///
1499    /// 将 CPU 置于 STOP 模式。
1500    ///
1501    /// **返回值:**
1502    ///
1503    ///  - Ok: 操作成功
1504    ///  - Err: 操作失败
1505    ///
1506    ///  `注:该功能受制于设定的安全级别。`
1507    ///
1508    pub fn plc_stop(&self) -> Result<()> {
1509        let res = unsafe { Cli_PlcStop(self.handle) };
1510        if res == 0 {
1511            return Ok(());
1512        }
1513        bail!("{}", Self::error_text(res))
1514    }
1515
1516    ///
1517    /// 执行复制 RAM 到 ROM。
1518    ///
1519    /// **输入参数:**
1520    ///
1521    /// - timeout: 预期完成操作的最大时间(ms)
1522    ///
1523    /// **返回值:**
1524    ///
1525    ///  - Ok: 操作成功
1526    ///  - Err: 操作失败
1527    ///
1528    ///  `注:不是所有的 CPU 都支持这个操作,CPU 必须处于 STOP 模式。`
1529    ///
1530    pub fn copy_ram_to_rom(&self, timeout: i32) -> Result<()> {
1531        let res = unsafe { Cli_CopyRamToRom(self.handle, timeout) };
1532        if res == 0 {
1533            return Ok(());
1534        }
1535        bail!("{}", Self::error_text(res))
1536    }
1537
1538    ///
1539    /// 执行内存压缩。
1540    ///
1541    /// **输入参数:**
1542    ///
1543    /// - timeout: 预期完成操作的最大时间(ms)
1544    ///
1545    /// **返回值:**
1546    ///
1547    ///  - Ok: 操作成功
1548    ///  - Err: 操作失败
1549    ///
1550    ///  `注:不是所有的 CPU 都支持这个操作,CPU 必须处于 STOP 模式。`
1551    ///
1552    pub fn compress(&self, timeout: i32) -> Result<()> {
1553        let res = unsafe { Cli_Compress(self.handle, timeout) };
1554        if res == 0 {
1555            return Ok(());
1556        }
1557        bail!("{}", Self::error_text(res))
1558    }
1559
1560    ///
1561    /// 获取 PLC 状态。
1562    ///
1563    /// **输入参数**
1564    ///
1565    ///  - status: PLC 状态
1566    ///     - 0x00: 状态未知
1567    ///     - 0x08:运行
1568    ///     - 0x04:停止
1569    ///
1570    /// **返回值:**
1571    ///
1572    ///  - Ok: 操作成功
1573    ///  - Err: 操作失败
1574    ///
1575    pub fn get_plc_status(&self, status: &mut i32) -> Result<()> {
1576        let res = unsafe { Cli_GetPlcStatus(self.handle, status as *mut c_int) };
1577        if res == 0 {
1578            return Ok(());
1579        }
1580        bail!("{}", Self::error_text(res))
1581    }
1582
1583    ///
1584    /// 向 PLC 发送密码,以满足其安全要求。
1585    ///
1586    /// **输入参数**
1587    ///
1588    ///  - password: 密码
1589    ///
1590    /// **返回值:**
1591    ///
1592    ///  - Ok: 操作成功
1593    ///  - Err: 操作失败
1594    ///
1595    pub fn set_session_password(&self, password: &str) -> Result<()> {
1596        let password = CString::new(password).unwrap();
1597        let res = unsafe { Cli_SetSessionPassword(self.handle, password.into_raw()) };
1598        if res == 0 {
1599            return Ok(());
1600        }
1601        bail!("{}", Self::error_text(res))
1602    }
1603
1604    ///
1605    /// 清除为当前会话设置的密码(注销)。
1606    ///
1607    /// **返回值:**
1608    ///
1609    ///  - Ok: 操作成功
1610    ///  - Err: 操作失败
1611    ///
1612    pub fn clear_session_password(&self) -> Result<()> {
1613        let res = unsafe { Cli_ClearSessionPassword(self.handle) };
1614        if res == 0 {
1615            return Ok(());
1616        }
1617        bail!("{}", Self::error_text(res))
1618    }
1619
1620    ///
1621    /// 获取 CPU 安全级别信息。
1622    ///
1623    /// **输入参数**
1624    ///
1625    ///  - ts7_protection: TS7Protection 结构体
1626    ///
1627    /// **返回值:**
1628    ///
1629    ///  - Ok: 操作成功
1630    ///  - Err: 操作失败
1631    ///
1632    pub fn get_protection(&self, ts7_protection: &mut TS7Protection) -> Result<()> {
1633        let res = unsafe { Cli_GetProtection(self.handle, ts7_protection as *mut TS7Protection) };
1634        if res == 0 {
1635            return Ok(());
1636        }
1637        bail!("{}", Self::error_text(res))
1638    }
1639
1640    ///
1641    /// 与 CPU 交换一个给定的 S7 PDU(协议数据单元)。
1642    ///
1643    /// **输入参数**
1644    ///
1645    ///  - buff: 用户缓冲区
1646    ///  - size: 输入时为用户缓冲区大小,输出时为回复报文大小
1647    ///
1648    /// **返回值:**
1649    ///
1650    ///  - Ok: 操作成功
1651    ///  - Err: 操作失败
1652    ///
1653    pub fn iso_exchange_buffer(&self, buff: &mut [u8], size: &mut i32) -> Result<()> {
1654        let res = unsafe {
1655            Cli_IsoExchangeBuffer(
1656                self.handle,
1657                buff as *mut [u8] as *mut c_void,
1658                size as *mut c_int,
1659            )
1660        };
1661        if res == 0 {
1662            return Ok(());
1663        }
1664        bail!("{}", Self::error_text(res))
1665    }
1666
1667    ///
1668    /// 返回最后的作业执行时间,单位是毫秒。
1669    ///
1670    /// **输入参数**
1671    ///
1672    ///  - time: 执行时间(ms)
1673    ///
1674    /// **返回值:**
1675    ///
1676    ///  - Ok: 操作成功
1677    ///  - Err: 操作失败
1678    ///
1679    pub fn get_exec_time(&self, time: &mut i32) -> Result<()> {
1680        let res = unsafe { Cli_GetExecTime(self.handle, time as *mut c_int) };
1681        if res == 0 {
1682            return Ok(());
1683        }
1684        bail!("{}", Self::error_text(res))
1685    }
1686
1687    ///
1688    /// 返回最后的工作结果。
1689    ///
1690    /// **输入参数:**
1691    ///
1692    ///  - last_error: 最后一次工作的返回结果
1693    ///
1694    /// **返回值:**
1695    ///  - Ok: 操作成功
1696    ///  - Err: 操作失败
1697    ///
1698    pub fn get_last_error(&self, last_error: &mut i32) -> Result<()> {
1699        unsafe {
1700            let res = Cli_GetLastError(self.handle, last_error as *mut i32);
1701            if res == 0 {
1702                return Ok(());
1703            }
1704            bail!("{}", Self::error_text(res))
1705        }
1706    }
1707
1708    ///
1709    /// 返回有关 PDU 长度的信息。
1710    ///
1711    /// **输入参数:**
1712    ///
1713    ///  - requested: 要求的 PDU 长度
1714    ///  - negotiated: 协商的 PDU 长度
1715    ///
1716    /// **返回值:**
1717    ///  - Ok: 操作成功
1718    ///  - Err: 操作失败
1719    ///
1720    pub fn get_pdu_length(&self, requested: &mut i32, negotiated: &mut i32) -> Result<()> {
1721        unsafe {
1722            let res = Cli_GetPduLength(
1723                self.handle,
1724                requested as *mut c_int,
1725                negotiated as *mut c_int,
1726            );
1727            if res == 0 {
1728                return Ok(());
1729            }
1730            bail!("{}", Self::error_text(res))
1731        }
1732    }
1733
1734    ///
1735    /// 返回一个给定错误的文本解释。
1736    ///
1737    /// **输入参数:**
1738    ///
1739    ///  - error: 错误代码
1740    ///
1741    pub fn error_text(error: i32) -> String {
1742        let mut chars = [0i8; 1024];
1743        unsafe {
1744            Cli_ErrorText(error, chars.as_mut_ptr() as *mut c_char, 1024);
1745            CStr::from_ptr(chars.as_ptr() as *const c_char)
1746                .to_string_lossy()
1747                .into_owned()
1748        }
1749    }
1750
1751    ///
1752    /// 获取连接状态。
1753    ///
1754    /// **输入参数**
1755    ///
1756    ///  - is_connected: 0 未连接,!=0 已连接
1757    ///
1758    /// **返回值:**
1759    ///
1760    ///  - Ok: 操作成功
1761    ///  - Err: 操作失败
1762    ///
1763    pub fn get_connected(&self, is_connected: &mut i32) -> Result<()> {
1764        let res = unsafe { Cli_GetConnected(self.handle, is_connected as *mut c_int) };
1765        if res == 0 {
1766            return Ok(());
1767        }
1768        bail!("{}", Self::error_text(res))
1769    }
1770
1771    ///
1772    /// 设置客户端在异步数据传输完成时的用户回调。。
1773    ///
1774    /// **输入参数:**
1775    ///
1776    ///  - callback: 回调函数
1777    ///
1778    /// **返回值:**
1779    ///  - Ok: 操作成功
1780    ///  - Err: 操作失败
1781    ///
1782    /// # Examples
1783    /// ```ignore
1784    /// client.set_as_callback(Some(|_, op_code, op_result| {
1785    ///     println!("op_code: {}", op_code);
1786    ///     println!("op_result: {:?}", S7Client::error_text(op_result));
1787    /// })).unwrap();
1788    /// ```
1789    pub fn set_as_callback<F>(&self, callback: Option<F>) -> Result<()>
1790    where
1791        F: FnMut(*mut c_void, c_int, c_int) + 'static,
1792    {
1793        if callback.is_some() {
1794            unsafe {
1795                let data = Box::into_raw(Box::new(callback));
1796                let res =
1797                    Cli_SetAsCallback(self.handle, Some(call_as_closure::<F>), data as *mut c_void);
1798                if res == 0 {
1799                    return Ok(());
1800                }
1801                bail!("{}", Self::error_text(res))
1802            }
1803        } else {
1804            unsafe {
1805                let res = Cli_SetAsCallback(self.handle, None, std::ptr::null_mut() as *mut c_void);
1806                if res == 0 {
1807                    return Ok(());
1808                }
1809                bail!("{}", Self::error_text(res))
1810            }
1811        }
1812    }
1813
1814    ///
1815    /// 检查当前的异步任务是否完成并立即返回。
1816    ///
1817    /// **输入参数:**
1818    ///
1819    ///  - op_result: 操作结果
1820    ///
1821    /// **返回值:**
1822    ///  - 0: 已完成
1823    ///  - 1:任务进行中
1824    ///  - -2: 提供的处理方式无效
1825    ///
1826    ///  `注:如果返回值是 0,则 op_result 包含函数执行结果。`
1827    ///
1828    /// # Examples
1829    /// ```ignore
1830    /// // 如果不想使用循环,可以考虑使用 wait_as_completion() 函数;
1831    /// loop {
1832    ///     let mut op = -1;
1833    ///     if partner.check_as_completion(&mut op) == 0 {
1834    ///         println!("{}", op);
1835    ///         break;
1836    ///     }
1837    ///     std::thread::sleep(std::time::Duration::from_millis(100));
1838    /// }
1839    /// ```
1840    pub fn check_as_completion(&self, op_result: &mut i32) -> i32 {
1841        unsafe { Cli_CheckAsCompletion(self.handle, op_result as *mut c_int) }
1842    }
1843
1844    ///
1845    /// 等待直到当前的异步任务完成或超时结束。
1846    ///
1847    /// **输入参数:**
1848    ///
1849    ///  - timeout: 超时,单位 ms
1850    ///
1851    /// **返回值:**
1852    ///  - 0: 已完成
1853    ///  - 0x02200000:任务超时
1854    ///  - 其它值: 见错误代码
1855    ///
1856    ///  `注:这个函数使用本地操作系统原语(事件、信号...),以避免浪费CPU时间。`
1857    ///
1858    pub fn wait_as_completion(&self, timeout: i32) -> i32 {
1859        unsafe { Cli_WaitAsCompletion(self.handle, timeout) }
1860    }
1861
1862    ///
1863    /// 从 PLC 中异步读取数据, 你可以读取数据块(DB)、输入、输出、内部标志位(Merkers)、定时器和计数器。
1864    ///
1865    /// **输入参数:**
1866    ///
1867    ///  - area: 要读取的区域
1868    ///  - db_number: 要读取的数据块(DB)编号。如果区域不为 S7AreaDB 则被忽略,值为 0。
1869    ///  - start: 开始读取的字节索引
1870    ///  - size: 要读取的字节长度
1871    ///  - buff: 待读取数据缓冲区
1872    ///
1873    /// **返回值:**
1874    ///
1875    ///  - Ok: 操作成功
1876    ///  - Err: 操作失败
1877    ///
1878    /// `注:
1879    /// (1) 如果你需要传输一个小于PDU长度的数据,应可以考虑使用同步的 read_area()。
1880    /// (2) 当 word_len=S7WLBit 时,Offset(start) 必须以比特表示。
1881    /// 示例: DB4.DBX 10.3 的起点是 (10*8)+3=83`
1882    ///
1883    pub fn as_read_area(
1884        &self,
1885        area: AreaTable,
1886        db_number: i32,
1887        start: i32,
1888        size: i32,
1889        word_len: WordLenTable,
1890        buff: &mut [u8],
1891    ) -> Result<()> {
1892        let res = unsafe {
1893            Cli_AsReadArea(
1894                self.handle,
1895                area as c_int,
1896                db_number as c_int,
1897                start as c_int,
1898                size as c_int,
1899                word_len as c_int,
1900                buff as *mut [u8] as *mut c_void,
1901            )
1902        };
1903        if res == 0 {
1904            return Ok(());
1905        }
1906        bail!("{}", Self::error_text(res))
1907    }
1908
1909    ///
1910    /// 将数据异步写入到 PLC, 这是 as_read_area() 的补充函数。
1911    ///
1912    /// **输入参数:**
1913    ///
1914    ///  - area: 要读取的区域
1915    ///  - db_number: 要读取的数据块(DB)编号。如果区域不为 S7AreaDB 则被忽略,值为 0。
1916    ///  - start: 开始读取的字节索引
1917    ///  - size: 要读取的字节长度
1918    ///  - buff: 待写入数据缓冲区
1919    ///
1920    /// **返回值:**
1921    ///
1922    ///  - Ok: 操作成功
1923    ///  - Err: 操作失败
1924    ///
1925    /// `注:
1926    /// (1) 如果你需要传输一个小于PDU长度的数据,应可以考虑使用同步的 write_area()。
1927    /// (2) 当 word_len=S7WLBit 时,Offset(start) 必须以比特表示。
1928    /// 示例: DB4.DBX 10.3 的起点是 (10*8)+3=83`
1929    ///
1930    pub fn as_write_area(
1931        &self,
1932        area: AreaTable,
1933        db_number: i32,
1934        start: i32,
1935        size: i32,
1936        word_len: WordLenTable,
1937        buff: &mut [u8],
1938    ) -> Result<()> {
1939        let res = unsafe {
1940            Cli_AsWriteArea(
1941                self.handle,
1942                area as c_int,
1943                db_number as c_int,
1944                start as c_int,
1945                size as c_int,
1946                word_len as c_int,
1947                buff as *mut [u8] as *mut c_void,
1948            )
1949        };
1950        if res == 0 {
1951            return Ok(());
1952        }
1953        bail!("{}", Self::error_text(res))
1954    }
1955
1956    ///
1957    /// 从 PLC DB 区异步读取数据。
1958    ///
1959    /// ```text
1960    /// 这是 as_read_area() 的一个精简函数,它从内部调用了 as_read_area(), 其内容为:
1961    ///     area = S7AreaDB.
1962    ///     word_len = S7WLBytes.
1963    /// ```
1964    ///
1965    /// **输入参数:**
1966    ///
1967    ///  - db_number: 要读取的数据块(DB)编号
1968    ///  - start: 开始读取的字节索引
1969    ///  - size: 要读取的字节长度
1970    ///  - buff: 待写入数据缓冲区
1971    ///
1972    /// **返回值:**
1973    ///
1974    ///  - Ok: 操作成功
1975    ///  - Err: 操作失败
1976    ///
1977    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 db_read()。`
1978    ///
1979    pub fn as_db_read(&self, db_number: i32, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
1980        let res = unsafe {
1981            Cli_AsDBRead(
1982                self.handle,
1983                db_number as c_int,
1984                start as c_int,
1985                size as c_int,
1986                buff as *mut [u8] as *mut c_void,
1987            )
1988        };
1989        if res == 0 {
1990            return Ok(());
1991        }
1992        bail!("{}", Self::error_text(res))
1993    }
1994
1995    ///
1996    /// 向 PLC DB 区异步写入数据。
1997    ///
1998    /// ```markdown
1999    /// 这是 as_write_area() 的一个精简函数,它从内部调用了 as_write_area(), 其内容为:
2000    ///     area = S7AreaDB.
2001    ///     word_len = S7WLBytes.
2002    /// ```
2003    ///
2004    /// **输入参数:**
2005    ///
2006    ///  - db_number: 要读取的数据块(DB)编号
2007    ///  - start: 开始读取的字节索引
2008    ///  - size: 要读取的字节长度
2009    ///  - buff: 待写入数据缓冲区
2010    ///
2011    /// **返回值:**
2012    ///
2013    ///  - Ok: 操作成功
2014    ///  - Err: 操作失败
2015    ///
2016    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 db_write()。`
2017    ///
2018    pub fn as_db_write(
2019        &self,
2020        db_number: i32,
2021        start: i32,
2022        size: i32,
2023        buff: &mut [u8],
2024    ) -> Result<()> {
2025        let res = unsafe {
2026            Cli_AsDBWrite(
2027                self.handle,
2028                db_number as c_int,
2029                start as c_int,
2030                size as c_int,
2031                buff as *mut [u8] as *mut c_void,
2032            )
2033        };
2034        if res == 0 {
2035            return Ok(());
2036        }
2037        bail!("{}", Self::error_text(res))
2038    }
2039
2040    ///
2041    /// 从 PLC 输出区异步读取数据。
2042    ///
2043    /// ```text
2044    /// 这是 as_read_area() 的一个精简函数,它从内部调用了 as_read_area(), 其内容为:
2045    ///     area = S7AreaPA.
2046    ///     word_len = S7WLBytes.
2047    /// ```
2048    ///
2049    /// **输入参数:**
2050    ///
2051    ///  - start: 开始读取的字节索引
2052    ///  - size: 要读取的字节长度
2053    ///  - buff: 待写入数据缓冲区
2054    ///
2055    /// **返回值:**
2056    ///
2057    ///  - Ok: 操作成功
2058    ///  - Err: 操作失败
2059    ///
2060    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 ab_read()。`
2061    ///
2062    pub fn as_ab_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2063        let res = unsafe {
2064            Cli_AsABRead(
2065                self.handle,
2066                start as c_int,
2067                size as c_int,
2068                buff as *mut [u8] as *mut c_void,
2069            )
2070        };
2071        if res == 0 {
2072            return Ok(());
2073        }
2074        bail!("{}", Self::error_text(res))
2075    }
2076
2077    ///
2078    /// 向 PLC 输出区异步写入数据。
2079    ///
2080    /// ```markdown
2081    /// 这是 as_write_area() 的一个精简函数,它从内部调用了 as_write_area(), 其内容为:
2082    ///     area = S7AreaPA.
2083    ///     word_len = S7WLBytes.
2084    /// ```
2085    ///
2086    /// **输入参数:**
2087    ///
2088    ///  - start: 开始读取的字节索引
2089    ///  - size: 要读取的字节长度
2090    ///  - buff: 待写入数据缓冲区
2091    ///
2092    /// **返回值:**
2093    ///
2094    ///  - Ok: 操作成功
2095    ///  - Err: 操作失败
2096    ///
2097    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 ab_write()。`
2098    ///
2099    pub fn as_ab_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2100        let res = unsafe {
2101            Cli_AsABWrite(
2102                self.handle,
2103                start as c_int,
2104                size as c_int,
2105                buff as *mut [u8] as *mut c_void,
2106            )
2107        };
2108        if res == 0 {
2109            return Ok(());
2110        }
2111        bail!("{}", Self::error_text(res))
2112    }
2113
2114    ///
2115    /// 从 PLC 输入区异步读取数据。
2116    ///
2117    /// ```text
2118    /// 这是 as_read_area() 的一个精简函数,它从内部调用了 as_read_area(), 其内容为:
2119    ///     area = S7AreaPE.
2120    ///     word_len = S7WLBytes.
2121    /// ```
2122    ///
2123    /// **输入参数:**
2124    ///
2125    ///  - start: 开始读取的字节索引
2126    ///  - size: 要读取的字节长度
2127    ///  - buff: 待写入数据缓冲区
2128    ///
2129    /// **返回值:**
2130    ///
2131    ///  - Ok: 操作成功
2132    ///  - Err: 操作失败
2133    ///
2134    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 eb_read()。`
2135    ///
2136    pub fn as_eb_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2137        let res = unsafe {
2138            Cli_AsEBRead(
2139                self.handle,
2140                start as c_int,
2141                size as c_int,
2142                buff as *mut [u8] as *mut c_void,
2143            )
2144        };
2145        if res == 0 {
2146            return Ok(());
2147        }
2148        bail!("{}", Self::error_text(res))
2149    }
2150
2151    ///
2152    /// 向 PLC 输入区异步写入数据。
2153    ///
2154    /// ```markdown
2155    /// 这是 as_write_area() 的一个精简函数,它从内部调用了 as_write_area(), 其内容为:
2156    ///     area = S7AreaPE.
2157    ///     word_len = S7WLBytes.
2158    /// ```
2159    ///
2160    /// **输入参数:**
2161    ///
2162    ///  - start: 开始读取的字节索引
2163    ///  - size: 要读取的字节长度
2164    ///  - buff: 待写入数据缓冲区
2165    ///
2166    /// **返回值:**
2167    ///
2168    ///  - Ok: 操作成功
2169    ///  - Err: 操作失败
2170    ///
2171    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 eb_write()。`
2172    ///
2173    pub fn as_eb_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2174        let res = unsafe {
2175            Cli_AsEBWrite(
2176                self.handle,
2177                start as c_int,
2178                size as c_int,
2179                buff as *mut [u8] as *mut c_void,
2180            )
2181        };
2182        if res == 0 {
2183            return Ok(());
2184        }
2185        bail!("{}", Self::error_text(res))
2186    }
2187
2188    ///
2189    /// 从 PLC 内部标志位(Merkers)异步读取数据。
2190    ///
2191    /// ```text
2192    /// 这是 as_read_area() 的一个精简函数,它从内部调用了 as_read_area(), 其内容为:
2193    ///     area = S7AreaMK.
2194    ///     word_len = S7WLBytes.
2195    /// ```
2196    ///
2197    /// **输入参数:**
2198    ///
2199    ///  - start: 开始读取的字节索引
2200    ///  - size: 要读取的字节长度
2201    ///  - buff: 待写入数据缓冲区
2202    ///
2203    /// **返回值:**
2204    ///
2205    ///  - Ok: 操作成功
2206    ///  - Err: 操作失败
2207    ///
2208    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 mb_read()。`
2209    ///
2210    pub fn as_mb_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2211        let res = unsafe {
2212            Cli_AsMBRead(
2213                self.handle,
2214                start as c_int,
2215                size as c_int,
2216                buff as *mut [u8] as *mut c_void,
2217            )
2218        };
2219        if res == 0 {
2220            return Ok(());
2221        }
2222        bail!("{}", Self::error_text(res))
2223    }
2224
2225    ///
2226    /// 向 PLC 内部标志位(Merkers)异步写入数据。
2227    ///
2228    /// ```markdown
2229    /// 这是 as_write_area() 的一个精简函数,它从内部调用了 as_write_area(), 其内容为:
2230    ///     area = S7AreaMK.
2231    ///     word_len = S7WLBytes.
2232    /// ```
2233    ///
2234    /// **输入参数:**
2235    ///
2236    ///  - start: 开始读取的字节索引
2237    ///  - size: 要读取的字节长度
2238    ///  - buff: 待写入数据缓冲区
2239    ///
2240    /// **返回值:**
2241    ///
2242    ///  - Ok: 操作成功
2243    ///  - Err: 操作失败
2244    ///
2245    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 mb_write()。`
2246    ///
2247    pub fn as_mb_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2248        let res = unsafe {
2249            Cli_AsMBWrite(
2250                self.handle,
2251                start as c_int,
2252                size as c_int,
2253                buff as *mut [u8] as *mut c_void,
2254            )
2255        };
2256        if res == 0 {
2257            return Ok(());
2258        }
2259        bail!("{}", Self::error_text(res))
2260    }
2261
2262    ///
2263    /// 异步读取 PLC 定时器数据。
2264    ///
2265    /// ```text
2266    /// 这是 as_read_area() 的一个精简函数,它从内部调用了 as_read_area(), 其内容为:
2267    ///     area = S7AreaTM.
2268    ///     word_len = S7WLTimer.
2269    /// ```
2270    ///
2271    /// **输入参数:**
2272    ///
2273    ///  - start: 开始读取的字节索引
2274    ///  - size: 要读取的字节长度
2275    ///  - buff: 待写入数据缓冲区
2276    ///
2277    /// **返回值:**
2278    ///
2279    ///  - Ok: 操作成功
2280    ///  - Err: 操作失败
2281    ///
2282    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 tm_read()。`
2283    /// `    缓冲区大小 = size * 2`
2284    ///
2285    pub fn as_tm_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2286        let res = unsafe {
2287            Cli_AsTMRead(
2288                self.handle,
2289                start as c_int,
2290                size as c_int,
2291                buff as *mut [u8] as *mut c_void,
2292            )
2293        };
2294        if res == 0 {
2295            return Ok(());
2296        }
2297        bail!("{}", Self::error_text(res))
2298    }
2299
2300    ///
2301    /// 向 PLC 定时器异步写入数据。
2302    ///
2303    /// ```markdown
2304    /// 这是 as_write_area() 的一个精简函数,它从内部调用了 as_write_area(), 其内容为:
2305    ///     area = S7AreaTM.
2306    ///     word_len = S7WLTimer.
2307    /// ```
2308    ///
2309    /// **输入参数:**
2310    ///
2311    ///  - start: 开始读取的字节索引
2312    ///  - size: 要读取的字节长度
2313    ///  - buff: 待写入数据缓冲区
2314    ///
2315    /// **返回值:**
2316    ///
2317    ///  - Ok: 操作成功
2318    ///  - Err: 操作失败
2319    ///
2320    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 tm_write()。`
2321    /// `    缓冲区大小 = size * 2`
2322    ///
2323    pub fn as_tm_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2324        let res = unsafe {
2325            Cli_AsTMWrite(
2326                self.handle,
2327                start as c_int,
2328                size as c_int,
2329                buff as *mut [u8] as *mut c_void,
2330            )
2331        };
2332        if res == 0 {
2333            return Ok(());
2334        }
2335        bail!("{}", Self::error_text(res))
2336    }
2337
2338    ///
2339    /// 异步读取 PLC 计数器数据。
2340    ///
2341    /// ```text
2342    /// 这是 as_read_area() 的一个精简函数,它从内部调用了 as_read_area(), 其内容为:
2343    ///     area = S7AreaCT.
2344    ///     word_len = S7WLCounter.
2345    /// ```
2346    ///
2347    /// **输入参数:**
2348    ///
2349    ///  - start: 开始读取的字节索引
2350    ///  - size: 要读取的字节长度
2351    ///  - buff: 待写入数据缓冲区
2352    ///
2353    /// **返回值:**
2354    ///
2355    ///  - Ok: 操作成功
2356    ///  - Err: 操作失败
2357    ///
2358    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 ct_read()。`
2359    /// `    缓冲区大小 = size * 2`
2360    ///
2361    pub fn as_ct_read(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2362        let res = unsafe {
2363            Cli_AsCTRead(
2364                self.handle,
2365                start as c_int,
2366                size as c_int,
2367                buff as *mut [u8] as *mut c_void,
2368            )
2369        };
2370        if res == 0 {
2371            return Ok(());
2372        }
2373        bail!("{}", Self::error_text(res))
2374    }
2375
2376    ///
2377    /// 向 PLC 计数器异步写入数据。
2378    ///
2379    /// ```markdown
2380    /// 这是 as_write_area() 的一个精简函数,它从内部调用了 as_write_area(), 其内容为:
2381    ///     area = S7AreaCT.
2382    ///     word_len = S7WLCounter.
2383    /// ```
2384    ///
2385    /// **输入参数:**
2386    ///
2387    ///  - start: 开始读取的字节索引
2388    ///  - size: 要读取的字节长度
2389    ///  - buff: 待写入数据缓冲区
2390    ///
2391    /// **返回值:**
2392    ///
2393    ///  - Ok: 操作成功
2394    ///  - Err: 操作失败
2395    ///
2396    /// `注:如果你需要传输一个小于 PDU 大小的数据,应考虑使用同步的 ct_write()。`
2397    /// `    缓冲区大小 = size * 2`
2398    ///
2399    pub fn as_ct_write(&self, start: i32, size: i32, buff: &mut [u8]) -> Result<()> {
2400        let res = unsafe {
2401            Cli_AsCTWrite(
2402                self.handle,
2403                start as c_int,
2404                size as c_int,
2405                buff as *mut [u8] as *mut c_void,
2406            )
2407        };
2408        if res == 0 {
2409            return Ok(());
2410        }
2411        bail!("{}", Self::error_text(res))
2412    }
2413
2414    ///
2415    /// 该函数异步返回指定区块类型的 AG 列表。
2416    ///
2417    /// **输入参数:**
2418    ///
2419    ///  - block_type: 要获取的区块类型
2420    ///  - buff: 待写入数据缓冲区
2421    ///  - items_count: 在输入中表示用户缓冲区的容量,在输出中表示找到了多少个项目
2422    ///
2423    /// **返回值:**
2424    ///
2425    ///  - Ok: 操作成功
2426    ///  - Err: 操作失败
2427    ///
2428    /// # Examples
2429    /// ```ignore
2430    ///    let mut buff: TS7BlocksOfType = [0; 8192];
2431    ///    let mut items_count = buff.len() as i32;
2432    ///    client.as_list_blocks_of_type(BlockType::BlockDB, &mut buff, &mut items_count);
2433    /// ```
2434    ///
2435    pub fn as_list_blocks_of_type(
2436        &self,
2437        block_type: BlockType,
2438        buff: &mut TS7BlocksOfType,
2439        items_count: &mut i32,
2440    ) -> Result<()> {
2441        let res = unsafe {
2442            Cli_AsListBlocksOfType(
2443                self.handle,
2444                block_type as c_int,
2445                buff as *mut TS7BlocksOfType,
2446                items_count as *mut c_int,
2447            )
2448        };
2449        if res == 0 {
2450            return Ok(());
2451        }
2452        bail!("{}", Self::error_text(res))
2453    }
2454
2455    ///
2456    /// 通过一个给定 ID 和 INDEX 异步读取局部系统状态列表。
2457    ///
2458    /// **输入参数:**
2459    ///
2460    /// - id: 列表 ID
2461    /// - index: 列表 INDEX
2462    /// - ts7szl: TS7SZL 结构体
2463    /// - size: 输入时为缓冲区大小,输出时为读取到的字节数
2464    ///
2465    /// **返回值:**
2466    ///
2467    ///  - Ok: 操作成功
2468    ///  - Err: 操作失败
2469    ///
2470    pub fn as_read_szl(
2471        &self,
2472        id: i32,
2473        index: i32,
2474        ts7szl: &mut TS7SZL,
2475        size: &mut i32,
2476    ) -> Result<()> {
2477        let res = unsafe {
2478            Cli_AsReadSZL(
2479                self.handle,
2480                id,
2481                index,
2482                ts7szl as *mut TS7SZL,
2483                size as *mut c_int,
2484            )
2485        };
2486        if res == 0 {
2487            return Ok(());
2488        }
2489        bail!("{}", Self::error_text(res))
2490    }
2491
2492    ///
2493    /// 异步读取局部系统状态列表的目录。
2494    ///
2495    /// **输入参数:**
2496    ///
2497    /// - ts7szl_list: TS7SZLList 结构体
2498    /// - items_count: 输入时为缓冲区大小,输出时为发现的项目数量
2499    ///
2500    /// **返回值:**
2501    ///
2502    ///  - Ok: 操作成功
2503    ///  - Err: 操作失败
2504    ///
2505    pub fn as_read_szl_list(
2506        &self,
2507        ts7szl_list: &mut TS7SZLList,
2508        items_count: &mut i32,
2509    ) -> Result<()> {
2510        let res = unsafe {
2511            Cli_AsReadSZLList(
2512                self.handle,
2513                ts7szl_list as *mut TS7SZLList,
2514                items_count as *mut c_int,
2515            )
2516        };
2517        if res == 0 {
2518            return Ok(());
2519        }
2520        bail!("{}", Self::error_text(res))
2521    }
2522
2523    ///
2524    /// 从 AG 异步上传一个区块。将整个区块复制到用户缓冲区。
2525    ///
2526    /// **输入参数:**
2527    ///
2528    ///  - block_type: 要获取的区块类型
2529    ///  - block_num: 要获取的区块号
2530    ///  - buff: 用户缓冲区
2531    ///  - size: 在输入中表示缓冲区大小,在输出中表示上传的字节数
2532    ///
2533    /// **返回值:**
2534    ///
2535    ///  - Ok: 操作成功
2536    ///  - Err: 操作失败
2537    ///
2538    /// # Examples
2539    /// ```ignore
2540    ///    let mut buff = [0; 4096];
2541    ///    let mut size = buff.len() as i32;
2542    ///    client.as_full_upload(BlockType::BlockSDB, 0, &mut buff, &mut size);
2543    /// ```
2544    ///
2545    pub fn as_full_upload(
2546        &self,
2547        block_type: BlockType,
2548        block_num: i32,
2549        buff: &mut [u8],
2550        size: &mut i32,
2551    ) -> Result<()> {
2552        let res = unsafe {
2553            Cli_AsFullUpload(
2554                self.handle,
2555                block_type as c_int,
2556                block_num as c_int,
2557                buff as *mut [u8] as *mut c_void,
2558                size as *mut c_int,
2559            )
2560        };
2561        if res == 0 {
2562            return Ok(());
2563        }
2564        bail!("{}", Self::error_text(res))
2565    }
2566
2567    ///
2568    /// 从 AG 异步上传一个区块主体。只将区块主体复制到用户缓冲区。
2569    ///
2570    /// **输入参数:**
2571    ///
2572    ///  - block_type: 要获取的区块类型
2573    ///  - block_num: 要获取的区块号
2574    ///  - buff: 用户缓冲区
2575    ///  - size: 在输入中表示缓冲区大小,在输出中表示上传的字节数
2576    ///
2577    /// **返回值:**
2578    ///
2579    ///  - Ok: 操作成功
2580    ///  - Err: 操作失败
2581    ///
2582    /// # Examples
2583    /// ```ignore
2584    ///    let mut buff = [0; 4096];
2585    ///    let mut size = buff.len() as i32;
2586    ///    client.as_upload(BlockType::BlockSDB, 0, &mut buff, &mut size);
2587    /// ```
2588    ///
2589    pub fn as_upload(
2590        &self,
2591        block_type: BlockType,
2592        block_num: i32,
2593        buff: &mut [u8],
2594        size: &mut i32,
2595    ) -> Result<()> {
2596        let res = unsafe {
2597            Cli_AsUpload(
2598                self.handle,
2599                block_type as c_int,
2600                block_num as c_int,
2601                buff as *mut [u8] as *mut c_void,
2602                size as *mut c_int,
2603            )
2604        };
2605        if res == 0 {
2606            return Ok(());
2607        }
2608        bail!("{}", Self::error_text(res))
2609    }
2610
2611    ///
2612    /// 从 AG 异步下载一个区块。将用户缓冲区复制到整个区块。
2613    ///
2614    /// **输入参数:**
2615    ///
2616    ///  - block_num: 新区块编号,或 -1
2617    ///  - buff: 用户缓冲区
2618    ///  - size: 缓冲区大小
2619    ///
2620    /// **返回值:**
2621    ///
2622    ///  - Ok: 操作成功
2623    ///  - Err: 操作失败
2624    ///
2625    /// `注:一个准备被下载的区块已经包含了关于区块类型和区块编号的信息。 如果参数 block_num 为 -1,则区块编号不会被改变,否则区块将以设置的编号被下载。`
2626    ///
2627    pub fn as_download(&self, block_num: i32, buff: &mut [u8], size: i32) -> Result<()> {
2628        let res = unsafe {
2629            Cli_AsDownload(
2630                self.handle,
2631                block_num as c_int,
2632                buff as *mut [u8] as *mut c_void,
2633                size as c_int,
2634            )
2635        };
2636        if res == 0 {
2637            return Ok(());
2638        }
2639        bail!("{}", Self::error_text(res))
2640    }
2641
2642    ///
2643    /// 从 AG 异步上传一个 DB,这个函数等同于 upload() 的参数 block_type = Block_DB,
2644    /// 但是它使用了一个不同的方法,所以它不受安全级别设置的限制。这个方法只上传数据。
2645    ///
2646    /// **输入参数:**
2647    ///
2648    ///  - block_num: 要上传的 DB 块编号
2649    ///  - buff: 用户缓冲区
2650    ///  - size: 在输入中表示缓冲区大小,在输出中表示上传的字节数
2651    ///
2652    /// **返回值:**
2653    ///
2654    ///  - Ok: 操作成功
2655    ///  - Err: 操作失败
2656    ///
2657    pub fn as_db_get(&self, block_num: i32, buff: &mut [u8], size: &mut i32) -> Result<()> {
2658        let res = unsafe {
2659            Cli_AsDBGet(
2660                self.handle,
2661                block_num as c_int,
2662                buff as *mut [u8] as *mut c_void,
2663                size as *mut c_int,
2664            )
2665        };
2666        if res == 0 {
2667            return Ok(());
2668        }
2669        bail!("{}", Self::error_text(res))
2670    }
2671
2672    ///
2673    /// 用一个给定的字节异步填充 AG 中的一个 DB,而不需要指定其大小。
2674    ///
2675    /// **输入参数:**
2676    ///
2677    ///  - block_num: 要填充的 DB 块编号
2678    ///  - fill_char: 要填充的字节
2679    ///
2680    /// **返回值:**
2681    ///
2682    ///  - Ok: 操作成功
2683    ///  - Err: 操作失败
2684    ///
2685    ///  `注:出于效率考虑,fill_char 是一个整数,且只有最低的字节被使用`
2686    ///
2687    pub fn as_db_fill(&self, block_num: i32, fill_char: i32) -> Result<()> {
2688        let res = unsafe { Cli_AsDBFill(self.handle, block_num as c_int, fill_char as c_int) };
2689        if res == 0 {
2690            return Ok(());
2691        }
2692        bail!("{}", Self::error_text(res))
2693    }
2694
2695    ///
2696    /// 异步执行复制 RAM 到 ROM。
2697    ///
2698    /// **输入参数:**
2699    ///
2700    /// - timeout: 预期完成操作的最大时间(ms)
2701    ///
2702    /// **返回值:**
2703    ///
2704    ///  - Ok: 操作成功
2705    ///  - Err: 操作失败
2706    ///
2707    ///  `注:不是所有的 CPU 都支持这个操作,CPU 必须处于 STOP 模式。`
2708    ///
2709    pub fn as_copy_ram_to_rom(&self, timeout: i32) -> Result<()> {
2710        let res = unsafe { Cli_AsCopyRamToRom(self.handle, timeout) };
2711        if res == 0 {
2712            return Ok(());
2713        }
2714        bail!("{}", Self::error_text(res))
2715    }
2716
2717    ///
2718    /// 异步执行内存压缩。
2719    ///
2720    /// **输入参数:**
2721    ///
2722    /// - timeout: 预期完成操作的最大时间(ms)
2723    ///
2724    /// **返回值:**
2725    ///
2726    ///  - Ok: 操作成功
2727    ///  - Err: 操作失败
2728    ///
2729    ///  `注:不是所有的 CPU 都支持这个操作,CPU 必须处于 STOP 模式。`
2730    ///
2731    pub fn as_compress(&self, timeout: i32) -> Result<()> {
2732        let res = unsafe { Cli_AsCompress(self.handle, timeout) };
2733        if res == 0 {
2734            return Ok(());
2735        }
2736        bail!("{}", Self::error_text(res))
2737    }
2738}
2739
2740unsafe extern "C" fn call_as_closure<F>(usr_ptr: *mut c_void, op_code: c_int, op_result: c_int)
2741where
2742    F: FnMut(*mut c_void, c_int, c_int),
2743{
2744    let callback_ptr = usr_ptr as *mut F;
2745    let callback = &mut *callback_ptr;
2746    callback(usr_ptr, op_code, op_result);
2747}
2748
2749#[cfg(test)]
2750mod tests {
2751    use super::*;
2752
2753    #[test]
2754    fn test_client() {
2755        std::thread::sleep(std::time::Duration::from_secs(1));
2756        let client = S7Client::create();
2757
2758        client
2759            .set_as_callback(Some(|_, op_code, op_result| {
2760                println!("op_code: {}", op_code);
2761                println!("op_result: {:?}", S7Client::error_text(op_result));
2762            }))
2763            .unwrap();
2764
2765        let value = InternalParamValue::U16(7878);
2766        assert!(client.set_param(InternalParam::RemotePort, value).is_ok());
2767        if let Err(e) = client.connect_to("127.0.0.1", 0, 1) {
2768            dbg!(e);
2769            return;
2770        }
2771        client
2772            .set_param(InternalParam::PingTimeout, InternalParamValue::I32(777))
2773            .unwrap();
2774        let mut value = InternalParamValue::U16(0);
2775        assert!(client
2776            .get_param(InternalParam::RemotePort, &mut value)
2777            .is_ok());
2778        println!("RemotePort: {:?}", value);
2779        assert!(client
2780            .get_param(InternalParam::PingTimeout, &mut value)
2781            .is_ok());
2782        println!("PingTimeout: {:?}", value);
2783        assert!(client
2784            .get_param(InternalParam::SendTimeout, &mut value)
2785            .is_ok());
2786        println!("SendTimeout: {:?}", value);
2787        assert!(client
2788            .get_param(InternalParam::RecvTimeout, &mut value)
2789            .is_ok());
2790        println!("RecvTimeout: {:?}", value);
2791        assert!(client.get_param(InternalParam::SrcRef, &mut value).is_ok());
2792        println!("SrcRef: {:?}", value);
2793        assert!(client.get_param(InternalParam::DstRef, &mut value).is_ok());
2794        println!("DstRef: {:?}", value);
2795        assert!(client.get_param(InternalParam::SrcTSap, &mut value).is_ok());
2796        println!("SrcTSap: {:?}", value);
2797        assert!(client
2798            .get_param(InternalParam::PDURequest, &mut value)
2799            .is_ok());
2800        println!("PDURequest: {:?}", value);
2801        let mut buff = [0u8; 2];
2802        assert!(client
2803            .read_area(
2804                AreaTable::S7AreaDB,
2805                1,
2806                0,
2807                2,
2808                WordLenTable::S7WLWord,
2809                &mut buff,
2810            )
2811            .is_ok());
2812        println!("word: {}", u16::from_be_bytes([buff[0], buff[1]]));
2813
2814        let mut buff = [1u8; 1];
2815        assert!(client
2816            .write_area(
2817                AreaTable::S7AreaDB,
2818                1,
2819                81,
2820                1,
2821                WordLenTable::S7WLBit,
2822                &mut buff,
2823            )
2824            .is_ok());
2825
2826        let mut buff = [0u8; 1];
2827        assert!(client
2828            .read_area(
2829                AreaTable::S7AreaDB,
2830                1,
2831                81,
2832                1,
2833                WordLenTable::S7WLBit,
2834                &mut buff,
2835            )
2836            .is_ok());
2837        println!("bit: {:#x?}", &buff);
2838
2839        let mut buff = 13.14f32.to_be_bytes();
2840        assert!(client
2841            .write_area(
2842                AreaTable::S7AreaDB,
2843                1,
2844                24,
2845                1,
2846                WordLenTable::S7WLDWord,
2847                &mut buff,
2848            )
2849            .is_ok());
2850        println!("{:#x?}", &buff);
2851
2852        let mut buff = [0u8; 4];
2853        assert!(client
2854            .read_area(
2855                AreaTable::S7AreaDB,
2856                1,
2857                24,
2858                1,
2859                WordLenTable::S7WLDWord,
2860                &mut buff,
2861            )
2862            .is_ok());
2863        println!("{:#x?}", &buff);
2864        println!(
2865            "dword: {}",
2866            f32::from_be_bytes([buff[0], buff[1], buff[2], buff[3]])
2867        );
2868
2869        let mut buff = 13.14f32.to_be_bytes();
2870        assert!(client.db_write(1, 20, 4, &mut buff,).is_ok());
2871        println!("{:#x?}", &buff);
2872
2873        let mut buff = [0u8; 4];
2874        assert!(client.db_read(1, 20, 4, &mut buff).is_ok());
2875        println!("{:#x?}", &buff);
2876        println!(
2877            "dword: {}",
2878            f32::from_be_bytes([buff[0], buff[1], buff[2], buff[3]])
2879        );
2880
2881        let mut buff = 77u16.to_be_bytes();
2882        assert!(client.db_write(1, 0, 2, &mut buff,).is_ok());
2883        println!("{:#x?}", &buff);
2884
2885        let mut buff = [0u8; 2];
2886        assert!(client.db_read(1, 0, 2, &mut buff).is_ok());
2887        println!("{:#x?}", &buff);
2888        println!("dword: {}", u16::from_be_bytes([buff[0], buff[1]]));
2889
2890        let mut buff = [0b0u8; 1];
2891        assert!(client.ab_write(0, 1, &mut buff).is_ok());
2892
2893        let mut buff = [0u8; 1];
2894        assert!(client.ab_read(0, 1, &mut buff).is_ok());
2895        println!("{:#b}", &buff[0]);
2896
2897        let mut buff = [0b1u8; 1];
2898        assert!(client.eb_write(0, 1, &mut buff).is_ok());
2899
2900        let mut buff = [0u8; 1];
2901        assert!(client.eb_read(0, 1, &mut buff).is_ok());
2902        println!("{:#x?}", &buff);
2903
2904        let mut buff = [0b1u8; 1];
2905        assert!(client.mb_write(0, 1, &mut buff).is_ok());
2906
2907        let mut buff = [0u8; 1];
2908        assert!(client.mb_read(0, 1, &mut buff).is_ok());
2909        println!("{:#x?}", &buff);
2910
2911        let mut db1 = [0u8; 2];
2912        let mut in1 = [0u8; 1];
2913        let item0 = TS7DataItem {
2914            Area: AreaTable::S7AreaDB as c_int,
2915            WordLen: WordLenTable::S7WLByte as c_int,
2916            Result: 0,
2917            DBNumber: 1,
2918            Start: 0,
2919            Amount: 2,
2920            pdata: &mut db1 as *mut [u8] as *mut c_void,
2921        };
2922        let item1 = TS7DataItem {
2923            Area: AreaTable::S7AreaPA as c_int,
2924            WordLen: WordLenTable::S7WLBit as c_int,
2925            Result: 0,
2926            DBNumber: 0,
2927            Start: 0,
2928            Amount: 1,
2929            pdata: &mut in1 as *mut [u8] as *mut c_void,
2930        };
2931        let mut item = [item0, item1];
2932        assert!(client.read_multi_vars(&mut item, 2).is_ok());
2933        println!("{:?}", u16::from_be_bytes([db1[0], db1[1]]));
2934        println!("{:?}", in1);
2935
2936        let mut ts7_blocks_list = TS7BlocksList::default();
2937        assert!(client.list_blocks(&mut ts7_blocks_list).is_ok());
2938        println!("{:?}", &ts7_blocks_list);
2939
2940        let mut buff: TS7BlocksOfType = [0; 8192];
2941        let mut items_count = buff.len() as i32;
2942        assert!(client
2943            .list_blocks_of_type(BlockType::BlockDB, &mut buff, &mut items_count)
2944            .is_ok());
2945        println!("buff: {:?}", &buff[0..items_count as usize]);
2946        println!("items_count: {:?}", items_count);
2947
2948        // let mut buff = [0u8; 4096];
2949        // let mut size = buff.len() as i32;
2950        // assert!(client.full_upload(BlockType::BlockSDB, 0, &mut buff, &mut size));
2951        // println!("buff: {:?}", &buff[0..size as usize]);
2952        // println!("size: {:?}", size);
2953
2954        // let mut buff = [0u8; 4096];
2955        // let mut size = buff.len() as i32;
2956        // assert!(client.upload(BlockType::BlockSDB, 0, &mut buff, &mut size));
2957        // println!("buff: {:?}", &buff[0..size as usize]);
2958        // println!("size: {:?}", size);
2959
2960        assert!(client.set_plc_system_date_time().is_ok());
2961
2962        let mut date_time = DateTime::default();
2963        assert!(client.get_plc_date_time(&mut date_time).is_ok());
2964        println!("{:?}", date_time);
2965
2966        let mut is_connected = 0;
2967        assert!(client.get_connected(&mut is_connected).is_ok());
2968        println!("connected: {:?}", is_connected);
2969
2970        let mut status = 0;
2971        if let Err(e) = client.get_plc_status(&mut status) {
2972            dbg!(e);
2973        }
2974        println!("plc status: {:?}", status);
2975
2976        let mut buff = [0u8; 2];
2977        if let Err(e) = client.as_read_area(
2978            AreaTable::S7AreaDB,
2979            1,
2980            0,
2981            2,
2982            WordLenTable::S7WLWord,
2983            &mut buff,
2984        ) {
2985            dbg!(e);
2986        }
2987        println!(
2988            "as_read_area(word): {}",
2989            u16::from_be_bytes([buff[0], buff[1]])
2990        );
2991        loop {
2992            let mut op = 0;
2993            if client.check_as_completion(&mut op) == 0 {
2994                println!(
2995                    "as_read_area(word): {}",
2996                    u16::from_be_bytes([buff[0], buff[1]])
2997                );
2998                break;
2999            }
3000            std::thread::sleep(std::time::Duration::from_millis(100));
3001        }
3002
3003        let mut buff = [0u8; 4];
3004        if let Err(e) = client.as_read_area(
3005            AreaTable::S7AreaDB,
3006            1,
3007            20,
3008            1,
3009            WordLenTable::S7WLDWord,
3010            &mut buff,
3011        ) {
3012            dbg!(e);
3013        }
3014        println!(
3015            "as_read_area(float): {}",
3016            f32::from_be_bytes([buff[0], buff[1], buff[2], buff[3]])
3017        );
3018        dbg!(client.wait_as_completion(100));
3019        println!(
3020            "as_read_area(float): {}",
3021            f32::from_be_bytes([buff[0], buff[1], buff[2], buff[3]])
3022        );
3023    }
3024}