Skip to content

Commit

Permalink
Nativ plc addressing (#68)
Browse files Browse the repository at this point in the history
* add first implementation for nativ addressing

* move decoder and add test

* add new data types for input and output double words

* use better exception type and message

* add test for new memory access function
  • Loading branch information
drunsinn authored Jun 24, 2024
1 parent e58d915 commit d9fcb7f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 2 deletions.
20 changes: 19 additions & 1 deletion pyLSV2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,24 @@ def read_plc_memory(
)
return plc_values

def read_plc_address(self, address: str) -> Union[None, int, float, str]:
"""
read from plc memory using the nativ addressing scheme of the control
Requires access level ``PLCDEBUG`` to work.
:param address: address of the plc memory location in the format used by the nc like W1090, M0 or S20
:raises LSV2InputException: if unknowns memory type is requested or if the to many elements are requested
:raises LSV2DataException: if number of received values does not match the number of expected
"""

m_type, m_num = lm.decode_plc_memory_address(address)

if m_type is None or m_num is None:
raise LSV2InputException("could not translate address %s to valid memory location" % address)

return self.read_plc_memory(m_num, m_type, 1)[0]

def set_keyboard_access(self, unlocked: bool) -> bool:
"""
Enable or disable the keyboard on the control.
Expand Down Expand Up @@ -1629,7 +1647,7 @@ def get_file_list(self, path: str = "", descend: bool = True, pattern: str = "")
return []

if self.change_directory(path) is False:
self._logger.warning("could not change to directory %s" % path)
self._logger.warning("could not change to directory %s", path)
return []

if len(pattern) == 0:
Expand Down
50 changes: 49 additions & 1 deletion pyLSV2/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
# -*- coding: utf-8 -*-
"""misc helper functions for pyLSV2"""
import struct
import re
from datetime import datetime
from pathlib import Path
from typing import Union, List, Dict

from . import dat_cls as ld
from .const import BIN_FILES, PATH_SEP, ControlType
from .const import BIN_FILES, PATH_SEP, ControlType, MemoryType
from .err import LSV2DataException


Expand Down Expand Up @@ -328,3 +329,50 @@ def decode_timestamp(data_set: bytearray) -> datetime:
"""
timestamp = struct.unpack("!L", data_set[0:4])[0]
return datetime.fromtimestamp(timestamp)


def decode_plc_memory_address(address: str):
"""
Decode memory address location from the format used by the plc program to
sequential representation.
"""
val_num = None
val_type = None
if result := re.fullmatch(r"(?P<m_type>[MBWDSIO])(?P<s_type>[WD])?(?P<num>\d+)", address):
m_type = result.group("m_type")
s_type = result.group("s_type")
num = int(result.group("num"))
val_num = num

if m_type == "M" and s_type is None:
val_type = MemoryType.MARKER
elif m_type == "B" and s_type is None:
val_type = MemoryType.BYTE
elif m_type == "W" and s_type is None:
val_num = int(val_num / 2)
val_type = MemoryType.WORD
elif m_type == "D" and s_type is None:
val_num = int(val_num / 4)
val_type = MemoryType.DWORD
elif m_type == "I":
if s_type is None:
val_type = MemoryType.INPUT
elif s_type == "W":
val_num = int(val_num / 2)
val_type = MemoryType.INPUT_WORD
elif s_type == "D":
val_num = int(val_num / 4)
val_type = MemoryType.INPUT_DWORD
elif m_type == "O":
if s_type is None:
val_type = MemoryType.OUTPUT
elif s_type == "W":
val_num = int(val_num / 2)
val_type = MemoryType.OUTPUT_WORD
elif s_type == "D":
val_num = int(val_num / 4)
val_type = MemoryType.OUTPUT_DWORD
elif m_type == "S" and s_type is None:
val_type = MemoryType.STRING

return val_type, val_num
28 changes: 28 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import tempfile
from pathlib import Path
import pyLSV2
import pyLSV2.misc


def test_grab_screen_dump(address: str, timeout: float):
Expand All @@ -18,3 +19,30 @@ def test_grab_screen_dump(address: str, timeout: float):
assert local_bmp_path.stat().st_size > 1

lsv2.disconnect()


def test_plc_address_decode():
"""test the decode function for plc memory addresses"""

assert pyLSV2.misc.decode_plc_memory_address("M0001") == (pyLSV2.const.MemoryType.MARKER, 1)
assert pyLSV2.misc.decode_plc_memory_address("M0") == (pyLSV2.const.MemoryType.MARKER, 0)
assert pyLSV2.misc.decode_plc_memory_address("M1") == (pyLSV2.const.MemoryType.MARKER, 1)
assert pyLSV2.misc.decode_plc_memory_address("M1234") == (pyLSV2.const.MemoryType.MARKER, 1234)

assert pyLSV2.misc.decode_plc_memory_address("B0") == (pyLSV2.const.MemoryType.BYTE, 0)
assert pyLSV2.misc.decode_plc_memory_address("W0") == (pyLSV2.const.MemoryType.WORD, 0)
assert pyLSV2.misc.decode_plc_memory_address("D0") == (pyLSV2.const.MemoryType.DWORD, 0)

assert pyLSV2.misc.decode_plc_memory_address("B4") == (pyLSV2.const.MemoryType.BYTE, 4)
assert pyLSV2.misc.decode_plc_memory_address("W4") == (pyLSV2.const.MemoryType.WORD, 2)
assert pyLSV2.misc.decode_plc_memory_address("D4") == (pyLSV2.const.MemoryType.DWORD, 1)

assert pyLSV2.misc.decode_plc_memory_address("B16") == (pyLSV2.const.MemoryType.BYTE, 16)
assert pyLSV2.misc.decode_plc_memory_address("W16") == (pyLSV2.const.MemoryType.WORD, 8)
assert pyLSV2.misc.decode_plc_memory_address("D16") == (pyLSV2.const.MemoryType.DWORD, 4)

assert pyLSV2.misc.decode_plc_memory_address("O8") == (pyLSV2.const.MemoryType.OUTPUT, 8)
assert pyLSV2.misc.decode_plc_memory_address("OW8") == (pyLSV2.const.MemoryType.OUTPUT_WORD, 4)

assert pyLSV2.misc.decode_plc_memory_address("I8") == (pyLSV2.const.MemoryType.INPUT, 8)
assert pyLSV2.misc.decode_plc_memory_address("IW8") == (pyLSV2.const.MemoryType.INPUT_WORD, 4)
29 changes: 29 additions & 0 deletions tests/test_plc_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,32 @@ def test_comapare_values(address: str, timeout: float):
assert v1 == v2

lsv2.disconnect()


def test_plc_mem_access(address: str, timeout: float):
"""test to see if reading via plc address and plc memory returns the same value"""

lsv2 = pyLSV2.LSV2(address, port=19000, timeout=timeout, safe_mode=False)
lsv2.connect()

for mem_address in [0, 1, 2, 4, 8, 12, 68, 69, 151, 300, 420]:
v1 = lsv2.read_plc_memory(mem_address, pyLSV2.MemoryType.DWORD, 1)[0]
v2 = lsv2.read_plc_address("D%d" % (mem_address * 4))
assert v1 == v2

for mem_address in [0, 1, 2, 4, 8, 12, 68, 69, 151, 300, 420]:
v1 = lsv2.read_plc_memory(mem_address, pyLSV2.MemoryType.WORD, 1)[0]
v2 = lsv2.read_plc_address("W%d" % (mem_address * 2))
assert v1 == v2

for mem_address in [0, 1, 2, 4, 8, 12, 68, 69, 151, 300, 420]:
v1 = lsv2.read_plc_memory(mem_address, pyLSV2.MemoryType.BYTE, 1)[0]
v2 = lsv2.read_plc_address("B%d" % (mem_address * 1))
assert v1 == v2

for mem_address in [0, 1, 2, 4, 8, 12, 68, 69, 151, 300, 420]:
v1 = lsv2.read_plc_memory(mem_address, pyLSV2.MemoryType.MARKER, 1)[0]
v2 = lsv2.read_plc_address("M%d" % (mem_address * 1))
assert v1 == v2

lsv2.disconnect()

0 comments on commit d9fcb7f

Please sign in to comment.