or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client.mddatastore.mdindex.mdpdu.mdserver.md

datastore.mddocs/

0

# Data Store

1

2

PyModbus provides flexible data storage implementations for server contexts, supporting both sequential and sparse memory layouts with full customization capabilities. The datastore system manages coils, discrete inputs, holding registers, and input registers with configurable addressing modes.

3

4

## Capabilities

5

6

### Server Context Management

7

8

Classes for managing server data contexts and device instances.

9

10

```python { .api }

11

class ModbusServerContext:

12

def __init__(self, device_default=None, single=False, **kwargs):

13

"""

14

Server context managing multiple device contexts.

15

16

Parameters:

17

- device_default: Default device context for single device mode

18

- single (bool): True for single device mode, False for multi-device

19

- **kwargs: Additional context parameters

20

"""

21

22

def __getitem__(self, device_id: int):

23

"""Get device context by device ID."""

24

25

def __setitem__(self, device_id: int, context):

26

"""Set device context for device ID."""

27

28

def __delitem__(self, device_id: int):

29

"""Remove device context for device ID."""

30

31

def __contains__(self, device_id: int) -> bool:

32

"""Check if device ID exists in context."""

33

34

def __iter__(self):

35

"""Iterate over device IDs."""

36

37

class ModbusDeviceContext:

38

def __init__(self, co=None, di=None, hr=None, ir=None, zero_mode=False):

39

"""

40

Device context managing data stores for different data types.

41

42

Parameters:

43

- co: Coils data store

44

- di: Discrete inputs data store

45

- hr: Holding registers data store

46

- ir: Input registers data store

47

- zero_mode (bool): Use zero-based addressing if True

48

"""

49

50

def validate(self, fx: int, address: int, count: int = 1) -> bool:

51

"""

52

Validate data store access.

53

54

Parameters:

55

- fx (int): Function code (1=coils, 2=discrete inputs, 3=holding regs, 4=input regs)

56

- address (int): Starting address

57

- count (int): Number of values

58

59

Returns:

60

bool: True if access is valid

61

"""

62

63

def getValues(self, fx: int, address: int, count: int = 1) -> list:

64

"""

65

Get values from data store.

66

67

Parameters:

68

- fx (int): Function code

69

- address (int): Starting address

70

- count (int): Number of values to read

71

72

Returns:

73

list: Retrieved values

74

"""

75

76

def setValues(self, fx: int, address: int, values: list):

77

"""

78

Set values in data store.

79

80

Parameters:

81

- fx (int): Function code

82

- address (int): Starting address

83

- values (list): Values to write

84

"""

85

86

class ModbusBaseDeviceContext:

87

def decode(self, fx: int) -> str:

88

"""Decode function code to data type name."""

89

90

def encode(self, fx: int) -> int:

91

"""Encode data type name to function code."""

92

93

def reset(self):

94

"""Reset all data stores to default values."""

95

96

def register(self, fx: int, slave_id: int = 0x00, func=None):

97

"""Register custom function handler."""

98

99

def validate(self, fx: int, address: int, count: int = 1) -> bool:

100

"""Validate data store access."""

101

102

def getValues(self, fx: int, address: int, count: int = 1) -> list:

103

"""Get values from data store."""

104

105

def setValues(self, fx: int, address: int, values: list):

106

"""Set values in data store."""

107

```

108

109

### Data Block Implementations

110

111

Different data storage backends for various use cases.

112

113

