Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion API_changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ API changes 3.13.0
------------------
- removed RemoteDeviceContext, because it only is a partial forwarder
a proper forwarder should be made at frame level.
- datastore get/setValues only exist as async_get/set
- datastore get/setValues is removed,
please use server.async_get/setValues instead.

API changes 3.12.0
------------------
Expand Down
Binary file modified doc/source/_static/examples.tgz
Binary file not shown.
Binary file modified doc/source/_static/examples.zip
Binary file not shown.
2 changes: 0 additions & 2 deletions pymodbus/datastore/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Datastore."""

__all__ = [
"ModbusBaseDeviceContext",
"ModbusDeviceContext",
"ModbusSequentialDataBlock",
"ModbusServerContext",
Expand All @@ -10,7 +9,6 @@
]

from .context import (
ModbusBaseDeviceContext,
ModbusDeviceContext,
ModbusServerContext,
)
Expand Down
73 changes: 19 additions & 54 deletions pymodbus/datastore/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,13 @@
from ..constants import ExcCodes
from ..exceptions import NoSuchIdException
from ..logging import Log
from .store import BaseModbusDataBlock
from .sequential import ModbusSequentialDataBlock
from .sparse import ModbusSparseDataBlock


# pylint: disable=missing-type-doc

class ModbusBaseDeviceContext:
"""Interface for a modbus device data context."""

_fx_mapper = {2: "d", 4: "i"}
_fx_mapper.update([(i, "h") for i in (3, 6, 16, 22, 23)])
_fx_mapper.update([(i, "c") for i in (1, 5, 15)])

def decode(self, fx):
"""Convert the function code to the datastore to.

