or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client.mdconstants.mdindex.mdserver.mdutils.md

server.mddocs/

0

# Server Operations

1

2

TCP server functionality for creating Modbus servers that can handle client requests. The server components include the main ModbusServer class, data storage management through DataBank, request handling via DataHandler, and device identification configuration.

3

4

## Capabilities

5

6

### ModbusServer Class

7

8

Main server class that creates a multi-threaded TCP server to handle Modbus client connections.

9

10

```python { .api }

11

class ModbusServer:

12

"""

13

Modbus TCP server with multi-threaded client handling.

14

15

Parameters:

16

host (str): Server bind address (default: "localhost")

17

port (int): TCP port number (default: 502)

18

no_block (bool): Non-blocking server mode (default: False)

19

ipv6 (bool): Enable IPv6 support (default: False)

20

data_bank (DataBank): Data storage instance (default: new DataBank())

21

data_hdl (DataHandler): Request handler instance (default: new DataHandler())

22

ext_engine (object): External engine instance (default: None)

23

device_id (DeviceIdentification): Device identification info (default: None)

24

"""

25

def __init__(self, host="localhost", port=502, no_block=False, ipv6=False, data_bank=None, data_hdl=None, ext_engine=None, device_id=None):

26

"""Initialize Modbus TCP server."""

27

28

def start(self):

29

"""

30

Start the server.

31

32

Returns:

33

bool: True if server started successfully, False otherwise

34

"""

35

36

def stop(self):

37

"""Stop the server."""

38

39

def is_run(self):

40

"""

41

Check if server is running.

42

43

Returns:

44

bool: True if server is running, False otherwise

45

"""

46

```

47

48

#### Properties

49

50

```python { .api }

51

@property

52

def host(self):

53

"""str: Server bind address."""

54

55

@property

56

def port(self):

57

"""int: TCP port number."""

58

59

@property

60

def data_bank(self):

61

"""DataBank: Data storage instance."""

62

63

@property

64

def data_hdl(self):

65

"""DataHandler: Request handler instance."""

66

67

@property

68

def device_identification(self):

69

"""DeviceIdentification: Device identification instance (read-write)."""

70

```

71

72

### DataBank Class

73

74

Default data storage implementation providing in-memory storage for all Modbus data types.

75

76

```python { .api }

77

class DataBank:

78

"""

79

Default data bank for storing coils, discrete inputs, holding registers, and input registers.

80

81

Parameters:

82

coils_size (int): Number of coils (default: 0x10000)

83

coils_default_value (bool): Default coil value (default: False)

84

d_inputs_size (int): Number of discrete inputs (default: 0x10000)

85

d_inputs_default_value (bool): Default discrete input value (default: False)

86

h_regs_size (int): Number of holding registers (default: 0x10000)

87

h_regs_default_value (int): Default holding register value (default: 0)

88

i_regs_size (int): Number of input registers (default: 0x10000)

89

i_regs_default_value (int): Default input register value (default: 0)

90

virtual_mode (bool): Enable virtual mode for external data management (default: False)

91

"""

92

def __init__(self, coils_size=0x10000, coils_default_value=False,

93

d_inputs_size=0x10000, d_inputs_default_value=False,

94

h_regs_size=0x10000, h_regs_default_value=0,

95

i_regs_size=0x10000, i_regs_default_value=0,

96

virtual_mode=False):

97

"""Initialize data bank with specified sizes and default values."""

98

```

99

100

#### Coil Operations

101

102

```python { .api }

103

def get_coils(self, address, number=1, srv_info=None):

104

"""

105

Get coils values.

106

107

Parameters:

108

address (int): Starting coil address (0-65535)

109

number (int): Number of coils to read (default: 1)

110

srv_info (dict): Server information (optional)

111

112

Returns:

113

list[bool] or None: List of coil values, None on error

114

"""

115

116

def set_coils(self, address, bit_list, srv_info=None):

117

"""

118

Set coils values.

119

120

Parameters:

121

address (int): Starting coil address (0-65535)

122

bit_list (list[bool]): List of coil values to set

123

srv_info (dict): Server information (optional)

124

125

Returns:

126

bool: True on success, False on error

127

"""

128

```

129

130

#### Discrete Input Operations

131

132

```python { .api }

133

def get_discrete_inputs(self, address, number=1, srv_info=None):

134

"""

135

Get discrete inputs values.

136

137

Parameters:

138

address (int): Starting input address (0-65535)

139

number (int): Number of inputs to read (default: 1)

140

srv_info (dict): Server information (optional)

141

142

Returns:

143

list[bool] or None: List of input values, None on error

144

"""

145

146

def set_discrete_inputs(self, address, bit_list):

147

"""

148

Set discrete inputs values.

149

150

Parameters:

151

address (int): Starting input address (0-65535)

152

bit_list (list[bool]): List of input values to set

153

154

Returns:

155

bool: True on success, False on error

156

"""

157

```

