Skip to content

Commit 2c4fd5a

Browse files
committed
Add server.async_get/setValues.
1 parent 97c9530 commit 2c4fd5a

File tree

20 files changed

+217
-226
lines changed

20 files changed

+217
-226
lines changed

API_changes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ API changes 3.13.0
66
------------------
77
- removed RemoteDeviceContext, because it only is a partial forwarder
88
a proper forwarder should be made at frame level.
9-
- datastore get/setValues only exist as async_get/set
9+
- datastore get/setValues is removed,
10+
please use server.async_get/setValues instead.
1011

1112
API changes 3.12.0
1213
------------------

doc/source/_static/examples.tgz

-314 Bytes
Binary file not shown.

doc/source/_static/examples.zip

-1 Bytes
Binary file not shown.

pymodbus/datastore/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Datastore."""
22

33
__all__ = [
4-
"ModbusBaseDeviceContext",
54
"ModbusDeviceContext",
65
"ModbusSequentialDataBlock",
76
"ModbusServerContext",
@@ -10,7 +9,6 @@
109
]
1110

1211
from .context import (
13-
ModbusBaseDeviceContext,
1412
ModbusDeviceContext,
1513
ModbusServerContext,
1614
)

pymodbus/datastore/context.py

Lines changed: 19 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,13 @@
55
from ..constants import ExcCodes
66
from ..exceptions import NoSuchIdException
77
from ..logging import Log
8-
from .store import BaseModbusDataBlock
8+
from .sequential import ModbusSequentialDataBlock
9+
from .sparse import ModbusSparseDataBlock
910

1011

1112
# pylint: disable=missing-type-doc
1213

13-
class ModbusBaseDeviceContext:
14-
"""Interface for a modbus device data context."""
15-
16-
_fx_mapper = {2: "d", 4: "i"}
17-
_fx_mapper.update([(i, "h") for i in (3, 6, 16, 22, 23)])
18-
_fx_mapper.update([(i, "c") for i in (1, 5, 15)])
19-
20-
def decode(self, fx):
21-
"""Convert the function code to the datastore to.
22-
23-
:param fx: The function we are working with
24-
:returns: one of [d(iscretes),i(nputs),h(olding),c(oils)
25-
"""
26-
return self._fx_mapper.get(fx, "x")
27-
28-
async def async_getValues(self, func_code: int, address: int, count: int = 1) -> list[int] | list[bool] | ExcCodes:
29-
"""Get `count` values from datastore.
30-
31-
:param func_code: The function we are working with
32-
:param address: The starting address
33-
:param count: The number of values to retrieve
34-
:returns: The requested values from a:a+c
35-
"""
36-
Log.error("getValues({},{},{}) not implemented!", func_code, address, count)
37-
return ExcCodes.ILLEGAL_FUNCTION
38-
39-
async def async_setValues(self, func_code: int, address: int, values: list[int] | list[bool] ) -> None | ExcCodes:
40-
"""Set the datastore with the supplied values.
41-
42-
:param func_code: The function we are working with
43-
:param address: The starting address
44-
:param values: The new values to be set
45-
"""
46-
Log.error("setValues({},{},{}) not implemented!", func_code, address, values)
47-
return ExcCodes.ILLEGAL_FUNCTION
48-
49-
50-
# ---------------------------------------------------------------------------#
51-
# Device Contexts
52-
# ---------------------------------------------------------------------------#
53-
class ModbusDeviceContext(ModbusBaseDeviceContext):
14+
class ModbusDeviceContext:
5415
"""Create a modbus data model with data stored in a block.
5516
5617
:param di: discrete inputs initializer ModbusDataBlock
@@ -59,11 +20,15 @@ class ModbusDeviceContext(ModbusBaseDeviceContext):
5920
:param ir: input registers initializer ModbusDataBlock
6021
"""
6122

23+
_fx_mapper = {2: "d", 4: "i"}
24+
_fx_mapper.update([(i, "h") for i in (3, 6, 16, 22, 23)])
25+
_fx_mapper.update([(i, "c") for i in (1, 5, 15)])
26+
6227
def __init__(self, *_args,
63-
di: BaseModbusDataBlock | None = None,
64-
co: BaseModbusDataBlock | None = None,
65-
ir: BaseModbusDataBlock | None = None,
66-
hr: BaseModbusDataBlock | None = None,
28+
di: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
29+
co: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
30+
ir: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
31+
hr: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
6732
):
6833
"""Initialize the datastores."""
6934
self.store = {
@@ -73,7 +38,7 @@ def __init__(self, *_args,
7338
"h": hr,
7439
}
7540

76-
async def async_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
41+
async def async_OLD_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
7742
"""Get `count` values from datastore.
7843
7944
:param func_code: The function we are working with
@@ -83,11 +48,11 @@ async def async_getValues(self, func_code, address, count=1) -> list[int] | list
8348
"""
8449
address += 1
8550
Log.debug("getValues: fc-[{}] address-{}: count-{}", func_code, address, count)
86-
if dt := self.store[self.decode(func_code)]:
87-
return await dt.async_getValues(address, count)
51+
if dt := self.store[self._fx_mapper.get(func_code, "x")]:
52+
return await dt.async_OLD_getValues(address, count)
8853
return ExcCodes.ILLEGAL_ADDRESS
8954

90-
async def async_setValues(self, func_code, address, values) -> None | ExcCodes:
55+
async def async_OLD_setValues(self, func_code, address, values) -> None | ExcCodes:
9156
"""Set the datastore with the supplied values.
9257
9358
:param func_code: The function we are working with
@@ -96,8 +61,8 @@ async def async_setValues(self, func_code, address, values) -> None | ExcCodes:
9661
"""
9762
address += 1
9863
Log.debug("setValues[{}] address-{}: count-{}", func_code, address, len(values))
99-
if dt := self.store[self.decode(func_code)]:
100-
return await dt.async_setValues(address, values)
64+
if dt := self.store[self._fx_mapper.get(func_code, "x")]:
65+
return await dt.async_OLD_setValues(address, values)
10166
return ExcCodes.ILLEGAL_ADDRESS
10267

10368

@@ -141,7 +106,7 @@ async def async_getValues(self, device_id: int, func_code: int, address: int, co
141106
:returns: The requested values from a:a+c
142107
"""
143108
dev = self.__get_device(device_id)
144-
return await dev.async_getValues(func_code, address, count)
109+
return await dev.async_OLD_getValues(func_code, address, count)
145110

146111
async def async_setValues(self, device_id: int, func_code: int, address: int, values: list[int] | list[bool] ) -> None | ExcCodes:
147112
"""Set the datastore with the supplied values.
@@ -152,7 +117,7 @@ async def async_setValues(self, device_id: int, func_code: int, address: int, va
152117
:param values: The new values to be set
153118
"""
154119
dev = self.__get_device(device_id)
155-
return await dev.async_setValues(func_code, address, values)
120+
return await dev.async_OLD_setValues(func_code, address, values)
156121

157122
def device_ids(self):
158123
"""Get the configured device ids."""

pymodbus/datastore/sequential.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
from __future__ import annotations
44

55
from ..constants import ExcCodes
6-
from .store import BaseModbusDataBlock
76

87

9-
class ModbusSequentialDataBlock(BaseModbusDataBlock[list]):
8+
class ModbusSequentialDataBlock:
109
"""Creates a sequential modbus datastore."""
1110

1211
def __init__(self, address, values):
@@ -21,7 +20,7 @@ def __init__(self, address, values):
2120
else:
2221
self.values = [values]
2322

24-
async def async_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
23+
async def async_OLD_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
2524
"""Return the requested values of the datastore.
2625
2726
:param address: The starting address
@@ -33,7 +32,7 @@ async def async_getValues(self, address, count=1) -> list[int] | list[bool] | Ex
3332
return ExcCodes.ILLEGAL_ADDRESS
3433
return self.values[start : start + count]
3534

36-
async def async_setValues(self, address, values) -> None | ExcCodes:
35+
async def async_OLD_setValues(self, address, values) -> None | ExcCodes:
3736
"""Set the requested values of the datastore.
3837
3938
:param address: The starting address

pymodbus/datastore/simulator.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from typing import Any
1010

1111
from ..constants import ExcCodes
12-
from .context import ModbusBaseDeviceContext
1312

1413

1514
WORD_SIZE = 16
@@ -378,7 +377,7 @@ def setup(self, config, custom_actions) -> None:
378377
raise RuntimeError(f"INVALID key in setup: {self.config}")
379378

380379

381-
class ModbusSimulatorContext(ModbusBaseDeviceContext):
380+
class ModbusSimulatorContext:
382381
"""Modbus simulator.
383382
384383
:param config: A dict with structure as shown below.
@@ -587,7 +586,7 @@ def validate(self, func_code, address, count=1):
587586
fx_write = func_code in self._write_func_code
588587
return self.loop_validate(real_address, real_address + count, fx_write)
589588

590-
async def async_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
589+
async def async_OLD_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
591590
"""Return the requested values of the datastore.
592591
593592
:meta private:
@@ -624,7 +623,7 @@ async def async_getValues(self, func_code, address, count=1) -> list[int] | list
624623
bit_index = 0
625624
return result
626625

627-
async def async_setValues(self, func_code, address, values) -> None | ExcCodes:
626+
async def async_OLD_setValues(self, func_code, address, values) -> None | ExcCodes:
628627
"""Set the requested values of the datastore.
629628
630629
:meta private:

pymodbus/datastore/sparse.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,20 @@
22
# pylint: disable=missing-type-doc
33
from __future__ import annotations
44

5-
from typing import Any
6-
75
from ..constants import ExcCodes
86
from ..exceptions import ParameterException
9-
from .store import BaseModbusDataBlock
107

118

12-
class ModbusSparseDataBlock(BaseModbusDataBlock[dict[int, Any]]):
9+
class ModbusSparseDataBlock:
1310
"""A sparse modbus datastore, silently redirected to ModbusSequentialBlock."""
1411

1512
def __init__(self, values=None, mutable=True):
1613
"""Initialize a sparse datastore."""
17-
self.values = {}
14+
self.values: dict[int, list[int]] = {}
1815
self._process_values(values)
1916
self.mutable = mutable
2017

21-
async def async_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
18+
async def async_OLD_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
2219
"""Return the requested values of the datastore.
2320
2421
:param address: The starting address
@@ -29,7 +26,7 @@ async def async_getValues(self, address, count=1) -> list[int] | list[bool] | Ex
2926
values = [self.values[i] for i in range(address, address + count)]
3027
except KeyError:
3128
return ExcCodes.ILLEGAL_ADDRESS
32-
return values
29+
return values # type: ignore[return-value]
3330

3431
def _process_values(self, values):
3532
"""Process values."""
@@ -40,7 +37,7 @@ def _process_as_dict(values):
4037
for i, v_item in enumerate(val):
4138
self.values[idx + i] = v_item
4239
else:
43-
self.values[idx] = int(val)
40+
self.values[idx] = int(val) # type: ignore[assignment]
4441

4542
if isinstance(values, dict):
4643
_process_as_dict(values)
@@ -55,7 +52,7 @@ def _process_as_dict(values):
5552
)
5653
_process_as_dict(values)
5754