```python { .api }

114

class ModbusSequentialDataBlock:

115

def __init__(self, address: int, values: list):

116

"""

117

Sequential data block with contiguous memory layout.

118

119

Parameters:

120

- address (int): Starting address

121

- values (list): Initial values (list of integers for registers, booleans for coils)

122

"""

123

124

def validate(self, address: int, count: int = 1) -> bool:

125

"""

126

Validate address range access.

127

128

Parameters:

129

- address (int): Starting address

130

- count (int): Number of values

131

132

Returns:

133

bool: True if address range is valid

134

"""

135

136

def getValues(self, address: int, count: int = 1) -> list:

137

"""

138

Get values from the data block.

139

140

Parameters:

141

- address (int): Starting address

142

- count (int): Number of values to read

143

144

Returns:

145

list: Retrieved values

146

"""

147

148

def setValues(self, address: int, values: list):

149

"""

150

Set values in the data block.

151

152

Parameters:

153

- address (int): Starting address

154

- values (list): Values to write

155

"""

156

157

def reset(self):

158

"""Reset all values to 0."""

159

160

class ModbusSparseDataBlock:

161

def __init__(self, values: dict = None):

162

"""

163

Sparse data block with dictionary-based storage.

164

165

Parameters:

166

- values (dict): Dictionary mapping addresses to values

167

"""

168

169

def validate(self, address: int, count: int = 1) -> bool:

170

"""Validate address range access."""

171

172

def getValues(self, address: int, count: int = 1) -> list:

173

"""Get values from specific addresses."""

174

175

def setValues(self, address: int, values: list):

176

"""Set values at specific addresses."""

177

178

def reset(self):

179

"""Clear all stored values."""

180

```

181

182

### Simulator Context

183

184

Specialized context for simulation with advanced features.

185

186

```python { .api }

187

class ModbusSimulatorContext:

188

def __init__(self, config: dict = None, custom_actions_module=None):

189

"""

190

Simulator context with configurable behavior.

191

192

Parameters:

193

- config (dict): Simulation configuration

194

- custom_actions_module: Module containing custom action functions

195

"""

196

197

def validate(self, fx: int, address: int, count: int = 1) -> bool:

198

"""Validate access with simulation rules."""

199

200

def getValues(self, fx: int, address: int, count: int = 1) -> list:

201

"""Get values with simulation behavior."""

202

203

def setValues(self, fx: int, address: int, values: list):

204

"""Set values with simulation actions."""

205

206

def apply_actions(self, fx: int, address: int, values: list):

207

"""Apply configured simulation actions."""

208

209

def reset(self):

210

"""Reset simulator to initial state."""

211

```

212

213

## Usage Examples

214

215

### Basic Sequential Data Store

216

217

```python

218

from pymodbus.datastore import ModbusSequentialDataBlock, ModbusDeviceContext, ModbusServerContext

219

220

# Create data blocks for different data types

221

coils = ModbusSequentialDataBlock(0, [False] * 100) # 100 coils starting at address 0

222

discrete_inputs = ModbusSequentialDataBlock(0, [False] * 100) # 100 discrete inputs

223

holding_registers = ModbusSequentialDataBlock(0, [0] * 100) # 100 holding registers

224

input_registers = ModbusSequentialDataBlock(0, [0] * 100) # 100 input registers

225

226

# Create device context with data stores

227

device_context = ModbusDeviceContext(

228

co=coils, # coils

229

di=discrete_inputs, # discrete inputs

230

hr=holding_registers, # holding registers

231

ir=input_registers # input registers

232

)

233

234

# Create server context (single device mode)

235

server_context = ModbusServerContext(device_default=device_context, single=True)

236

```

237

238

### Multi-Device Data Store

239

240

```python

241

from pymodbus.datastore import ModbusSequentialDataBlock, ModbusDeviceContext, ModbusServerContext

242

243

# Create different data stores for different devices

244

def create_device_context(initial_values):

245

store = ModbusSequentialDataBlock(0, initial_values)

246

return ModbusDeviceContext(co=store, di=store, hr=store, ir=store)

247

248

# Device 1: Sensor data

249

device1 = create_device_context([10, 20, 30, 40, 50])

250

251

# Device 2: Actuator controls

252

device2 = create_device_context([100, 200, 300, 400, 500])

253

254

# Device 3: System status

255

device3 = create_device_context([1, 0, 1, 1, 0])

256

257

# Create multi-device server context

258

server_context = ModbusServerContext(single=False)

259

server_context[1] = device1 # Device ID 1

260

server_context[2] = device2 # Device ID 2

261

server_context[3] = device3 # Device ID 3

262

263

# Check device availability

264

if 1 in server_context:

265

print("Device 1 is available")

266

267

# Get device context

268

device1_context = server_context[1]

269

values = device1_context.getValues(3, 0, 5) # Read holding registers

270

print(f"Device 1 holding registers: {values}")

271

```

