-
Notifications
You must be signed in to change notification settings - Fork 54
/
ErrorResponse.php
140 lines (122 loc) · 3.58 KB
/
ErrorResponse.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<?php
declare(strict_types=1);
namespace ModbusTcpClient\Packet;
use ModbusTcpClient\Utils\Types;
/**
* Modbus ErrorResponse packet
*
* Example packet: \xda\x87\x00\x00\x00\x03\x01\x81\x03
* \xda\x87 - transaction id
* \x00\x00 - protocol id
* \x00\x03 - number of bytes in the message (PDU = ProtocolDataUnit) to follow
* \x01 - unit id
* \x81 - function code + 128 (exception bitmask)
* \x03 - error code
*
*/
class ErrorResponse implements ModbusResponse
{
/**
* @var int Modbus exceptions are transferred in function code byte and have their high bit set (128)
*/
const EXCEPTION_BITMASK = 128;
/**
* @var ModbusApplicationHeader
*/
private ModbusApplicationHeader $header;
/**
* @var int
*/
private int $functionCode;
/**
* @var int
*/
private int $errorCode;
public function __construct(ModbusApplicationHeader $header, int $functionCode, int $errorCode)
{
$this->header = $header;
$this->functionCode = $functionCode;
$this->errorCode = $errorCode;
}
public function getHeader(): ModbusApplicationHeader
{
return $this->header;
}
public function getFunctionCode(): int
{
return $this->functionCode;
}
public function getErrorCode(): int
{
return $this->errorCode;
}
public function getErrorMessage(): string
{
switch ($this->errorCode) {
case 1:
$message = 'Illegal function';
break;
case 2:
$message = 'Illegal data address';
break;
case 3:
$message = 'Illegal data value';
break;
case 4:
$message = 'Server failure';
break;
case 5:
$message = 'Acknowledge';
break;
case 6:
$message = 'Server busy';
break;
case 10:
$message = 'Gateway path unavailable';
break;
case 11:
$message = 'Gateway targeted device failed to respond';
break;
default:
$message = "Unknown error code ($this->errorCode)";
break;
}
return $message;
}
public function getLength(): int
{
return 2; // 2 bytes for function code and error code
}
public function __toString(): string
{
return b''
. $this->getHeader()
. Types::toByte($this->getFunctionCode() + self::EXCEPTION_BITMASK)
. Types::toByte($this->getErrorCode());
}
public function toHex(): string
{
return unpack('H*', $this->__toString())[1];
}
public function withStartAddress(int $startAddress): static
{
return clone $this; // just to have same interface as 'success' responses
}
/**
* is checks if given binary string is complete MODBUS TCP error packet
* NB: do not use for RTU packets
*
* @param string|null $binaryData binary string to be checked
* @return bool true if data is actual error packet
*/
public static function is(string|null $binaryData): bool
{
// a) data is too short. can not determine packet.
// b) data is too long. can not be an error packet
// Actual packet is at least 9 bytes. 7 bytes for Modbus TCP header and at least 2 bytes for PDU
if (strlen($binaryData) !== 9) {
return false;
}
return (ord($binaryData[7]) & self::EXCEPTION_BITMASK) > 0;
}
}