or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client.mdconstants.mdindex.mdserver.mdutils.md

constants.mddocs/

0

# Constants and Error Handling

1

2

Protocol constants, function codes, exception codes, and error handling utilities for robust Modbus communication. These constants define the Modbus protocol specifications and provide comprehensive error reporting capabilities.

3

4

## Capabilities

5

6

### Package Constants

7

8

Basic package information and protocol defaults.

9

10

```python { .api }

11

VERSION = '0.3.0' # Package version string

12

MODBUS_PORT = 502 # Default Modbus TCP port

13

MAX_PDU_SIZE = 253 # Maximum Protocol Data Unit size

14

```

15

16

### Function Codes

17

18

Standard Modbus function codes supported by the library.

19

20

```python { .api }

21

# Read functions

22

READ_COILS = 0x01 # Read coils (outputs)

23

READ_DISCRETE_INPUTS = 0x02 # Read discrete inputs

24

READ_HOLDING_REGISTERS = 0x03 # Read holding registers

25

READ_INPUT_REGISTERS = 0x04 # Read input registers

26

27

# Write functions

28

WRITE_SINGLE_COIL = 0x05 # Write single coil

29

WRITE_SINGLE_REGISTER = 0x06 # Write single register

30

WRITE_MULTIPLE_COILS = 0x0F # Write multiple coils

31

WRITE_MULTIPLE_REGISTERS = 0x10 # Write multiple registers

32

33

# Advanced functions

34

WRITE_READ_MULTIPLE_REGISTERS = 0x17 # Write/read multiple registers

35

ENCAPSULATED_INTERFACE_TRANSPORT = 0x2B # Encapsulated interface transport

36

37

# Supported function codes tuple

38

SUPPORTED_FUNCTION_CODES = (

39

READ_COILS, READ_DISCRETE_INPUTS, READ_HOLDING_REGISTERS, READ_INPUT_REGISTERS,

40

WRITE_SINGLE_COIL, WRITE_SINGLE_REGISTER, WRITE_MULTIPLE_COILS, WRITE_MULTIPLE_REGISTERS,

41

WRITE_READ_MULTIPLE_REGISTERS, ENCAPSULATED_INTERFACE_TRANSPORT

42

)

43

```

44

45

### MEI Types

46

47

Modbus Encapsulated Interface (MEI) type definitions.

48

49

```python { .api }

50

MEI_TYPE_READ_DEVICE_ID = 0x0E # MEI type for read device identification

51

```

52

53

### Modbus Exception Codes

54

55

Standard Modbus exception codes returned by servers when errors occur.

56

57

```python { .api }

58

EXP_NONE = 0x00 # No exception

59

EXP_ILLEGAL_FUNCTION = 0x01 # Illegal function code

60

EXP_DATA_ADDRESS = 0x02 # Illegal data address

61

EXP_DATA_VALUE = 0x03 # Illegal data value

62

EXP_SLAVE_DEVICE_FAILURE = 0x04 # Slave device failure

63

EXP_ACKNOWLEDGE = 0x05 # Acknowledge

64

EXP_SLAVE_DEVICE_BUSY = 0x06 # Slave device busy

65

EXP_NEGATIVE_ACKNOWLEDGE = 0x07 # Negative acknowledge

66

EXP_MEMORY_PARITY_ERROR = 0x08 # Memory parity error

67

EXP_GATEWAY_PATH_UNAVAILABLE = 0x0A # Gateway path unavailable

68

EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0x0B # Gateway target device failed to respond

69

```

70

71

### Exception Text Mappings

72

73

Human-readable descriptions for exception codes.

74

75