:param fx: The function we are working with
:returns: one of [d(iscretes),i(nputs),h(olding),c(oils)
"""
return self._fx_mapper.get(fx, "x")

async def async_getValues(self, func_code: int, address: int, count: int = 1) -> list[int] | list[bool] | ExcCodes:
"""Get `count` values from datastore.

:param func_code: The function we are working with
:param address: The starting address
:param count: The number of values to retrieve
:returns: The requested values from a:a+c
"""
Log.error("getValues({},{},{}) not implemented!", func_code, address, count)
return ExcCodes.ILLEGAL_FUNCTION

async def async_setValues(self, func_code: int, address: int, values: list[int] | list[bool] ) -> None | ExcCodes:
"""Set the datastore with the supplied values.

:param func_code: The function we are working with
:param address: The starting address
:param values: The new values to be set
"""
Log.error("setValues({},{},{}) not implemented!", func_code, address, values)
return ExcCodes.ILLEGAL_FUNCTION


# ---------------------------------------------------------------------------#
# Device Contexts
# ---------------------------------------------------------------------------#
class ModbusDeviceContext(ModbusBaseDeviceContext):
class ModbusDeviceContext:
"""Create a modbus data model with data stored in a block.

:param di: discrete inputs initializer ModbusDataBlock
Expand All @@ -59,11 +20,15 @@ class ModbusDeviceContext(ModbusBaseDeviceContext):
:param ir: input registers initializer ModbusDataBlock
"""

_fx_mapper = {2: "d", 4: "i"}
_fx_mapper.update([(i, "h") for i in (3, 6, 16, 22, 23)])
_fx_mapper.update([(i, "c") for i in (1, 5, 15)])

def __init__(self, *_args,
di: BaseModbusDataBlock | None = None,
co: BaseModbusDataBlock | None = None,
ir: BaseModbusDataBlock | None = None,
hr: BaseModbusDataBlock | None = None,
di: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
co: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
ir: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
hr: ModbusSequentialDataBlock | ModbusSparseDataBlock | None = None,
):
"""Initialize the datastores."""
self.store = {
Expand All @@ -73,7 +38,7 @@ def __init__(self, *_args,
"h": hr,
}

async def async_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
async def async_OLD_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
"""Get `count` values from datastore.

:param func_code: The function we are working with
Expand All @@ -83,11 +48,11 @@ async def async_getValues(self, func_code, address, count=1) -> list[int] | list
"""
address += 1
Log.debug("getValues: fc-[{}] address-{}: count-{}", func_code, address, count)
if dt := self.store[self.decode(func_code)]:
return await dt.async_getValues(address, count)
if dt := self.store[self._fx_mapper.get(func_code, "x")]:
return await dt.async_OLD_getValues(address, count)
return ExcCodes.ILLEGAL_ADDRESS

async def async_setValues(self, func_code, address, values) -> None | ExcCodes:
async def async_OLD_setValues(self, func_code, address, values) -> None | ExcCodes:
"""Set the datastore with the supplied values.

:param func_code: The function we are working with
Expand All @@ -96,8 +61,8 @@ async def async_setValues(self, func_code, address, values) -> None | ExcCodes:
"""
address += 1
Log.debug("setValues[{}] address-{}: count-{}", func_code, address, len(values))
if dt := self.store[self.decode(func_code)]:
return await dt.async_setValues(address, values)
if dt := self.store[self._fx_mapper.get(func_code, "x")]:
return await dt.async_OLD_setValues(address, values)
return ExcCodes.ILLEGAL_ADDRESS


Expand Down Expand Up @@ -141,7 +106,7 @@ async def async_getValues(self, device_id: int, func_code: int, address: int, co
:returns: The requested values from a:a+c
"""
dev = self.__get_device(device_id)
return await dev.async_getValues(func_code, address, count)
return await dev.async_OLD_getValues(func_code, address, count)

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

def device_ids(self):
"""Get the configured device ids."""
Expand Down
7 changes: 3 additions & 4 deletions pymodbus/datastore/sequential.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from __future__ import annotations

from ..constants import ExcCodes
from .store import BaseModbusDataBlock


class ModbusSequentialDataBlock(BaseModbusDataBlock[list]):
class ModbusSequentialDataBlock:
"""Creates a sequential modbus datastore."""

def __init__(self, address, values):
Expand All @@ -21,7 +20,7 @@ def __init__(self, address, values):
else:
self.values = [values]

async def async_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
async def async_OLD_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
"""Return the requested values of the datastore.

:param address: The starting address
Expand All @@ -33,7 +32,7 @@ async def async_getValues(self, address, count=1) -> list[int] | list[bool] | Ex
return ExcCodes.ILLEGAL_ADDRESS
return self.values[start : start + count]

async def async_setValues(self, address, values) -> None | ExcCodes:
async def async_OLD_setValues(self, address, values) -> None | ExcCodes:
"""Set the requested values of the datastore.

:param address: The starting address
Expand Down
7 changes: 3 additions & 4 deletions pymodbus/datastore/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from typing import Any

from ..constants import ExcCodes
from .context import ModbusBaseDeviceContext


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


class ModbusSimulatorContext(ModbusBaseDeviceContext):
class ModbusSimulatorContext:
"""Modbus simulator.

:param config: A dict with structure as shown below.
Expand Down Expand Up @@ -587,7 +586,7 @@ def validate(self, func_code, address, count=1):
fx_write = func_code in self._write_func_code
return self.loop_validate(real_address, real_address + count, fx_write)

async def async_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
async def async_OLD_getValues(self, func_code, address, count=1) -> list[int] | list[bool] | ExcCodes:
"""Return the requested values of the datastore.

:meta private:
Expand Down Expand Up @@ -624,7 +623,7 @@ async def async_getValues(self, func_code, address, count=1) -> list[int] | list
bit_index = 0
return result

async def async_setValues(self, func_code, address, values) -> None | ExcCodes:
async def async_OLD_setValues(self, func_code, address, values) -> None | ExcCodes:
"""Set the requested values of the datastore.

:meta private:
Expand Down
15 changes: 6 additions & 9 deletions pymodbus/datastore/sparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@
# pylint: disable=missing-type-doc
from __future__ import annotations

from typing import Any

from ..constants import ExcCodes
from ..exceptions import ParameterException
from .store import BaseModbusDataBlock


class ModbusSparseDataBlock(BaseModbusDataBlock[dict[int, Any]]):
class ModbusSparseDataBlock:
"""A sparse modbus datastore, silently redirected to ModbusSequentialBlock."""

def __init__(self, values=None, mutable=True):
"""Initialize a sparse datastore."""
self.values = {}
self.values: dict[int, list[int]] = {}
self._process_values(values)
self.mutable = mutable

async def async_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
async def async_OLD_getValues(self, address, count=1) -> list[int] | list[bool] | ExcCodes:
"""Return the requested values of the datastore.

:param address: The starting address
Expand All @@ -29,7 +26,7 @@ async def async_getValues(self, address, count=1) -> list[int] | list[bool] | Ex
values = [self.values[i] for i in range(address, address + count)]
except KeyError:
return ExcCodes.ILLEGAL_ADDRESS
return values
return values # type: ignore[return-value]

def _process_values(self, values):
"""Process values."""
Expand All @@ -40,7 +37,7 @@ def _process_as_dict(values):
for i, v_item in enumerate(val):
self.values[idx + i] = v_item
else:
self.values[idx] = int(val)
self.values[idx] = int(val) # type: ignore[assignment]

if isinstance(values, dict):
_process_as_dict(values)
Expand All @@ -55,7 +52,7 @@ def _process_as_dict(values):
)
_process_as_dict(values)

