1use byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, ReadBytesExt, WriteBytesExt};
2use hex;
3use std::error::Error;
4use std::io::Cursor;
5use std::io::{Read, Write};
6use std::net::TcpStream;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10use super::db::DataType;
11use super::db::{commands, consts, subcommands, DeviceConstants};
12use super::device_info::{DeviceInfo, E3, E4};
13use super::err;
14use super::tag::{QueryTag, Tag};
15
16use regex::Regex;
17
18fn get_device_type(device: &str) -> Result<String, String> {
19 let re = Regex::new(r"\D+").map_err(|_| "Failed to compile regex".to_string())?;
20 match re.find(device) {
21 Some(mat) => Ok(mat.as_str().to_string()),
22 None => Err(format!("Invalid device type \"{}\"", device)),
23 }
24}
25
26fn get_device_index(device: &str) -> Result<i32, String> {
27 let re = Regex::new(r"\d.*").map_err(|_| "Failed to compile regex".to_string())?;
28 match re.find(device) {
29 Some(mat) => match mat.as_str().parse::<i32>() {
30 Ok(index) => Ok(index),
31 Err(_) => Err(format!("Failed to parse device index \"{}\"", mat.as_str())),
32 },
33 None => Err(format!("Invalid device index \"{}\"", device)),
34 }
35}
36
37pub struct Client {
38 pub plc_type: &'static str,
39 pub comm_type: &'static str,
40 pub network: u8,
41 pub pc: u8,
42 pub dest_moduleio: u16,
43 pub dest_modulesta: u8,
44 pub timer: u8,
45 pub sock_timeout: u64,
46 device_type: Box<dyn DeviceInfo>,
47 _is_connected: Arc<Mutex<bool>>,
48 _sockbufsize: usize,
49 _wordsize: usize,
50 _debug: bool,
51 endian: &'static char,
52 host: String,
53 port: u16,
54 _sock: Option<TcpStream>,
55 use_e4: bool,
56}
57
58impl Client {
59 pub fn new(host: String, port: u16, plc_type: &'static str, use_e4: bool) -> Self {
60 let device_type: Box<dyn DeviceInfo> = if use_e4 {
61 Box::new(E4 {
62 subheader: 0x5400,
63 subheader_serial: 0x0000,
64 })
65 } else {
66 Box::new(E3 { subheader: 0x5000 })
67 };
68
69 Client {
70 plc_type,
71 comm_type: consts::COMMTYPE_BINARY,
72 device_type,
73 network: 0,
74 pc: 0xFF,
75 dest_moduleio: 0x3FF,
76 dest_modulesta: 0x0,
77 timer: 4,
78 sock_timeout: 2,
79 _is_connected: Arc::new(Mutex::new(false)),
80 _sockbufsize: 4096,
81 _wordsize: 2,
82 _debug: false,
83 endian: &consts::ENDIAN_LITTLE,
84 host,
85 port,
86 _sock: None,
87 use_e4,
88 }
89 }
90
91 pub fn set_debug(&mut self, enable: bool) {
92 self._debug = enable;
93 }
94
95 pub fn connect(&mut self) -> Result<(), Box<dyn Error>> {
96 self.check_plc_type()?;
97 let ip_port = format!("{}:{}", self.host, self.port);
98 let stream = TcpStream::connect(ip_port)?;
99 stream.set_read_timeout(Some(Duration::new(self.sock_timeout, 0)))?;
100 stream.set_write_timeout(Some(Duration::new(self.sock_timeout, 0)))?;
101 self._sock = Some(stream);
102 let mut is_connected = self._is_connected.lock().unwrap();
103 *is_connected = true;
104 Ok(())
105 }
106
107 pub fn set_subheader_serial(&mut self, subheader_serial: u16) -> Result<(), String> {
108 self.device_type.set_subheader_series(subheader_serial);
109 Ok(())
110 }
111
112 pub fn close(&mut self) -> Result<(), Box<dyn Error>> {
113 if let Some(ref mut sock) = self._sock {
114 sock.shutdown(std::net::Shutdown::Both)?;
115 }
116 self._sock = None;
117 let mut is_connected = self._is_connected.lock().unwrap();
118 *is_connected = false;
119 Ok(())
120 }
121
122 pub fn send(&self, send_data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
123 if *self._is_connected.lock().unwrap() {
124 self._sock.as_ref().unwrap().write_all(send_data)?;
125 Ok(())
126 } else {
127 Err("Socket is not connected. Please use the connect method.".into())
128 }
129 }
130
131 pub fn recv(&self) -> Result<Vec<u8>, Box<dyn Error>> {
132 let mut recv_data = vec![0u8; self._sockbufsize];
133 let size = self._sock.as_ref().unwrap().read(&mut recv_data)?;
134 recv_data.truncate(size);
135 Ok(recv_data)
136 }
137
138 fn check_plc_type(&mut self) -> Result<(), String> {
139 match self.plc_type {
140 "Q" | "L" | "QnA" | "iQ-L" | "iQ-R" => Ok(()),
141 _ => Err(format!("Invalid PLC type: {}", self.plc_type)),
142 }
143 }
144
145 pub fn set_comm_type(&mut self, comm_type: &str) {
146 match comm_type {
147 "binary" => {
148 self.comm_type = consts::COMMTYPE_BINARY;
149 self._wordsize = 2;
150 }
151 "ascii" => {
152 self.comm_type = consts::COMMTYPE_ASCII;
153 self._wordsize = 4;
154 }
155 _ => panic!("Failed to set communication type. Please use 'binary' or 'ascii'"),
156 }
157 }
158
159 fn build_send_data(&self, request_data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
160 let mut mc_data = Vec::new();
161
162 if self.comm_type == consts::COMMTYPE_BINARY {
163 let mut buffer = Vec::new();
164 buffer.write_u16::<BigEndian>(self.device_type.get_subheader())?;
165 mc_data.extend_from_slice(&buffer);
166 } else {
167 let subheader_hex = format!("{:04X}", self.device_type.get_subheader());
168 mc_data.extend_from_slice(subheader_hex.as_bytes());
169 }
170 mc_data.extend_from_slice(&self.encode_value(
171 self.device_type.get_subheader_serial() as i64,
172 DataType::SWORD,
173 false,
174 )?);
175 mc_data.extend_from_slice(&self.encode_value(0, DataType::SWORD, false)?);
176 if self.use_e4 {
177 } else {
178 if self.comm_type == consts::COMMTYPE_BINARY {
179 let mut buffer = Vec::new();
180 buffer.write_u16::<BigEndian>(self.device_type.get_subheader())?;
181 mc_data.extend_from_slice(&buffer);
182 } else {
183 let subheader_hex = format!("{:04X}", self.device_type.get_subheader());
184 mc_data.extend_from_slice(subheader_hex.as_bytes());
185 }
186 }
187
188 mc_data.extend_from_slice(&self.encode_value(self.network as i64, DataType::BIT, false)?);
189 mc_data.extend_from_slice(&self.encode_value(self.pc as i64, DataType::BIT, false)?);
190 mc_data.extend_from_slice(&self.encode_value(
191 self.dest_moduleio as i64,
192 DataType::SWORD,
193 false,
194 )?);
195 mc_data.extend_from_slice(&self.encode_value(
196 self.dest_modulesta as i64,
197 DataType::BIT,
198 false,
199 )?);
200 mc_data.extend_from_slice(&self.encode_value(
201 (self._wordsize + request_data.len() as usize) as i64,
202 DataType::SWORD,
203 false,
204 )?);
205 mc_data.extend_from_slice(&self.encode_value(self.timer as i64, DataType::SWORD, false)?);
206 mc_data.extend_from_slice(request_data);
207 Ok(mc_data)
208 }
209
210 fn build_command_data(&self, command: u16, subcommand: u16) -> Result<Vec<u8>, Box<dyn Error>> {
211 let mut command_data = Vec::new();
212 command_data.extend_from_slice(&self.encode_value(
213 command as i64,
214 DataType::SWORD,
215 false,
216 )?);
217 command_data.extend_from_slice(&self.encode_value(
218 subcommand as i64,
219 DataType::SWORD,
220 false,
221 )?);
222 Ok(command_data)
223 }
224
225 pub fn encode_value(
226 &self,
227 value: i64,
228 mode: DataType,
229 is_signal: bool,
230 ) -> Result<Vec<u8>, Box<dyn Error>> {
231 let mut buffer = Vec::new();
232
233 let mode_size = mode.size();
234 match *self.endian {
235 consts::ENDIAN_LITTLE => match mode_size {
236 2 => buffer.write_u8(value as u8)?,
237 4 => match is_signal {
238 true => buffer.write_i16::<LittleEndian>(value as i16)?,
239 false => buffer.write_u16::<LittleEndian>(value as u16)?,
240 },
241 8 => match is_signal {
242 true => buffer.write_i32::<LittleEndian>(value as i32)?,
243 false => buffer.write_u32::<LittleEndian>(value as u32)?,
244 },
245 _ => return Err("Unsupported data type size".into()),
246 },
247 consts::ENDIAN_BIG => match mode_size {
248 2 => buffer.write_u8(value as u8)?,
249 4 => match is_signal {
250 true => buffer.write_i32::<BigEndian>(value as i32)?,
251 false => buffer.write_u32::<BigEndian>(value as u32)?,
252 },
253 8 => match is_signal {
254 true => buffer.write_i32::<BigEndian>(value as i32)?,
255 false => buffer.write_u32::<BigEndian>(value as u32)?,
256 },
257 _ => return Err("Unsupported data type size".into()),
258 },
259 consts::ENDIAN_NATIVE => match mode_size {
260 2 => buffer.write_u8(value as u8)?,
261 4 => match is_signal {
262 true => buffer.write_i64::<NativeEndian>(value as i64)?,
263 false => buffer.write_u64::<NativeEndian>(value as u64)?,
264 },
265 8 => match is_signal {
266 true => buffer.write_i64::<NativeEndian>(value as i64)?,
267 false => buffer.write_u64::<NativeEndian>(value as u64)?,
268 },
269 _ => return Err("Unsupported data type size".into()),
270 },
271 _ => return Err("Unsupported endianness".into()),
272 }
273
274 Ok(buffer)
275 }
276
277 fn decode_value(
278 &self,
279 data: &[u8],
280 mode: &DataType,
281 is_signed: bool,
282 ) -> Result<i64, Box<dyn Error>> {
283 let mut bytes = data.to_vec();
284 if self.comm_type != consts::COMMTYPE_BINARY {
285 bytes = hex::decode(bytes)?;
286 }
287
288 let mode_size = mode.size();
289 let mut cursor = Cursor::new(bytes);
290 let value = match *self.endian {
291 consts::ENDIAN_LITTLE => match mode_size {
292 2 => cursor.read_u8()? as i64,
293 4 => match is_signed {
294 true => cursor.read_i16::<LittleEndian>()? as i64,
295 false => cursor.read_u16::<LittleEndian>()? as i64,
296 },
297 8 => match is_signed {
298 true => cursor.read_i16::<LittleEndian>()? as i64,
299 false => cursor.read_u16::<LittleEndian>()? as i64,
300 },
301 _ => return Err("Unsupported data type size".into()),
302 },
303 consts::ENDIAN_BIG => match mode_size {
304 2 => cursor.read_u8()? as i64,
305 4 => match is_signed {
306 true => cursor.read_i16::<BigEndian>()? as i64,
307 false => cursor.read_u16::<BigEndian>()? as i64,
308 },
309 8 => match is_signed {
310 true => cursor.read_i16::<BigEndian>()? as i64,
311 false => cursor.read_u16::<BigEndian>()? as i64,
312 },
313 _ => return Err("Unsupported data type size".into()),
314 },
315 consts::ENDIAN_NATIVE => match mode_size {
316 2 => cursor.read_u8()? as i64,
317 4 => match is_signed {
318 true => cursor.read_i16::<NativeEndian>()? as i64,
319 false => cursor.read_u16::<NativeEndian>()? as i64,
320 },
321 8 => match is_signed {
322 true => cursor.read_i16::<NativeEndian>()? as i64,
323 false => cursor.read_u16::<NativeEndian>()? as i64,
324 },
325 _ => return Err("Unsupported data type size".into()),
326 },
327 _ => return Err("Unsupported endianness".into()),
328 };
329 Ok(value)
330 }
331
332 fn check_mc_error(status: u16) -> Result<(), err::MCError> {
333 if status == 0 {
334 Ok(())
335 } else {
336 Err(err::MCError::new(status))
337 }
338 }
339
340 pub fn batch_read(
341 &mut self,
342 ref_device: &str,
343 read_size: usize,
344 data_type: DataType,
345 decode: bool,
346 ) -> Result<Vec<Tag>, Box<dyn Error>> {
347 let data_type_size = data_type.size();
348 let device_type = get_device_type(ref_device)?;
349 let device_index: i32 = get_device_index(ref_device)?;
350
351 let command = commands::BATCH_READ;
352 let subcommand = if data_type == DataType::BIT {
353 if self.plc_type == consts::IQR_SERIES {
354 subcommands::THREE
355 } else {
356 subcommands::ONE
357 }
358 } else {
359 if self.plc_type == consts::IQR_SERIES {
360 subcommands::TWO
361 } else {
362 subcommands::ZERO
363 }
364 };
365
366 let mut request_data = Vec::new();
367 request_data.extend(self.build_command_data(command, subcommand)?);
368 request_data.extend(self.build_device_data(ref_device)?);
369 request_data.extend(self.encode_value(
370 (read_size * data_type_size as usize) as i64 / 2,
371 DataType::SWORD,
372 false,
373 )?);
374 let send_data = self.build_send_data(&request_data)?;
375
376 self.send(&send_data)?;
377 let recv_data = self.recv()?;
378 self.check_command_response(&recv_data)?;
379
380 let mut result = Vec::new();
381 let mut data_index = self.device_type.get_response_data_index(self.comm_type);
382
383 if data_type == DataType::BIT {
384 if self.comm_type == consts::COMMTYPE_BINARY {
385 for index in 0..read_size {
386 data_index = index / 2 + data_index;
387 let bit_value = if decode {
388 let value = recv_data[data_index];
389 if index % 2 == 0 {
390 if (value & (1 << 4)) != 0 {
391 1
392 } else {
393 0
394 }
395 } else {
396 if (value & (1 << 0)) != 0 {
397 1
398 } else {
399 0
400 }
401 }
402 } else {
403 recv_data[data_index] as i32
404 };
405 result.push(Tag {
406 device: format!("{}{}", device_type, device_index + index as i32),
407 value: format!("{}", bit_value).into(),
408 data_type: data_type.clone(),
409 });
410 }
411 } else {
412 for index in 0..read_size {
413 let bit_value = if decode {
414 recv_data[data_index] as i32
415 } else {
416 recv_data[data_index] as i32
417 };
418 result.push(Tag {
419 device: format!("{}{}", device_type, device_index + index as i32),
420 value: format!("{}", bit_value).into(),
421 data_type: data_type.clone(),
422 });
423 data_index += 1;
424 }
425 }
426 } else {
427 for index in 0..read_size {
428 let value = if decode {
429 let decode_value = self.decode_value(
430 &recv_data[data_index..data_index + data_type_size as usize].to_vec(),
431 &data_type,
432 false,
433 )?;
434 format!("{}", decode_value).to_string()
435 } else {
436 let raw_value = &recv_data[data_index..data_index + data_type_size as usize];
437 String::from_utf8(raw_value.to_vec())?
438 };
439 result.push(Tag {
440 device: format!("{}{}", device_type, device_index + index as i32),
441 value: Some(value),
442 data_type: data_type.clone(),
443 });
444 data_index += data_type_size as usize;
445 }
446 }
447
448 Ok(result)
449 }
450
451 pub fn batch_write(
452 &self,
453 ref_device: &str,
454 values: Vec<i64>,
455 data_type: &DataType,
456 ) -> Result<(), Box<dyn Error>> {
457 let data_type_size = data_type.size();
458 let write_elements = values.len();
459
460 let command = commands::BATCH_WRITE;
461 let subcommand = if *data_type == DataType::BIT {
462 if self.plc_type == consts::IQR_SERIES {
463 subcommands::THREE
464 } else {
465 subcommands::ONE
466 }
467 } else {
468 if self.plc_type == consts::IQR_SERIES {
469 subcommands::TWO
470 } else {
471 subcommands::ZERO
472 }
473 };
474
475 let mut request_data = Vec::new();
476 request_data.extend(self.build_command_data(command, subcommand)?);
477 request_data.extend(self.build_device_data(ref_device)?);
478 request_data.extend(self.encode_value(
479 (write_elements * data_type_size as usize) as i64 / 2,
480 DataType::SWORD,
481 false,
482 )?);
483
484 if *data_type == DataType::BIT {
485 if self.comm_type == consts::COMMTYPE_BINARY {
486 let mut bit_data = vec![0; (values.len() + 1) / 2];
487 for (index, value) in values.iter().enumerate() {
488 let value = (*value != 0) as u8;
489 let value_index = index / 2;
490 let bit_index = if index % 2 == 0 { 4 } else { 0 };
491 let bit_value = value << bit_index;
492 bit_data[value_index] |= bit_value;
493 }
494 request_data.extend(bit_data);
495 } else {
496 for value in values {
497 request_data.extend(value.to_string().into_bytes());
498 }
499 }
500 } else {
501 for value in values {
502 request_data.extend(self.encode_value(value, data_type.clone(), false)?);
503 }
504 }
505
506 let send_data = self.build_send_data(&request_data)?;
507
508 self.send(&send_data)?;
509 let recv_data = self.recv()?;
510 self.check_command_response(&recv_data)?;
511 Ok(())
512 }
513
514 fn build_device_data(&self, device: &str) -> Result<Vec<u8>, Box<dyn Error>> {
515 let mut device_data = Vec::new();
516
517 let device_type = get_device_type(device)?;
518
519 if self.comm_type == consts::COMMTYPE_BINARY {
520 let (device_code, device_base) =
521 DeviceConstants::get_binary_device_code(self.plc_type, &device_type)?;
522 let device_number =
523 i32::from_str_radix(&get_device_index(device)?.to_string(), device_base)?;
524
525 if self.plc_type == consts::IQR_SERIES {
526 let mut buf = [0u8; 6];
527 if *self.endian == consts::ENDIAN_LITTLE {
528 LittleEndian::write_u32(&mut buf, device_number as u32);
529 } else {
530 BigEndian::write_u32(&mut buf, device_number as u32);
531 }
532 device_data.extend_from_slice(&buf[0..4]);
533 device_data.extend_from_slice(&buf[4..6]);
534 } else {
535 let mut buf = [0u8; 4];
536 if *self.endian == consts::ENDIAN_LITTLE {
537 LittleEndian::write_u32(&mut buf, device_number as u32);
538 } else {
539 BigEndian::write_u32(&mut buf, device_number as u32);
540 }
541 device_data.extend_from_slice(&buf[0..3]);
542 device_data.push(device_code as u8);
543 }
544 } else {
545 let (device_code, device_base) =
546 DeviceConstants::get_ascii_device_code(self.plc_type, &device_type)?;
547 let device_number = format!(
548 "{:06x}",
549 i32::from_str_radix(&get_device_index(device)?.to_string(), device_base)?
550 );
551
552 device_data.extend_from_slice(device_code.as_bytes());
553 device_data.extend_from_slice(device_number.as_bytes());
554 }
555
556 Ok(device_data)
557 }
558
559 fn check_command_response(&self, recv_data: &[u8]) -> Result<(), err::MCError> {
560 let response_status_index = self.device_type.get_response_status_index(self.comm_type);
561 let response_status = self
562 .decode_value(
563 &recv_data[response_status_index..response_status_index + self._wordsize],
564 &DataType::SWORD,
565 false,
566 )
567 .unwrap() as u16;
568
569 Client::check_mc_error(response_status)
570 }
571
572 pub fn read(&self, devices: Vec<QueryTag>) -> Result<Vec<Tag>, Box<dyn Error>> {
573 let command = commands::RANDOM_READ;
574 let subcommand = if self.plc_type == consts::IQR_SERIES {
575 subcommands::TWO
576 } else {
577 subcommands::ZERO
578 };
579
580 let mut words_count = 0;
581
582 for element in &devices {
583 let _size = element.data_type.size();
584 words_count += _size / 2;
585 }
586
587 let mut request_data = Vec::new();
588 request_data.extend(self.build_command_data(command, subcommand)?);
589 request_data.extend(self.encode_value(words_count as i64, DataType::BIT, false)?);
590 request_data.extend(self.encode_value(0, DataType::BIT, false)?);
591
592 for element in &devices {
593 let element_size = element.data_type.size() / 2;
594 if element_size > 1 {
595 let tag_name = &element.device;
596 let device_type = get_device_type(tag_name)?;
597 let mut device_index = get_device_index(tag_name)?;
598 for _ in 0..element_size {
599 let temp_tag_name = format!("{}{}", device_type, device_index);
600 request_data.extend(self.build_device_data(&temp_tag_name)?);
601 device_index += 1;
602 }
603 } else {
604 request_data.extend(self.build_device_data(&element.device)?);
605 }
606 }
607
608 if words_count < 1 {
609 return Ok(Vec::new());
610 }
611
612 let send_data = self.build_send_data(&request_data)?;
613 self.send(&send_data)?;
614 let recv_data = self.recv()?;
615
616 let mut output = Vec::new();
617 self.check_command_response(&recv_data)?;
618
619 let mut data_index = self.device_type.get_response_data_index(self.comm_type);
620
621 for element in devices {
622 let size = element.data_type.size();
623 let value = self.decode_value(
624 &recv_data[data_index..data_index + size as usize],
625 &DataType::BIT,
626 false,
627 )?;
628
629 output.push(Tag {
630 device: element.device,
631 value: format!("{}", value).into(),
632 data_type: element.data_type,
633 });
634
635 data_index += size as usize;
636 }
637
638 Ok(output)
639 }
640
641 pub fn write(&self, devices: Vec<Tag>) -> Result<(), Box<dyn Error>> {
642 let command = commands::RANDOM_WRITE;
643 let subcommand = if self.plc_type == consts::IQR_SERIES {
644 subcommands::TWO
645 } else {
646 subcommands::ZERO
647 };
648
649 let mut words_count = 0;
651 for element in &devices {
652 words_count += element.data_type.size() / 2;
653 }
654
655 let mut request_data = Vec::new();
656 request_data.extend(self.build_command_data(command, subcommand)?);
657 request_data.extend(self.encode_value(words_count as i64, DataType::BIT, false)?);
658 request_data.extend(self.encode_value(0, DataType::BIT, false)?);
659
660 for mut element in devices {
661 if element.data_type == DataType::BIT {
662 match element.value {
663 Some(s) => {
664 let s_vec: Vec<i64> = s
665 .split_whitespace()
666 .filter_map(|part| part.parse::<i64>().ok())
667 .collect();
668 self.batch_write(&element.device, s_vec, &element.data_type)?;
669 }
670 None => continue,
671 }
672 continue;
673 }
674 let element_size = element.data_type.size() / 2;
675 if (element.data_type == DataType::UWORD || element.data_type == DataType::UDWORD)
676 && element.value.clone().unwrap().parse::<i64>().unwrap() < 0
677 {
678 element.value = format!("-{}", element.value.unwrap()).into();
679 }
680 if element_size > 1 {
681 let tag_name = &element.device;
682 let device_type = get_device_type(tag_name)?;
683 let mut device_index = get_device_index(tag_name)?;
684 let _value = element.value.unwrap().parse::<i64>().unwrap();
685 let temp_tag_value = self.encode_value(_value, element.data_type, false)?;
686 let mut data_index = 0;
687 for _ in 0..element_size {
688 let temp_tag_name = format!("{}{}", device_type, device_index);
689 request_data.extend(self.build_device_data(&temp_tag_name)?);
690 request_data.extend(&temp_tag_value[data_index..data_index + self._wordsize]);
691 data_index += self._wordsize;
692 device_index += 1;
693 }
694 } else {
695 request_data.extend(self.build_device_data(&element.device)?);
696 let _value = element.value.unwrap().parse::<i64>().unwrap();
697 request_data.extend(&self.encode_value(_value, element.data_type, false)?);
698 }
699 }
700
701 let send_data = self.build_send_data(&request_data)?;
702 self.send(&send_data)?;
703 let recv_data = self.recv()?;
704 self.check_command_response(&recv_data)?;
705
706 Ok(())
707 }
708}
709
710impl Drop for Client {
711 fn drop(&mut self) {
712 if let Err(e) = self.close() {
713 eprintln!("Error closing connection: {:?}", e);
714 }
715 }
716}
717
718impl std::fmt::Debug for Client {
719 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
720 f.debug_struct("Type3E")
721 .field("plc_type", &self.plc_type)
722 .field("comm_type", &self.comm_type)
723 .field("network", &self.network)
724 .field("pc", &self.pc)
725 .field("dest_moduleio", &self.dest_moduleio)
726 .field("dest_modulesta", &self.dest_modulesta)
727 .field("timer", &self.timer)
728 .field("sock_timeout", &self.sock_timeout)
729 .field("_is_connected", &self._is_connected)
730 .field("_sockbufsize", &self._sockbufsize)
731 .field("_wordsize", &self._wordsize)
732 .field("_debug", &self._debug)
733 .field("endian", &self.endian)
734 .field("host", &self.host)
735 .field("port", &self.port)
736 .finish()
737 }
738}
739
740#[cfg(test)]
741mod tests_client {
742 use super::*;
743 use std::io::{Read, Write};
744 use std::net::TcpListener;
745 use std::thread;
746
747 pub fn start_mock_server(port: u16) -> std::net::SocketAddr {
748 let addr = format!("127.0.0.1:{}", port).parse().unwrap();
749 let listener = TcpListener::bind(addr).expect("Failed to bind to address");
750
751 thread::spawn(move || {
752 for stream in listener.incoming() {
753 let mut stream = stream.expect("Failed to accept connection");
754 thread::spawn(move || {
755 let mut buffer = [0; 1024];
756 loop {
757 match stream.read(&mut buffer) {
758 Ok(0) => break, Ok(size) => {
760 let received = &buffer[..size];
761 stream
762 .write_all(received)
763 .expect("Failed to write to stream");
764 }
765 Err(_) => break,
766 }
767 }
768 });
769 }
770 });
771
772 addr
773 }
774
775 struct MockDeviceInfo {
777 subheader: u16,
778 subheader_serial: u16,
779 }
780
781 impl DeviceInfo for MockDeviceInfo {
782 fn set_subheader_series(&mut self, subheader_serial: u16) {
783 self.subheader_serial = subheader_serial;
784 }
785
786 fn get_response_data_index(&self, _: &str) -> usize {
787 10
788 }
789 fn get_response_status_index(&self, _: &str) -> usize {
790 11
791 }
792
793 fn get_subheader(&self) -> u16 {
794 self.subheader
795 }
796 fn get_subheader_serial(&self) -> u16 {
797 self.subheader_serial
798 }
799 }
800
801 #[test]
802 fn test_client_new() {
803 let client = Client::new("localhost".to_string(), 8080, "Q", true);
804 assert_eq!(client.host, "localhost");
805 assert_eq!(client.port, 8080);
806 assert_eq!(client.plc_type, "Q");
807 assert!(client.use_e4);
808 }
809
810 #[test]
811 fn test_set_debug() {
812 let mut client = Client::new("localhost".to_string(), 8080, "Q", true);
813 client.set_debug(true);
814 assert!(client._debug);
815 }
816
817 #[test]
818 fn test_set_subheader_serial() {
819 let mut client = Client::new("localhost".to_string(), 8080, "Q", true);
820 client.device_type = Box::new(MockDeviceInfo {
821 subheader_serial: 0,
822 subheader: 12,
823 });
824 let result = client.set_subheader_serial(1234);
825 assert!(result.is_ok());
826 assert_eq!(client.device_type.get_subheader_serial(), 1234);
827 }
828
829 #[test]
830 fn test_connect() {
831 let server_addr = start_mock_server(9999);
833 let port = server_addr.port();
834 let mut client = Client::new("localhost".to_string(), port, "Q", true);
835 let result = client.connect();
836 assert!(result.is_ok());
837 let data_to_send = b"Hello, server!";
838 let send_result = client.send(data_to_send);
839 assert!(send_result.is_ok());
840 let received_data = client.recv().expect("Failed to receive data");
841 assert_eq!(received_data, data_to_send);
842 let close_result = client.close();
843 assert!(close_result.is_ok());
844 }
845
846 #[test]
847 fn test_check_plc_type() {
848 let mut client = Client::new("localhost".to_string(), 8080, "Q", true);
849 let result = client.check_plc_type();
850 assert!(result.is_ok());
851
852 client.plc_type = "InvalidType";
853 let result = client.check_plc_type();
854 assert!(result.is_err());
855 }
856
857 #[test]
858 fn test_set_comm_type() {
859 let mut client = Client::new("localhost".to_string(), 8080, "Q", true);
860 client.set_comm_type("binary");
861 assert_eq!(client.comm_type, consts::COMMTYPE_BINARY);
862 assert_eq!(client._wordsize, 2);
863
864 client.set_comm_type("ascii");
865 assert_eq!(client.comm_type, consts::COMMTYPE_ASCII);
866 assert_eq!(client._wordsize, 4);
867 }
868 #[test]
869 fn test_build_send_data_binary() -> Result<(), Box<dyn Error>> {
870 let client = Client::new("localhost".to_string(), 8080, "Q", true);
871 let request_data = b"test";
872 let expected_length = 14;
873 let result = client.build_send_data(request_data)?;
874 assert_eq!(result.len(), expected_length);
875 Ok(())
876 }
877
878 #[test]
879 fn test_encode_value_little_endian() -> Result<(), Box<dyn Error>> {
880 let client = Client::new("localhost".to_string(), 8080, "Q", true);
881 let value = 1234;
882 let encoded = client.encode_value(value as i64, DataType::SWORD, false)?;
883 let mut expected = Vec::new();
884 expected.write_u8(value as u8)?;
885 assert_eq!(encoded, expected);
886 Ok(())
887 }
888
889 #[test]
890 fn test_encode_value_big_endian() -> Result<(), Box<dyn Error>> {
891 let client = Client::new("localhost".to_string(), 8080, "Q", true);
892 let value = 1234;
893 let encoded = client.encode_value(value as i64, DataType::SWORD, false)?;
894 let mut expected = Vec::new();
895 expected.write_u8(value as u8)?;
896
897 assert_eq!(encoded, expected);
898 Ok(())
899 }
900}