```python { .api }

76

# Short exception descriptions

77

EXP_TXT = {

78

EXP_NONE: 'no exception',

79

EXP_ILLEGAL_FUNCTION: 'illegal function',

80

EXP_DATA_ADDRESS: 'illegal data address',

81

EXP_DATA_VALUE: 'illegal data value',

82

EXP_SLAVE_DEVICE_FAILURE: 'slave device failure',

83

EXP_ACKNOWLEDGE: 'acknowledge',

84

EXP_SLAVE_DEVICE_BUSY: 'slave device busy',

85

EXP_NEGATIVE_ACKNOWLEDGE: 'negative acknowledge',

86

EXP_MEMORY_PARITY_ERROR: 'memory parity error',

87

EXP_GATEWAY_PATH_UNAVAILABLE: 'gateway path unavailable',

88

EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND: 'gateway target device failed to respond'

89

}

90

91

# Detailed exception descriptions

92

EXP_DETAILS = {

93

EXP_NONE: 'The last request produced no exceptions.',

94

EXP_ILLEGAL_FUNCTION: 'Function code received in the query is not recognized or allowed by slave.',

95

EXP_DATA_ADDRESS: 'Data address of some or all the required entities are not allowed or do not exist in slave.',

96

EXP_DATA_VALUE: 'Value is not accepted by slave.',

97

EXP_SLAVE_DEVICE_FAILURE: 'Unrecoverable error occurred while slave was attempting to perform requested action.',

98

EXP_ACKNOWLEDGE: 'Slave has accepted request and is processing it, but a long duration of time is required. This response is returned to prevent a timeout error from occurring in the master. Master can next issue a Poll Program Complete message to determine whether processing is completed.',

99

EXP_SLAVE_DEVICE_BUSY: 'Slave is engaged in processing a long-duration command. Master should retry later.',

100

EXP_NEGATIVE_ACKNOWLEDGE: 'Slave cannot perform the programming functions. Master should request diagnostic or error information from slave.',

101

EXP_MEMORY_PARITY_ERROR: 'Slave detected a parity error in memory. Master can retry the request, but service may be required on the slave device.',

102

EXP_GATEWAY_PATH_UNAVAILABLE: 'Specialized for Modbus gateways, this indicates a misconfiguration on gateway.',

103

EXP_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND: 'Specialized for Modbus gateways, sent when slave fails to respond.'

104

}

105

```

106

107

### Module Error Codes

108

109

Internal library error codes for network and communication issues.

110

111

```python { .api }

112

MB_NO_ERR = 0 # No error

113

MB_RESOLVE_ERR = 1 # Name resolve error

114

MB_CONNECT_ERR = 2 # Connect error

115

MB_SEND_ERR = 3 # Socket send error

116

MB_RECV_ERR = 4 # Socket receive error

117

MB_TIMEOUT_ERR = 5 # Receive timeout error

118

MB_FRAME_ERR = 6 # Frame format error

119

MB_EXCEPT_ERR = 7 # Modbus exception error

120

MB_CRC_ERR = 8 # Bad CRC on receive frame

121

MB_SOCK_CLOSE_ERR = 9 # Socket is closed error

122

```

123

124

### Module Error Text Mapping

125

126

Human-readable descriptions for module error codes.

127

128

```python { .api }

129

MB_ERR_TXT = {

130

MB_NO_ERR: 'no error',

131

MB_RESOLVE_ERR: 'name resolve error',

132

MB_CONNECT_ERR: 'connect error',

133

MB_SEND_ERR: 'socket send error',

134

MB_RECV_ERR: 'socket recv error',

135

MB_TIMEOUT_ERR: 'recv timeout occur',

136

MB_FRAME_ERR: 'frame format error',

137

MB_EXCEPT_ERR: 'modbus exception',

138

MB_CRC_ERR: 'bad CRC on receive frame',

139

MB_SOCK_CLOSE_ERR: 'socket is closed'

140

}

141

```

142

143

## Usage Examples

144

145

### Using Function Codes

146

147

```python

148

from pyModbusTCP.client import ModbusClient

149

from pyModbusTCP.constants import READ_HOLDING_REGISTERS, WRITE_MULTIPLE_REGISTERS

150

151

client = ModbusClient(host="192.168.1.100", auto_open=True)

152

153

# Function codes are used internally, but you can reference them

154

print(f"Reading using function code: {READ_HOLDING_REGISTERS}")

155

registers = client.read_holding_registers(0, 10)

156

157

print(f"Writing using function code: {WRITE_MULTIPLE_REGISTERS}")

158

success = client.write_multiple_registers(0, [100, 200, 300])

159

```

160

161

### Error Handling with Constants

162

163