async def async_setValues(self, address, values) -> None | ExcCodes:
async def async_OLD_setValues(self, address, values) -> None | ExcCodes:
"""Set the requested values of the datastore.

:param address: The register starting address
Expand Down
39 changes: 0 additions & 39 deletions pymodbus/datastore/store.py

This file was deleted.

25 changes: 25 additions & 0 deletions pymodbus/server/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,28 @@ def callback_disconnected(self, exc: Exception | None) -> None:
def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
"""Handle received data."""
raise RuntimeError("callback_data should never be called")

async def async_getValues(self, device_id: int, func_code: int, address: int, count: int = 1) -> list[int] | list[bool]:
"""Get `count` values from datastore.

:param device_id: the device being addressed
:param func_code: The function we are working with
:param address: The starting address
:param count: The number of values to retrieve
:returns: The requested values from a:a+c
"""
res = await self.context.async_getValues(device_id, func_code, address, count)
if not isinstance(res, list):
raise TypeError("Illegal external call to server.async_getValues")
return res

async def async_setValues(self, device_id: int, func_code: int, address: int, values: list[int] | list[bool] ) -> None:
"""Set the datastore with the supplied values.

:param device_id: the device being addressed
:param func_code: The function we are working with
:param address: The starting address
:param values: The new values to be set
"""
if await self.context.async_setValues(device_id, func_code, address, values):
raise TypeError("Illegal external call to server.async_setValues")
6 changes: 3 additions & 3 deletions pymodbus/simulator/simcore.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""
from __future__ import annotations

from ..pdu import ExceptionResponse
from ..constants import ExcCodes
from .simdevice import SimDevice
from .simruntime import SimRuntime

Expand All @@ -30,10 +30,10 @@ def __get_device(self, device_id: int) -> SimRuntime:
"""Return device object."""
return self.devices[device_id] if device_id in self.devices else self.devices[0]

async def async_getValues(self,device_id: int, func_code: int, address: int, count: int = 1) -> list[int] | ExceptionResponse:
async def async_getValues(self,device_id: int, func_code: int, address: int, count: int = 1) -> list[int] | list[bool] | ExcCodes:
"""Get `count` values from datastore."""
return await self.__get_device(device_id).async_getValues(func_code, address, count)

async def async_setValues(self, device_id: int, func_code: int, address: int, values: list[int] ) -> None | ExceptionResponse:
async def async_setValues(self, device_id: int, func_code: int, address: int, values: list[int] | list[bool]) -> None | ExcCodes:
"""Set the datastore with the supplied values."""
return await self.__get_device(device_id).async_setValues(func_code, address, values)
Loading