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}