158

159

#### Holding Register Operations

160

161

```python { .api }

162

def get_holding_registers(self, address, number=1, srv_info=None):

163

"""

164

Get holding registers values.

165

166

Parameters:

167

address (int): Starting register address (0-65535)

168

number (int): Number of registers to read (default: 1)

169

srv_info (dict): Server information (optional)

170

171

Returns:

172

list[int] or None: List of register values (0-65535), None on error

173

"""

174

175

def set_holding_registers(self, address, word_list, srv_info=None):

176

"""

177

Set holding registers values.

178

179

Parameters:

180

address (int): Starting register address (0-65535)

181

word_list (list[int]): List of register values to set (0-65535)

182

srv_info (dict): Server information (optional)

183

184

Returns:

185

bool: True on success, False on error

186

"""

187

```

188

189

#### Input Register Operations

190

191

```python { .api }

192

def get_input_registers(self, address, number=1, srv_info=None):

193

"""

194

Get input registers values.

195

196

Parameters:

197

address (int): Starting register address (0-65535)

198

number (int): Number of registers to read (default: 1)

199

srv_info (dict): Server information (optional)

200

201

Returns:

202

list[int] or None: List of register values (0-65535), None on error

203

"""

204

205

def set_input_registers(self, address, word_list):

206

"""

207

Set input registers values.

208

209

Parameters:

210

address (int): Starting register address (0-65535)

211

word_list (list[int]): List of register values to set (0-65535)

212

213

Returns:

214

bool: True on success, False on error

215

"""

216

```

217

218

#### Change Callbacks

219

220

```python { .api }

221

def on_coils_change(self, address, from_value, to_value, srv_info):

222

"""

223

Callback called when coils are modified (override in subclass).

224

225

Parameters:

226

address (int): Starting address that changed

227

from_value (list[bool]): Previous values

228

to_value (list[bool]): New values

229

srv_info (dict): Server information

230

"""

231

232

def on_holding_registers_change(self, address, from_value, to_value, srv_info):

233

"""

234

Callback called when holding registers are modified (override in subclass).

235

236

Parameters:

237

address (int): Starting address that changed

238

from_value (list[int]): Previous values

239

to_value (list[int]): New values

240

srv_info (dict): Server information

241

"""

242

```

243

244

#### Static Methods (Override Points)

245

246

```python { .api }

247

@staticmethod

248

def get_bits(*_args, **_kwargs):

249

"""Override this method for custom bit access logic."""

250

251

@staticmethod

252

def set_bits(*_args, **_kwargs):

253

"""Override this method for custom bit write logic."""

254

255

@staticmethod

256

def get_words(*_args, **_kwargs):

257

"""Override this method for custom word access logic."""

258

259

@staticmethod

260

def set_words(*_args, **_kwargs):

261

"""Override this method for custom word write logic."""

262

```

263

264

### DataHandler Class

265

266

Request handler that processes Modbus function codes and interacts with the data bank.

267

268

```python { .api }

269

class DataHandler:

270

"""

271

Modbus request handler for processing function codes.

272

273

Parameters:

274

data_bank (DataBank): Data storage instance (default: new DataBank())

275

"""

276

def __init__(self, data_bank=None):

277

"""Initialize request handler with data bank."""

278

```

279

280

#### Request Processing Methods

281

282

```python { .api }

283

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

284

"""

285

Handle read coils request (function code 1).

286

287

Parameters:

288

address (int): Starting coil address

289

count (int): Number of coils to read

290

srv_info (dict): Server information

291

292

Returns:

293

DataHandler.Return: Response object with success/error status and data

294

"""

295

296

def write_coils(self, address, bits_l, srv_info):

297

"""

298

Handle write coils request (function code 15).

299

300

Parameters:

301

address (int): Starting coil address

302

bits_l (list[bool]): List of coil values to write

303

srv_info (dict): Server information

304

305

Returns:

306

DataHandler.Return: Response object with success/error status

307

"""

308

309

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

310

"""

311

Handle read discrete inputs request (function code 2).

312

313

Parameters:

314

address (int): Starting input address

315

count (int): Number of inputs to read

316

srv_info (dict): Server information

317

318

Returns:

319

DataHandler.Return: Response object with success/error status and data

320

"""

321

322

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

323

"""

324

Handle read holding registers request (function code 3).

325

326

Parameters:

327

address (int): Starting register address

328

count (int): Number of registers to read

329

srv_info (dict): Server information

330

331

Returns:

332

DataHandler.Return: Response object with success/error status and data

333

"""

334

335

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

336

"""

337

Handle write holding registers request (function code 16).

338

339

Parameters:

340

address (int): Starting register address

341

words_l (list[int]): List of register values to write

342

srv_info (dict): Server information

343

344

Returns:

345

DataHandler.Return: Response object with success/error status

346

"""

347

348

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

349

"""

350

Handle read input registers request (function code 4).

351

352

Parameters:

353

address (int): Starting register address

354

count (int): Number of registers to read

355

srv_info (dict): Server information

356

357

Returns:

358

DataHandler.Return: Response object with success/error status and data

359

"""

360

```

