s7_client/
error.rs

1use std::io;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
5pub enum Error {
6    #[error(transparent)]
7    IoErr(#[from] io::Error),
8
9    #[error(transparent)]
10    TpktErr(#[from] tpkt::Error),
11
12    #[error("Error: {0}")]
13    Err(String),
14
15    #[error("WriteTimeout")]
16    WriteTimeout,
17
18    #[error("ReadTimeout")]
19    ReadTimeout,
20
21    #[error("Error: {0}")]
22    ConnectErr(String),
23
24    #[error("InvalidBitAddr: {0}")]
25    InvalidBitAddr(u16),
26}
27
28pub type Result<T> =
29    std::result::Result<T, Error>;
30
31// impl<T: TryFromPrimitive>
32// From<TryFromPrimitiveError<T>> for Error {
33//     fn from(value: TryFromPrimitiveError<T>) ->
34// Self {         Self::Error(format!("{}",
35// value))     }
36// }
37//
38// impl ToCoptError for Error {
39//     fn to_err(self) -> copt::error::Error {
40//         copt::error::Error::Error(self.
41// to_string())     }
42// }
43
44// Copyright 2019 Petar Dambovaliev. All rights
45// reserved. This software may be modified and
46// distributed under the terms of the BSD license.
47// See the LICENSE file for details.
48
49use std::{
50    error, fmt,
51    io::{Error as IOError, ErrorKind},
52};
53
54const TCP_SOCKET_CREATION: i32 = 1;
55const TCP_CONNECTION_TIMEOUT: i32 = 2;
56const TCP_CONNECTION_FAILED: i32 = 3;
57const TCP_RECEIVE_TIMEOUT: i32 = 4;
58const TCP_DATA_RECEIVE: i32 = -5;
59const TCP_SEND_TIMEOUT: i32 = 0x00000006;
60const TCP_DATA_SEND: i32 = 0x00000007;
61const TCP_CONNECTION_RESET: i32 = 0x00000008;
62const TCP_NOT_CONNECTED: i32 = 0x00000009;
63const TCP_UNREACHALE_HOST: i32 = 0x00002751;
64
65const ISO_CONNECT: i32 = 0x00010000;
66pub(crate) const ISO_INVALID_PDU: i32 =
67    0x00030000; // Bad format
68pub(crate) const ISO_INVALID_DATA_SIZE: i32 =
69    0x00040000;
70
71pub(crate) const CLI_NEGOTIATING_PDU: i32 =
72    0x00100000;
73const CLI_INVALID_PARAMS: i32 = 0x00200000;
74const CLI_JOB_PENDING: i32 = 0x00300000;
75const CLI_TOO_MANY_ITEMS: i32 = 0x00400000;
76const CLI_INVALID_DWORD_LEN: i32 = 0x00500000;
77const CLI_PARTIAL_DATA_WRITTEN: i32 = 0x00600000;
78const CLI_SIZE_OVER_PDU: i32 = 0x00700000;
79pub(crate) const CLI_INVALID_PLC_ANSWER: i32 =
80    0x00800000;
81const CLI_ADDRESS_OUT_OF_RANGE: i32 = 0x00900000;
82const CLI_INVALID_TRANSPORT_SIZE: i32 =
83    0x00A00000;
84const CLI_WRITE_DATA_SIZE_MISMATCH: i32 =
85    0x00B00000;
86const CLI_ITEM_NOT_AVAILABLE: i32 = 0x00C00000;
87const CLI_INVALID_VALUE: i32 = 0x00D00000;
88pub(crate) const CLI_CANNOT_START_PLC: i32 =
89    0x00E00000;
90pub(crate) const CLI_ALREADY_RUN: i32 =
91    0x00F00000;
92pub(crate) const CLI_CANNOT_STOP_PLC: i32 =
93    0x01000000;
94const CLI_CANNOT_COPY_RAM_TO_ROM: i32 =
95    0x01100000;
96const CLI_CANNOT_COMPRESS: i32 = 0x01200000;
97pub(crate) const CLI_ALREADY_STOP: i32 =
98    0x01300000;
99const CLI_FUN_NOT_AVAILABLE: i32 = 0x01400000;
100const CLI_UPLOAD_SEQUENCE_FAILED: i32 =
101    0x01500000;
102const CLI_INVALID_DATA_SIZE_RECVD: i32 =
103    0x01600000;
104const CLI_INVALID_BLOCK_TYPE: i32 = 0x01700000;
105const CLI_INVALID_BLOCK_NUMBER: i32 = 0x01800000;
106const CLI_INVALID_BLOCK_SIZE: i32 = 0x01900000;
107const CLI_NEED_PASSWORD: i32 = 0x01D00000;
108const CLI_INVALID_PASSWORD: i32 = 0x01E00000;
109const CLI_NO_PASSWORD_TO_SET_OR_CLEAR: i32 =
110    0x01F00000;
111const CLI_JOB_TIMEOUT: i32 = 0x02000000;
112const CLI_PARTIAL_DATA_READ: i32 = 0x02100000;
113const CLI_BUFFER_TOO_SMALL: i32 = 0x02200000;
114const CLI_FUNCTION_REFUSED: i32 = 0x02300000;
115const CLI_DESTROYING: i32 = 0x02400000;
116const CLI_INVALID_PARAM_NUMBER: i32 = 0x02500000;
117const CLI_CANNOT_CHANGE_PARAM: i32 = 0x02600000;
118const CLI_FUNCTION_NOT_IMPLEMENTED: i32 =
119    0x02700000;
120
121const CODE_7_ADDRESS_OUT_OF_RANGE: i32 = 5;
122const CODE_7_INVALID_TRANSPORT_SIZE: i32 = 6;
123const CODE_7_WRITE_DATA_SIZE_MISMATCH: i32 = 7;
124const CODE_7_RES_ITEM_NOT_AVAILABLE: i32 = 10;
125const CODE_7_RES_ITEM_NOT_AVAILABLE1: i32 = 53769;
126const CODE_7_INVALID_VALUE: i32 = 56321;
127const CODE_7_NEED_PASSWORD: i32 = 53825;
128const CODE_7_INVALID_PASSWORD: i32 = 54786;
129const CODE_7_NO_PASSWORD_TO_CLEAR: i32 = 54788;
130const CODE_7_NO_PASSWORD_TO_SET: i32 = 54789;
131const CODE_7_FUN_NOT_AVAILABLE: i32 = 33028;
132const CODE_7_DATA_OVER_PDU: i32 = 34048;
133
134#[derive(Debug)]
135pub enum S7ConnectError {
136    ConnectFail(String),
137    Lock,
138    IOError(ErrorKind),
139    Response {
140        code: i32,
141    },
142    CPU {
143        code: i32,
144    },
145    InvalidInput {
146        input: String,
147    },
148    Send,
149    Iso,
150    PduLength(u16),
151    TryFrom(Vec<u8>, String),
152    InvalidCpuStatus(u8),
153    InvalidResponse {
154        reason: String,
155        bytes: Vec<u8>,
156    },
157}
158
159impl fmt::Display for S7ConnectError {
160    fn fmt(
161        &self,
162        f: &mut fmt::Formatter,
163    ) -> fmt::Result {
164        match self {
165            S7ConnectError::ConnectFail(s) => {
166                write!(
167                    f,
168                    "connection error: {}",
169                    s
170                )
171            },
172            S7ConnectError::Lock => {
173                write!(f, "Lock error: panicked")
174            },
175            S7ConnectError::IOError(kind) => {
176                write!(f, "IO error: {:?}", kind)
177            },
178            S7ConnectError::Response { code } => {
179                write!(
180                    f,
181                    "Error response: {}",
182                    error_text(*code)
183                )
184            },
185            S7ConnectError::CPU { code } => {
186                write!(
187                    f,
188                    "Error response CPU: {}",
189                    error_text(cpu_error(*code))
190                )
191            },
192            S7ConnectError::InvalidInput {
193                input,
194            } => write!(
195                f,
196                "Invalid input: {}",
197                input
198            ),
199            S7ConnectError::Send => {
200                write!(f, "Send connection error")
201            },
202            S7ConnectError::Iso => {
203                write!(f, "ISO connection error")
204            },
205            S7ConnectError::PduLength(pdu) => {
206                write!(
207                    f,
208                    "PDU length connection \
209                     error {}",
210                    pdu
211                )
212            },
213            S7ConnectError::TryFrom(
214                bytes,
215                reason,
216            ) => {
217                write!(
218                    f,
219                    "Could not read bytes {:?} \
220                     reason {}",
221                    bytes, reason
222                )
223            },
224            S7ConnectError::InvalidCpuStatus(
225                status,
226            ) => write!(
227                f,
228                "Invalid cpu status {}",
229                status
230            ),
231            S7ConnectError::InvalidResponse {
232                reason,
233                bytes,
234            } => {
235                write!(
236                    f,
237                    "Invalid response {:?} err \
238                     {}",
239                    bytes, reason
240                )
241            },
242            // S7ConnectError::InvalidBitAddr(
243            //     addr
244            // ) => {
245            //     write!(
246            //         f,
247            //         "Invalid bit addr {}",
248            //         addr
249            //     )
250            // }
251        }
252    }
253}
254
255impl From<IOError> for S7ConnectError {
256    fn from(e: IOError) -> Self {
257        S7ConnectError::IOError(e.kind())
258    }
259}
260// This is important for other errors to wrap this
261// one.
262impl error::Error for S7ConnectError {
263    fn source(
264        &self,
265    ) -> Option<&(dyn error::Error + 'static)>
266    {
267        None
268    }
269}
270
271//CPUError specific CPU error after response
272fn cpu_error(err: i32) -> i32 {
273    match err {
274        CODE_7_ADDRESS_OUT_OF_RANGE => {
275            CLI_ADDRESS_OUT_OF_RANGE
276        },
277        CODE_7_INVALID_TRANSPORT_SIZE => {
278            CLI_INVALID_TRANSPORT_SIZE
279        },
280        CODE_7_WRITE_DATA_SIZE_MISMATCH => {
281            CLI_WRITE_DATA_SIZE_MISMATCH
282        },
283        CODE_7_RES_ITEM_NOT_AVAILABLE
284        | CODE_7_RES_ITEM_NOT_AVAILABLE1 => {
285            CLI_ITEM_NOT_AVAILABLE
286        },
287        CODE_7_DATA_OVER_PDU => CLI_SIZE_OVER_PDU,
288        CODE_7_INVALID_VALUE => CLI_INVALID_VALUE,
289        CODE_7_FUN_NOT_AVAILABLE => {
290            CLI_FUN_NOT_AVAILABLE
291        },
292        CODE_7_NEED_PASSWORD => CLI_NEED_PASSWORD,
293        CODE_7_INVALID_PASSWORD => {
294            CLI_INVALID_PASSWORD
295        },
296        CODE_7_NO_PASSWORD_TO_SET
297        | CODE_7_NO_PASSWORD_TO_CLEAR => {
298            CLI_NO_PASSWORD_TO_SET_OR_CLEAR
299        },
300        _ => CLI_FUNCTION_REFUSED,
301    }
302}
303
304//ErrorText return a string error text from error
305// code integer
306fn error_text(err: i32) -> &'static str {
307    match err {
308        0 => "OK",
309        TCP_SOCKET_CREATION => {
310            "SYS : Error creating the Socket"
311        },
312        TCP_CONNECTION_TIMEOUT => {
313            "TCP : Connection Timeout"
314        },
315        TCP_CONNECTION_FAILED => {
316            "TCP : Connection Error"
317        },
318        TCP_RECEIVE_TIMEOUT => {
319            "TCP : Data receive Timeout"
320        },
321        TCP_DATA_RECEIVE => {
322            "TCP : Error receiving Data"
323        },
324        TCP_SEND_TIMEOUT => {
325            "TCP : Data send Timeout"
326        },
327        TCP_DATA_SEND => {
328            "TCP : Error sending Data"
329        },
330        TCP_CONNECTION_RESET => {
331            "TCP : Connection reset by the Peer"
332        },
333        TCP_NOT_CONNECTED => {
334            "CLI : Client not connected"
335        },
336        TCP_UNREACHALE_HOST => {
337            "TCP : Unreachable host"
338        },
339
340        ISO_CONNECT => "ISO : Connection Error",
341        ISO_INVALID_PDU => {
342            "ISO : Invalid PDU received"
343        },
344        ISO_INVALID_DATA_SIZE => {
345            "ISO : Invalid Buffer passed to \
346             Send/Receive"
347        },
348
349        CLI_NEGOTIATING_PDU => {
350            "CLI : Error in PDU negotiation"
351        },
352        CLI_INVALID_PARAMS => {
353            "CLI : invalid param(s) supplied"
354        },
355        CLI_JOB_PENDING => "CLI : Job pending",
356        CLI_TOO_MANY_ITEMS => {
357            "CLI : too may items (>20) in multi \
358             read/write"
359        },
360        CLI_INVALID_DWORD_LEN => {
361            "CLI : invalid WordLength"
362        },
363        CLI_PARTIAL_DATA_WRITTEN => {
364            "CLI : Partial data written"
365        },
366        CLI_SIZE_OVER_PDU => {
367            "CPU : total data exceeds the PDU \
368             size"
369        },
370        CLI_INVALID_PLC_ANSWER => {
371            "CLI : invalid CPU answer"
372        },
373        CLI_ADDRESS_OUT_OF_RANGE => {
374            "CPU : Address out of range"
375        },
376        CLI_INVALID_TRANSPORT_SIZE => {
377            "CPU : Invalid Transport size"
378        },
379        CLI_WRITE_DATA_SIZE_MISMATCH => {
380            "CPU : Data size mismatch"
381        },
382        CLI_ITEM_NOT_AVAILABLE => {
383            "CPU : Item not available"
384        },
385        CLI_INVALID_VALUE => {
386            "CPU : Invalid value supplied"
387        },
388        CLI_CANNOT_START_PLC => {
389            "CPU : Cannot start PLC"
390        },
391        CLI_ALREADY_RUN => {
392            "CPU : PLC already RUN"
393        },
394        CLI_CANNOT_STOP_PLC => {
395            "CPU : Cannot stop PLC"
396        },
397        CLI_CANNOT_COPY_RAM_TO_ROM => {
398            "CPU : Cannot copy RAM to ROM"
399        },
400        CLI_CANNOT_COMPRESS => {
401            "CPU : Cannot compress"
402        },
403        CLI_ALREADY_STOP => {
404            "CPU : PLC already STOP"
405        },
406        CLI_FUN_NOT_AVAILABLE => {
407            "CPU : Function not available"
408        },
409        CLI_UPLOAD_SEQUENCE_FAILED => {
410            "CPU : Upload sequence failed"
411        },
412        CLI_INVALID_DATA_SIZE_RECVD => {
413            "CLI : Invalid data size received"
414        },
415        CLI_INVALID_BLOCK_TYPE => {
416            "CLI : Invalid block type"
417        },
418        CLI_INVALID_BLOCK_NUMBER => {
419            "CLI : Invalid block number"
420        },
421        CLI_INVALID_BLOCK_SIZE => {
422            "CLI : Invalid block size"
423        },
424        CLI_NEED_PASSWORD => {
425            "CPU : Function not authorized for \
426             current protection level"
427        },
428        CLI_INVALID_PASSWORD => {
429            "CPU : Invalid password"
430        },
431        CLI_NO_PASSWORD_TO_SET_OR_CLEAR => {
432            "CPU : No password to set or clear"
433        },
434        CLI_JOB_TIMEOUT => "CLI : Job Timeout",
435        CLI_FUNCTION_REFUSED => {
436            "CLI : function refused by CPU \
437             (Unknown error)"
438        },
439        CLI_PARTIAL_DATA_READ => {
440            "CLI : Partial data read"
441        },
442        CLI_BUFFER_TOO_SMALL => {
443            "CLI : The buffer supplied is too \
444             small to accomplish the operation"
445        },
446        CLI_DESTROYING => {
447            "CLI : Cannot perform (destroying)"
448        },
449        CLI_INVALID_PARAM_NUMBER => {
450            "CLI : Invalid Param Number"
451        },
452        CLI_CANNOT_CHANGE_PARAM => {
453            "CLI : Cannot change this param now"
454        },
455        CLI_FUNCTION_NOT_IMPLEMENTED => {
456            "CLI : Function not implemented"
457        },
458        _ => "CLI : Unknown error",
459    }
460}