58-
async def async_setValues(self, address, values) -> None | ExcCodes:
55+
async def async_OLD_setValues(self, address, values) -> None | ExcCodes:
5956
"""Set the requested values of the datastore.
6057
6158
:param address: The register starting address

pymodbus/datastore/store.py

Lines changed: 0 additions & 39 deletions
This file was deleted.

pymodbus/server/base.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,28 @@ def callback_disconnected(self, exc: Exception | None) -> None:
103103
def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
104104
"""Handle received data."""
105105
raise RuntimeError("callback_data should never be called")
106+
107+
async def async_getValues(self, device_id: int, func_code: int, address: int, count: int = 1) -> list[int] | list[bool]:
108+
"""Get `count` values from datastore.
109+
110+
:param device_id: the device being addressed
111+
:param func_code: The function we are working with
112+
:param address: The starting address
113+
:param count: The number of values to retrieve
114+
:returns: The requested values from a:a+c
115+
"""
116+
res = await self.context.async_getValues(device_id, func_code, address, count)
117+
if not isinstance(res, list):
118+
raise TypeError("Illegal external call to server.async_getValues")
119+
return res
120+
121+
async def async_setValues(self, device_id: int, func_code: int, address: int, values: list[int] | list[bool] ) -> None:
122+
"""Set the datastore with the supplied values.
123+
124+
:param device_id: the device being addressed
125+
:param func_code: The function we are working with
126+
:param address: The starting address
127+
:param values: The new values to be set
128+
"""
129+
if not await self.context.async_setValues(device_id, func_code, address, values):
130+
raise TypeError("Illegal external call to server.async_setValues")

0 commit comments

Comments
 (0)