272

273

### Sparse Data Store

274

275

```python

276

from pymodbus.datastore import ModbusSparseDataBlock, ModbusDeviceContext, ModbusServerContext

277

278

# Create sparse data blocks with non-contiguous addresses

279

sparse_coils = ModbusSparseDataBlock({

280

0: True, # Address 0

281

10: False, # Address 10

282

100: True, # Address 100

283

1000: False # Address 1000

284

})

285

286

sparse_registers = ModbusSparseDataBlock({

287

0: 12345, # Address 0

288

50: 67890, # Address 50

289

500: 11111, # Address 500

290

5000: 22222 # Address 5000

291

})

292

293

# Create device context with sparse stores

294

device_context = ModbusDeviceContext(

295

co=sparse_coils,

296

di=sparse_coils,

297

hr=sparse_registers,

298

ir=sparse_registers

299

)

300

301

server_context = ModbusServerContext(device_default=device_context, single=True)

302

303

# Access sparse data

304

print(f"Coil at address 0: {device_context.getValues(1, 0, 1)}")

305

print(f"Register at address 50: {device_context.getValues(3, 50, 1)}")

306

```

307

308

### Custom Data Store Implementation

309

310

```python

311

from pymodbus.datastore import ModbusDeviceContext, ModbusServerContext

312

313

class DatabaseDataBlock:

314

"""Custom data block that stores values in a database."""

315

316

def __init__(self, table_name, db_connection):

317

self.table_name = table_name

318

self.db = db_connection

319

320

def validate(self, address, count=1):

321

# Check if addresses exist in database

322

return True # Simplified validation

323

324

def getValues(self, address, count=1):

325

# Read values from database

326

cursor = self.db.cursor()

327

cursor.execute(

328

f"SELECT value FROM {self.table_name} WHERE address BETWEEN ? AND ? ORDER BY address",

329

(address, address + count - 1)

330

)

331

results = cursor.fetchall()

332

return [row[0] for row in results]

333

334

def setValues(self, address, values):

335

# Write values to database

336

cursor = self.db.cursor()

337

for i, value in enumerate(values):

338

cursor.execute(

339

f"INSERT OR REPLACE INTO {self.table_name} (address, value) VALUES (?, ?)",

340

(address + i, value)

341

)

342

self.db.commit()

343

344

# Usage (assuming database connection exists)

345

# db_store = DatabaseDataBlock('modbus_registers', db_connection)

346

# device_context = ModbusDeviceContext(hr=db_store, ir=db_store)

347

```

348

349

### Data Store with Validation

350

351

```python

352

from pymodbus.datastore import ModbusSequentialDataBlock, ModbusDeviceContext

353

354

class ValidatedDataBlock(ModbusSequentialDataBlock):

355

"""Data block with value validation."""

356

357

def __init__(self, address, values, min_value=0, max_value=65535):

358

super().__init__(address, values)

359

self.min_value = min_value

360

self.max_value = max_value

361

362

def setValues(self, address, values):

363

# Validate values before storing

364

validated_values = []

365

for value in values:

366

if isinstance(value, bool):

367

validated_values.append(value)

368

else:

369

# Clamp numeric values to valid range

370

clamped = max(self.min_value, min(self.max_value, int(value)))

371

validated_values.append(clamped)

372

373

super().setValues(address, validated_values)

374

375

# Create validated data store

376

validated_registers = ValidatedDataBlock(0, [0] * 100, min_value=0, max_value=1000)

377

device_context = ModbusDeviceContext(hr=validated_registers)

378

379

# Values will be clamped to valid range

380

device_context.setValues(3, 0, [500, 1500, -100]) # [500, 1000, 0]

381

```