```python

164

from pyModbusTCP.client import ModbusClient

165

from pyModbusTCP.constants import (

166

MB_NO_ERR, MB_CONNECT_ERR, MB_TIMEOUT_ERR, MB_ERR_TXT,

167

EXP_NONE, EXP_DATA_ADDRESS, EXP_TXT, EXP_DETAILS

168

)

169

170

client = ModbusClient(host="192.168.1.100", auto_open=True)

171

172

# Attempt to read registers

173

registers = client.read_holding_registers(0, 10)

174

175

if registers is None:

176

# Check for network/connection errors

177

error_code = client.last_error

178

if error_code != MB_NO_ERR:

179

print(f"Network error: {MB_ERR_TXT.get(error_code, 'unknown error')}")

180

181

if error_code == MB_CONNECT_ERR:

182

print("Check if server is running and network is accessible")

183

elif error_code == MB_TIMEOUT_ERR:

184

print("Server not responding, consider increasing timeout")

185

186

# Check for Modbus exceptions

187

except_code = client.last_except

188

if except_code != EXP_NONE:

189

print(f"Modbus exception: {EXP_TXT.get(except_code, 'unknown exception')}")

190

print(f"Details: {EXP_DETAILS.get(except_code, 'No details available')}")

191

192

if except_code == EXP_DATA_ADDRESS:

193

print("The requested register address may not exist on the server")

194

else:

195

print(f"Successfully read registers: {registers}")

196

```

197

198

### Server Exception Handling

199

200

```python

201

from pyModbusTCP.server import ModbusServer, DataHandler

202

from pyModbusTCP.constants import (

203

EXP_DATA_ADDRESS, EXP_DATA_VALUE, EXP_ILLEGAL_FUNCTION

204

)

205

206

class ValidatingDataHandler(DataHandler):

207

def read_h_regs(self, address, count, srv_info):

208

# Validate address range

209

if address >= 1000:

210

return self.Return(exp_code=EXP_DATA_ADDRESS)

211

212

# Validate count

213

if count > 100:

214

return self.Return(exp_code=EXP_DATA_VALUE)

215

216

return super().read_h_regs(address, count, srv_info)

217

218

def write_h_regs(self, address, words_l, srv_info):

219

# Validate write address

220

if address < 100: # Read-only area

221

return self.Return(exp_code=EXP_DATA_ADDRESS)

222

223

# Validate data values

224

if any(w > 32767 for w in words_l): # Signed 16-bit limit

225

return self.Return(exp_code=EXP_DATA_VALUE)

226

227

return super().write_h_regs(address, words_l, srv_info)

228

229

# Use validating handler

230

handler = ValidatingDataHandler()

231

server = ModbusServer(data_hdl=handler)

232

server.start()

233

```

234

235

### Function Code Validation

236

237

```python

238

from pyModbusTCP.constants import SUPPORTED_FUNCTION_CODES

239

240

def is_supported_function(func_code):

241

"""Check if a function code is supported."""

242

return func_code in SUPPORTED_FUNCTION_CODES

243

244

# Check function code support

245

test_functions = [0x01, 0x03, 0x10, 0x2B, 0xFF]

246

for func in test_functions:

247

supported = is_supported_function(func)

248

print(f"Function code 0x{func:02X}: {'supported' if supported else 'not supported'}")

249

```

250

251

### Comprehensive Error Reporting

252

253

```python

254

from pyModbusTCP.client import ModbusClient

255

from pyModbusTCP.constants import *

256

257

def detailed_error_report(client):

258

"""Generate detailed error report for a client."""

259

report = []

260

261

# Network error information

262

error_code = client.last_error

263

if error_code != MB_NO_ERR:

264

error_text = MB_ERR_TXT.get(error_code, f'Unknown error code: {error_code}')

265

report.append(f"Network Error ({error_code}): {error_text}")

266

267

# Modbus exception information

268

except_code = client.last_except

269

if except_code != EXP_NONE:

270

except_text = EXP_TXT.get(except_code, f'Unknown exception code: {except_code}')

271

except_details = EXP_DETAILS.get(except_code, 'No additional details available')

272

report.append(f"Modbus Exception ({except_code}): {except_text}")

273

report.append(f"Details: {except_details}")

274

275

return report if report else ["No errors detected"]

276

277

# Example usage

278

client = ModbusClient(host="192.168.1.100", timeout=5.0, auto_open=True)

279

280

# Attempt operation that might fail

281

result = client.read_holding_registers(9999, 100) # Likely to cause address error

282

283

if result is None:

284

error_info = detailed_error_report(client)

285

print("Error Report:")

286

for line in error_info:

287

print(f" {line}")

288

```