361

362

### DeviceIdentification Class

363

364

Container for device identification objects used in function code 43 responses.

365

366

```python { .api }

367

class DeviceIdentification:

368

"""

369

Device identification information container.

370

371

Parameters:

372

vendor_name (bytes): Vendor name (object ID 0x00, default: b'')

373

product_code (bytes): Product code (object ID 0x01, default: b'')

374

major_minor_revision (bytes): Major/minor revision (object ID 0x02, default: b'')

375

vendor_url (bytes): Vendor URL (object ID 0x03, default: b'')

376

product_name (bytes): Product name (object ID 0x04, default: b'')

377

model_name (bytes): Model name (object ID 0x05, default: b'')

378

user_application_name (bytes): User application name (object ID 0x06, default: b'')

379

objects_id (dict): Additional identification objects (object_id: bytes_value, default: None)

380

"""

381

def __init__(self, vendor_name=b'', product_code=b'', major_minor_revision=b'',

382

vendor_url=b'', product_name=b'', model_name=b'', user_application_name=b'', objects_id=None):

383

"""Initialize device identification with standard objects and optional additional objects."""

384

385

def __getitem__(self, key):

386

"""

387

Get identification object by ID.

388

389

Parameters:

390

key (int): Object ID (0-255)

391

392

Returns:

393

bytes: Object value

394

"""

395

396

def __setitem__(self, key, value):

397

"""

398

Set identification object by ID.

399

400

Parameters:

401

key (int): Object ID (0-255)

402

value (bytes): Object value

403

"""

404

405

def items(self):

406

"""

407

Get all identification objects as key-value pairs.

408

409

Returns:

410

dict_items: All object ID and value pairs

411

"""

412

413

@property

414

def vendor_name(self):

415

"""bytes: Vendor name (object ID 0x00, read-only)."""

416

417

@property

418

def product_code(self):

419

"""bytes: Product code (object ID 0x01, read-only)."""

420

421

@property

422

def major_minor_revision(self):

423

"""bytes: Major/minor revision (object ID 0x02, read-only)."""

424

425

@property

426

def vendor_url(self):

427

"""bytes: Vendor URL (object ID 0x03, read-only)."""

428

429

@property

430

def product_name(self):

431

"""bytes: Product name (object ID 0x04, read-only)."""

432

433

@property

434

def model_name(self):

435

"""bytes: Model name (object ID 0x05, read-only)."""

436

437

@property

438

def user_application_name(self):

439

"""bytes: User application name (object ID 0x06, read-only)."""

440

```

441

442

## Usage Examples

443

444

### Basic Server Setup

445

446

```python

447

from pyModbusTCP.server import ModbusServer, DataBank

448

449

# Create data bank with initial values

450

data_bank = DataBank()

451

data_bank.set_holding_registers(0, [100, 200, 300, 400, 500])

452

data_bank.set_coils(0, [True, False, True, False])

453

454

# Create and start server

455

server = ModbusServer(host="0.0.0.0", port=502, data_bank=data_bank)

456

if server.start():

457

print("Server started successfully")

458

# Server runs in background thread

459

# Call server.stop() when done

460

else:

461

print("Failed to start server")

462

```

463

464

### Custom Data Bank with Change Monitoring

465

466

```python

467

from pyModbusTCP.server import ModbusServer, DataBank

468

469

class MonitoredDataBank(DataBank):

470

def on_holding_registers_change(self, address, from_value, to_value, srv_info):

471

print(f"Registers {address}-{address+len(to_value)-1} changed:")

472

print(f" From: {from_value}")

473

print(f" To: {to_value}")

474

print(f" Client: {srv_info.get('client_addr', 'unknown')}")

475

476

def on_coils_change(self, address, from_value, to_value, srv_info):

477

print(f"Coils {address}-{address+len(to_value)-1} changed:")

478

print(f" From: {from_value}")

479

print(f" To: {to_value}")

480

481

# Use custom data bank

482

custom_data_bank = MonitoredDataBank()

483

server = ModbusServer(data_bank=custom_data_bank)

484

server.start()

485

```

486

487

### Server with Device Identification

488

489

