Skip to content

Commit

Permalink
fix(plc4py): start to make the umas and modbus drivers act similialy.
Browse files Browse the repository at this point in the history
  • Loading branch information
hutcheb committed May 10, 2024
1 parent e0735e8 commit 26b333a
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 41 deletions.
42 changes: 22 additions & 20 deletions plc4py/plc4py/drivers/modbus/ModbusConnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class ModbusConnection(PlcConnection, PlcReader, PlcWriter, PlcConnectionMetaDat
:param config: Modbus configuration object
:param transport: Plc4xBaseTransport object used for the TCP connection
"""

def __init__(self, config: ModbusConfiguration, transport: Plc4xBaseTransport):
"""
Initializes a ModbusConnection object
Expand Down Expand Up @@ -150,7 +151,7 @@ def read_request_builder(self) -> ReadRequestBuilder:
"""
return DefaultReadRequestBuilder(ModbusTagBuilder)

async def execute(self, request: PlcRequest) -> PlcResponse:
def execute(self, request: PlcRequest) -> Awaitable[PlcResponse]:
"""
Executes a PlcRequest as long as it's already connected
:param request: Plc Request to execute
Expand All @@ -160,9 +161,10 @@ async def execute(self, request: PlcRequest) -> PlcResponse:
return self._default_failed_request(PlcResponseCode.NOT_CONNECTED)

if isinstance(request, PlcReadRequest):
return await self._read(request)
return self._read(request)

elif isinstance(request, PlcWriteRequest):
return await self._write(request)
return self._write(request)

return self._default_failed_request(PlcResponseCode.NOT_CONNECTED)

Expand All @@ -181,7 +183,7 @@ def _check_connection(self) -> bool:
"""
return self._device is None

async def _read(self, request: PlcReadRequest) -> PlcReadResponse:
def _read(self, request: PlcReadRequest) -> Awaitable[PlcReadResponse]:
"""
Executes a PlcReadRequest
Expand All @@ -195,24 +197,23 @@ async def _read(self, request: PlcReadRequest) -> PlcReadResponse:
:param request: PlcReadRequest to execute
:return: PlcReadResponse
"""
if self._check_connection():
# If no device is set, log an error and return a response with the NOT_CONNECTED code
logging.error("No device is set in the modbus connection!")
if self._device is None:
logging.error("No device is set in the Umas connection!")
return self._default_failed_request(PlcResponseCode.NOT_CONNECTED)

try:
# Send the read request to the device and wait for a response
logging.debug("Sending read request to Modbus Device")
response = await asyncio.wait_for(
self._device.read(request, self._transport), 5
)
# Return the response
return response
except Exception:
# If an error occurs during the execution of the read request, return a response with
# the INTERNAL_ERROR code
# TODO:- This exception is very general and probably should be replaced
return PlcReadResponse(PlcResponseCode.INTERNAL_ERROR, {})
# TODO: Insert Optimizer base on data from a browse request
async def _request(req, device) -> PlcReadResponse:
try:
response = await asyncio.wait_for(device.read(req, self._transport), 10)
return response
except Exception:
# TODO:- This exception is very general and probably should be replaced

return PlcReadResponse(PlcResponseCode.INTERNAL_ERROR, {})

logging.debug("Sending read request to UmasDevice")
future = asyncio.ensure_future(_request(request, self._device))
return future

async def _write(self, request: PlcWriteRequest) -> PlcWriteResponse:
"""
Expand Down Expand Up @@ -295,6 +296,7 @@ def __init__(self):
:param authentication: Optional PlcAuthentication instance used to authenticate the connection.
:return: An instance of PlcConnection that is connected to the PLC
"""

async def get_connection(
self, url: str, authentication: PlcAuthentication = PlcAuthentication()
) -> PlcConnection:
Expand Down
1 change: 1 addition & 0 deletions plc4py/plc4py/drivers/modbus/ModbusDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ async def read(
read_buffer,
request.tags[request.tag_names[0]].data_type,
request.tags[request.tag_names[0]].quantity,
True,
)

response_item = ResponseItem(PlcResponseCode.OK, returned_value)
Expand Down
10 changes: 7 additions & 3 deletions plc4py/plc4py/drivers/modbus/ModbusProtocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,16 @@ def packet_length_estimator(self, read_buffer: ReadBufferByteBased):
bool: True if the estimated packet length plus the current position is less than the length of the buffer, False otherwise.
"""
current_position = read_buffer.position
logging.debug(f"Current position in packet_length_estimator: {current_position}")
logging.debug(
f"Current position in packet_length_estimator: {current_position}"
)
read_buffer.position = 8 * 4
packet_length: int = read_buffer.read_unsigned_short()
logging.debug(f"Packet length in packet_length_estimator: {packet_length}")
read_buffer.position = current_position
logging.debug(f"Buffer length in packet_length_estimator: {len(read_buffer.bb)}")
logging.debug(
f"Buffer length in packet_length_estimator: {len(read_buffer.bb)}"
)
return packet_length + current_position < len(read_buffer.bb)

def data_received(self, data):
Expand Down Expand Up @@ -79,7 +83,7 @@ def data_received(self, data):
adu: ModbusTcpADU = ModbusTcpADU.static_parse_builder(
read_buffer, DriverType.MODBUS_TCP, True
).build(True)
#logging.debug(f"Received ADU: {adu}")
# logging.debug(f"Received ADU: {adu}")
# If the ADU transaction identifier is in the messages dictionary,
# set the PDU (data) as a result of the associated Future object.
if adu.transaction_identifier in self.messages:
Expand Down
8 changes: 1 addition & 7 deletions plc4py/plc4py/protocols/modbus/readwrite/ModbusTcpADU.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ def static_parse_builder(
read_buffer: ReadBuffer, driver_type: DriverType, response: bool
):
read_buffer.push_context("ModbusTcpADU")
print(f"Entering ModbusTcpADU static_parse_builder()")

transaction_identifier: int = read_buffer.read_unsigned_short(
logical_name="transaction_identifier",
Expand All @@ -110,23 +109,20 @@ def static_parse_builder(
driver_type=driver_type,
response=response,
)
print(f"transaction_identifier: 0x{transaction_identifier:04x}")

PROTOCOL_IDENTIFIER: int = read_buffer.read_unsigned_short(
logical_name="protocol_identifier",
byte_order=ByteOrder.BIG_ENDIAN,
driver_type=driver_type,
response=response,
)
print(f"PROTOCOL_IDENTIFIER: 0x{PROTOCOL_IDENTIFIER:04x}")

length: int = read_buffer.read_unsigned_short(
logical_name="length",
byte_order=ByteOrder.BIG_ENDIAN,
driver_type=driver_type,
response=response,
)
print(f"length: {length}")

unit_identifier: int = read_buffer.read_unsigned_byte(
logical_name="unit_identifier",
Expand All @@ -135,7 +131,6 @@ def static_parse_builder(
driver_type=driver_type,
response=response,
)
print(f"unit_identifier: 0x{unit_identifier:02x}")

pdu: ModbusPDU = read_buffer.read_complex(
read_function=ModbusPDU.static_parse,
Expand All @@ -144,9 +139,8 @@ def static_parse_builder(
driver_type=driver_type,
response=response,
)

read_buffer.pop_context("ModbusTcpADU")
print(f"Exiting ModbusTcpADU static_parse_builder()")
# Create the instance
return ModbusTcpADUBuilder(transaction_identifier, unit_identifier, pdu)

Expand Down
18 changes: 8 additions & 10 deletions plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,19 @@ async def manual_test_plc_driver_modbus_connect():


@pytest.mark.asyncio
async def manual_test_plc_driver_modbus_read():
async def test_plc_driver_modbus_read():
log = logging.getLogger(__name__)

connection_string = "modbus://127.0.0.1:5020"
driver_manager = PlcDriverManager()
response = None

async def communicate_with_plc():
async with driver_manager.connection(connection_string) as connection:
with connection.read_request_builder() as builder:
builder.add_item("Random Tag", "4x00001[10]")
request = builder.build()
async with driver_manager.connection("modbus://192.168.1.177:502") as connection:
with connection.read_request_builder() as builder:
builder.add_item("Random Tag", "4x00001[10]")
request = builder.build()

future = connection.execute(request)
await future
response = future.result()
value = response.tags["Random Tag"].value
log.error(f"Read tag 4x00001[10] - {value}")

communicate_with_plc()
pass
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async def manual_test_plc_driver_umas_connect():


@pytest.mark.asyncio
async def manual_test_plc_driver_umas_read():
async def test_plc_driver_umas_read():
log = logging.getLogger(__name__)

driver_manager = PlcDriverManager()
Expand Down

0 comments on commit 26b333a

Please sign in to comment.