289

290

### Protocol Configuration

291

292

```python

293

from pyModbusTCP.server import ModbusServer

294

from pyModbusTCP.constants import MODBUS_PORT, MAX_PDU_SIZE

295

296

# Use standard constants for configuration

297

server = ModbusServer(

298

host="0.0.0.0",

299

port=MODBUS_PORT, # Standard Modbus TCP port

300

no_block=False

301

)

302

303

print(f"Server configured on port {MODBUS_PORT}")

304

print(f"Maximum PDU size: {MAX_PDU_SIZE} bytes")

305

server.start()

306

```

307

308

## Error Handling Best Practices

309

310

### Client-Side Error Handling

311

312

```python

313

from pyModbusTCP.client import ModbusClient

314

from pyModbusTCP.constants import *

315

316

def robust_read_registers(client, address, count, max_retries=3):

317

"""Robust register reading with retry logic."""

318

for attempt in range(max_retries):

319

result = client.read_holding_registers(address, count)

320

321

if result is not None:

322

return result

323

324

error_code = client.last_error

325

except_code = client.last_except

326

327

if error_code == MB_TIMEOUT_ERR:

328

print(f"Timeout on attempt {attempt + 1}, retrying...")

329

continue

330

elif except_code == EXP_SLAVE_DEVICE_BUSY:

331

print(f"Device busy on attempt {attempt + 1}, retrying...")

332

continue

333

else:

334

# Non-recoverable error

335

break

336

337

return None

338

339

# Usage

340

client = ModbusClient(host="192.168.1.100", auto_open=True)

341

registers = robust_read_registers(client, 0, 10)

342

if registers:

343

print(f"Successfully read: {registers}")

344

else:

345

print("Failed to read registers after retries")

346

```

347

348

### Server-Side Validation

349

350

```python

351

from pyModbusTCP.server import ModbusServer, DataHandler

352

from pyModbusTCP.constants import *

353

354

class RobustDataHandler(DataHandler):

355

def __init__(self, data_bank=None):

356

super().__init__(data_bank)

357

self.valid_read_ranges = [(0, 999), (2000, 2999)] # Valid address ranges

358

self.valid_write_ranges = [(100, 899), (2100, 2899)]

359

360

def _validate_read_address(self, address, count):

361

"""Validate if address range is readable."""

362

end_address = address + count - 1

363

for start, end in self.valid_read_ranges:

364

if start <= address <= end and start <= end_address <= end:

365

return True

366

return False

367

368

def _validate_write_address(self, address, count):

369

"""Validate if address range is writable."""

370

end_address = address + count - 1

371

for start, end in self.valid_write_ranges:

372

if start <= address <= end and start <= end_address <= end:

373

return True

374

return False

375

376

def read_h_regs(self, address, count, srv_info):

377

if not self._validate_read_address(address, count):

378

return self.Return(exp_code=EXP_DATA_ADDRESS)

379

380

if count > 125: # Modbus limit

381

return self.Return(exp_code=EXP_DATA_VALUE)

382

383

return super().read_h_regs(address, count, srv_info)

384

385

def write_h_regs(self, address, words_l, srv_info):

386

if not self._validate_write_address(address, len(words_l)):

387

return self.Return(exp_code=EXP_DATA_ADDRESS)

388

389

# Validate data values

390

for word in words_l:

391

if not (0 <= word <= 65535):

392

return self.Return(exp_code=EXP_DATA_VALUE)

393

394

return super().write_h_regs(address, words_l, srv_info)

395

396

# Use robust handler

397

handler = RobustDataHandler()

398

server = ModbusServer(data_hdl=handler)

399

server.start()

400

```