From 20071f7b944b7bf5ad94599900ff1c46883da317 Mon Sep 17 00:00:00 2001 From: Ben Hutcheson Date: Mon, 8 Jul 2024 20:20:27 +0200 Subject: [PATCH] feat: Improve the structure of the model to more closely match am L5X file. --- acd/generated/comps/rx_generic.py | 22 ++++ acd/l5x/elements.py | 149 ++++++++++++++++++++++-- resources/templates/Comps/RxGeneric.ksy | 6 + setup.py | 2 +- test/test_api.py | 2 +- test/test_database.py | 4 +- 6 files changed, 169 insertions(+), 16 deletions(-) diff --git a/acd/generated/comps/rx_generic.py b/acd/generated/comps/rx_generic.py index 0ce0226..13a3c14 100644 --- a/acd/generated/comps/rx_generic.py +++ b/acd/generated/comps/rx_generic.py @@ -159,6 +159,17 @@ def cip_data_type(self): self._io.seek(_pos) return getattr(self, '_m_cip_data_type', None) + @property + def radix(self): + if hasattr(self, '_m_radix'): + return self._m_radix + + _pos = self._io.pos() + self._io.seek(32) + self._m_radix = self._io.read_u2le() + self._io.seek(_pos) + return getattr(self, '_m_radix', None) + @property def data_type(self): if hasattr(self, '_m_data_type'): @@ -200,6 +211,17 @@ def valid(self): self._m_valid = True return getattr(self, '_m_valid', None) + @property + def external_access(self): + if hasattr(self, '_m_external_access'): + return self._m_external_access + + _pos = self._io.pos() + self._io.seek(34) + self._m_external_access = self._io.read_u2le() + self._io.seek(_pos) + return getattr(self, '_m_external_access', None) + @property def dimension_1(self): if hasattr(self, '_m_dimension_1'): diff --git a/acd/l5x/elements.py b/acd/l5x/elements.py index 229cbda..4a15178 100644 --- a/acd/l5x/elements.py +++ b/acd/l5x/elements.py @@ -1,7 +1,8 @@ import os import shutil import struct -from dataclasses import dataclass +from dataclasses import dataclass, field +from enum import Enum from os import PathLike from pathlib import Path from sqlite3 import Cursor @@ -38,7 +39,7 @@ def to_xml(self): if isinstance(attribute_value, L5xElement): child_list.append(attribute_value.to_xml()) elif isinstance(attribute_value, list): - if attribute == "tags": + if attribute == "tags" or attribute == "data_types" or attribute == "members": new_child_list: List[str] = [] for element in attribute_value: if isinstance(element, L5xElement): @@ -48,16 +49,30 @@ def to_xml(self): child_list.append(f'<{attribute.title().replace("_", "")}>{" ".join(new_child_list)}') else: + if attribute == "cls": + attribute = "class" attribute_list.append(f'{attribute.title().replace("_", "")}="{attribute_value}"') _export_name = self.__class__.__name__.title().replace("_", "") return f'<{_export_name} {" ".join(attribute_list)}>{" ".join(child_list)}' +@dataclass +class Member(L5xElement): + name: str + data_type: str + dimension: int + radix: str + hidden: bool + external_access: str + + @dataclass class DataType(L5xElement): name: str - children: List[str] + family: str + cls: str + members: List[Member] @dataclass @@ -131,6 +146,85 @@ def __post_init__(self): self._name = "RSLogix5000Content" +def radix_enum(i: int) -> str: + if i == 0: + return "NullType" + if i == 1: + return "General" + if i == 2: + return "Binary" + if i == 3: + return "Octal" + if i == 4: + return "Decimal" + if i == 5: + return "Hex" + if i == 6: + return "Exponential" + if i == 7: + return "Float" + if i == 8: + return "ASCII" + if i == 9: + return "Unicode" + if i == 10: + return "Date/Time" + if i == 11: + return "Date/Time (ns)" + if i == 12: + return "UseTypeStyle" + return "General" + + +def external_access_enum(i: int) -> str: + if i == 0: + return "Read/Write" + if i == 1: + return "Read Only" + if i == 2: + return "None" + return "Read/Write" + +@dataclass +class MemberBuilder(L5xElementBuilder): + record: List[int] = field(default_factory=[]) + + def build(self) -> Member: + self._cur.execute( + "SELECT comp_name, object_id, parent_id, record FROM comps WHERE object_id=" + str( + self._object_id)) + results = self._cur.fetchall() + + name = results[0][0] + r = RxGeneric.from_bytes(results[0][3]) + try: + r = RxGeneric.from_bytes(results[0][3]) + except Exception as e: + return Member(name, name, "", 0, "Decimal", False, "Read/Write") + + extended_records: Dict[int, List[int]] = {} + for extended_record in r.extended_records: + extended_records[extended_record.attribute_id] = extended_record.value + extended_records[r.last_extended_record.attribute_id] = r.last_extended_record.value + + cip_data_typoe = struct.unpack_from("