```python

490

from pyModbusTCP.server import ModbusServer, DataBank, DeviceIdentification

491

492

# Create device identification

493

device_id = DeviceIdentification(

494

vendor_name=b'My Company',

495

product_code=b'MC-001',

496

major_minor_revision=b'1.0',

497

vendor_url=b'https://mycompany.com',

498

product_name=b'Industrial Controller',

499

model_name=b'IC-2024',

500

user_application_name=b'Factory Automation'

501

)

502

503

# Configure server with device identification

504

data_bank = DataBank()

505

server = ModbusServer(data_bank=data_bank)

506

server.device_identification = device_id

507

server.start()

508

```

509

510

### Custom Request Handler

511

512

```python

513

from pyModbusTCP.server import ModbusServer, DataBank, DataHandler

514

from pyModbusTCP.constants import EXP_ILLEGAL_FUNCTION

515

516

class CustomDataHandler(DataHandler):

517

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

518

# Custom logic for holding register reads

519

if address >= 1000: # Restrict access to certain addresses

520

return self.Return(exp_code=EXP_ILLEGAL_FUNCTION)

521

522

# Log the request

523

client_addr = srv_info.get('client_addr', 'unknown')

524

print(f"Client {client_addr} reading {count} registers from {address}")

525

526

# Use default implementation

527

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

528

529

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

530

# Custom validation for writes

531

if any(w > 1000 for w in words_l): # Limit register values

532

return self.Return(exp_code=EXP_DATA_VALUE)

533

534

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

535

536

# Use custom handler

537

data_bank = DataBank()

538

custom_handler = CustomDataHandler(data_bank)

539

server = ModbusServer(data_bank=data_bank, data_hdl=custom_handler)

540

server.start()

541

```

542

543

### Virtual Data Generation

544

545

```python

546

from pyModbusTCP.server import ModbusServer, DataBank

547

import time

548

import threading

549

import random

550

551

class VirtualDataBank(DataBank):

552

def __init__(self):

553

super().__init__()

554

self._running = True

555

self._thread = threading.Thread(target=self._update_data)

556

self._thread.daemon = True

557

self._thread.start()

558

559

def _update_data(self):

560

"""Generate virtual data continuously."""

561

while self._running:

562

# Simulate sensor readings

563

temperature = int(random.uniform(18.0, 25.0) * 10) # Temperature * 10

564

humidity = int(random.uniform(30.0, 70.0))

565

pressure = int(random.uniform(990.0, 1020.0))

566

567

# Update input registers (sensor readings)

568

self.set_input_registers(0, [temperature, humidity, pressure])

569

570

# Update status coils

571

self.set_discrete_inputs(0, [True, temperature > 220, humidity > 60])

572

573

time.sleep(1)

574

575

def stop(self):

576

self._running = False

577

578

# Use virtual data bank

579

virtual_data = VirtualDataBank()

580

server = ModbusServer(data_bank=virtual_data)

581

server.start()

582

```

583

584

## Exception Classes

585

586

```python { .api }

587

class ModbusServer.Error(Exception):

588

"""Base class for server errors."""

589

590

class ModbusServer.NetworkError(Error):

591

"""Network-related server errors."""

592

593

class ModbusServer.DataFormatError(Error):

594

"""Data format validation errors."""

595

596

class DataHandler.ModbusExcept(Exception):

597

"""

598

Exception for Modbus protocol errors in request handling.

599

600

Attributes:

601

exp_code (int): Modbus exception code

602

data (bytes): Optional additional data

603

"""

604

```

605

606

## Container Classes

607

608

```python { .api }

609

class ModbusServer.ClientInfo:

610

"""

611

Container for client connection information.

612

613

Attributes:

614

address (str): Client IP address

615

port (int): Client port number

616

"""

617

618

class ModbusServer.ServerInfo:

619

"""

620

Container for server instance information.

621

622

Attributes:

623

host (str): Server bind address

624

port (int): Server port number

625

"""

626

627

class ModbusServer.SessionData:

628

"""

629

Container for server session data and state information.

630

631

Attributes:

632

client_addr (str): Connected client address

633

unit_id (int): Current request unit ID

634

transaction_id (int): Current transaction ID

635

"""

636

637

class ModbusServer.Frame:

638

"""Raw Modbus frame container."""

639

640

class ModbusServer.MBAP:

641

"""Modbus Application Protocol header container."""

642

643

class ModbusServer.PDU:

644

"""Protocol Data Unit container."""

645

```

646

647

## Types

648

649

```python { .api }

650

class DataHandler.Return:

651

"""

652

Return object for data handler methods.

653

654

Attributes:

655

exp_code (int): Exception code (0 for success, other values indicate specific errors)

656

data (list): Response data (for read operations, None for write operations)

657

658

Methods:

659

ok() -> bool: Returns True if operation was successful (exp_code == 0)

660

"""

661

```