382

383

### Zero-Based vs One-Based Addressing

384

385

```python

386

from pymodbus.datastore import ModbusSequentialDataBlock, ModbusDeviceContext

387

388

# Create data store

389

registers = ModbusSequentialDataBlock(0, list(range(100)))

390

391

# Zero-based addressing (default)

392

zero_based_context = ModbusDeviceContext(hr=registers, zero_mode=True)

393

394

# One-based addressing (traditional Modbus)

395

one_based_context = ModbusDeviceContext(hr=registers, zero_mode=False)

396

397

# With zero_mode=True, address 0 maps to first element

398

zero_values = zero_based_context.getValues(3, 0, 5) # Gets elements [0,1,2,3,4]

399

400

# With zero_mode=False, address 1 maps to first element

401

one_values = one_based_context.getValues(3, 1, 5) # Gets elements [0,1,2,3,4]

402

403

print(f"Zero-based: {zero_values}")

404

print(f"One-based: {one_values}")

405

```

406

407

### Dynamic Data Store Updates

408

409

```python

410

import threading

411

import time

412

from pymodbus.datastore import ModbusSequentialDataBlock, ModbusDeviceContext, ModbusServerContext

413

414

# Create data store

415

registers = ModbusSequentialDataBlock(0, [0] * 100)

416

device_context = ModbusDeviceContext(hr=registers)

417

server_context = ModbusServerContext(device_default=device_context, single=True)

418

419

def update_simulation_data():

420

"""Background thread to update simulation data."""

421

counter = 0

422

while True:

423

# Update some register values

424

device_context.setValues(3, 0, [counter]) # Counter at address 0

425

device_context.setValues(3, 1, [counter % 100]) # Modulo counter at address 1

426

device_context.setValues(3, 2, [int(time.time()) % 65536]) # Timestamp at address 2

427

428

counter += 1

429

time.sleep(1)

430

431

# Start background update thread

432

update_thread = threading.Thread(target=update_simulation_data, daemon=True)

433

update_thread.start()

434

435

# Server context now has dynamically updating data

436

```

437

438

### Simulator Context Configuration

439

440

```python

441

from pymodbus.datastore import ModbusSimulatorContext

442

443

# Simulator configuration

444

simulator_config = {

445

"setup": {

446

"co size": 100,

447

"di size": 100,

448

"hr size": 100,

449

"ir size": 100

450

},

451

"invalid": [

452

{"device": 1, "function": 3, "address": [5, 6]}, # Invalid addresses

453

],

454

"write": [

455

{"device": 1, "function": 16, "address": 10, "action": "random"} # Random values on write

456

],

457

"repeat": [

458

{"device": 1, "function": 3, "address": 20, "period": 5} # Update every 5 seconds

459

]

460

}

461

462

# Create simulator context

463

simulator_context = ModbusSimulatorContext(config=simulator_config)

464

465

# Simulator will handle reads/writes according to configuration

466

```

467

468

### Function Code Constants

469

470

The datastore system uses function codes to identify data types:

471

472

```python

473

# Function codes for data store access

474

FUNCTION_CODES = {

475

1: "coils", # Read Coils

476

2: "discrete_inputs", # Read Discrete Inputs

477

3: "holding_registers", # Read Holding Registers

478

4: "input_registers", # Read Input Registers

479

5: "coils", # Write Single Coil

480

6: "holding_registers", # Write Single Register

481

15: "coils", # Write Multiple Coils

482

16: "holding_registers" # Write Multiple Registers

483

}

484

485

# Example of function code usage

486

device_context = ModbusDeviceContext(hr=registers)

487

values = device_context.getValues(3, 0, 10) # Function code 3 = holding registers

488

device_context.setValues(3, 0, [1, 2, 3]) # Write to holding registers

489

```