rs_melsec/
client.rs

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        // Get the words equivalent in size
650        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, // Connection closed
759                            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    // Mock DeviceInfo implementations for testing
776    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        // This test requires a server running that sends data
832        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}