DLMS-SPODES 0.87.13__py3-none-any.whl → 0.87.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- DLMS_SPODES/Values/EN/__init__.py +1 -1
- DLMS_SPODES/Values/EN/actors.py +8 -8
- DLMS_SPODES/Values/EN/relation_to_obis_names.py +387 -387
- DLMS_SPODES/Values/RU/__init__.py +1 -1
- DLMS_SPODES/Values/RU/actors.py +8 -8
- DLMS_SPODES/Values/RU/relation_to_obis_names.py +396 -396
- DLMS_SPODES/__init__.py +6 -6
- DLMS_SPODES/configEN.ini +126 -126
- DLMS_SPODES/config_parser.py +53 -53
- DLMS_SPODES/cosem_interface_classes/__class_init__.py +3 -3
- DLMS_SPODES/cosem_interface_classes/__init__.py +1 -1
- DLMS_SPODES/cosem_interface_classes/a_parameter.py +20 -20
- DLMS_SPODES/cosem_interface_classes/activity_calendar.py +254 -254
- DLMS_SPODES/cosem_interface_classes/arbitrator.py +105 -105
- DLMS_SPODES/cosem_interface_classes/association_ln/abstract.py +34 -34
- DLMS_SPODES/cosem_interface_classes/association_ln/authentication_mechanism_name.py +25 -25
- DLMS_SPODES/cosem_interface_classes/association_ln/mechanism_id.py +25 -25
- DLMS_SPODES/cosem_interface_classes/association_ln/method.py +5 -5
- DLMS_SPODES/cosem_interface_classes/association_ln/ver0.py +485 -485
- DLMS_SPODES/cosem_interface_classes/association_ln/ver1.py +133 -133
- DLMS_SPODES/cosem_interface_classes/association_ln/ver2.py +36 -36
- DLMS_SPODES/cosem_interface_classes/association_ln/ver3.py +4 -4
- DLMS_SPODES/cosem_interface_classes/association_sn/ver0.py +12 -12
- DLMS_SPODES/cosem_interface_classes/attr_indexes.py +12 -12
- DLMS_SPODES/cosem_interface_classes/clock.py +131 -131
- DLMS_SPODES/cosem_interface_classes/collection.py +2122 -2122
- DLMS_SPODES/cosem_interface_classes/cosem_interface_class.py +583 -583
- DLMS_SPODES/cosem_interface_classes/data.py +21 -21
- DLMS_SPODES/cosem_interface_classes/demand_register/ver0.py +59 -59
- DLMS_SPODES/cosem_interface_classes/disconnect_control.py +74 -74
- DLMS_SPODES/cosem_interface_classes/extended_register.py +27 -27
- DLMS_SPODES/cosem_interface_classes/gprs_modem_setup.py +43 -43
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver0.py +103 -103
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver1.py +40 -40
- DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver2.py +9 -9
- DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver0.py +11 -11
- DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver1.py +53 -53
- DLMS_SPODES/cosem_interface_classes/iec_local_port_setup.py +11 -11
- DLMS_SPODES/cosem_interface_classes/image_transfer/image_transfer_status.py +15 -15
- DLMS_SPODES/cosem_interface_classes/image_transfer/ver0.py +126 -126
- DLMS_SPODES/cosem_interface_classes/implementations/__init__.py +3 -3
- DLMS_SPODES/cosem_interface_classes/implementations/arbitrator.py +19 -19
- DLMS_SPODES/cosem_interface_classes/implementations/data.py +487 -487
- DLMS_SPODES/cosem_interface_classes/implementations/profile_generic.py +83 -83
- DLMS_SPODES/cosem_interface_classes/ipv4_setup.py +72 -72
- DLMS_SPODES/cosem_interface_classes/limiter.py +111 -111
- DLMS_SPODES/cosem_interface_classes/ln_pattern.py +333 -333
- DLMS_SPODES/cosem_interface_classes/modem_configuration/ver0.py +65 -65
- DLMS_SPODES/cosem_interface_classes/modem_configuration/ver1.py +39 -39
- DLMS_SPODES/cosem_interface_classes/ntp_setup/ver0.py +67 -67
- DLMS_SPODES/cosem_interface_classes/obis.py +23 -23
- DLMS_SPODES/cosem_interface_classes/overview.py +197 -197
- DLMS_SPODES/cosem_interface_classes/parameter.py +547 -547
- DLMS_SPODES/cosem_interface_classes/parameters.py +172 -172
- DLMS_SPODES/cosem_interface_classes/profile_generic/ver0.py +122 -122
- DLMS_SPODES/cosem_interface_classes/profile_generic/ver1.py +277 -277
- DLMS_SPODES/cosem_interface_classes/push_setup/ver0.py +12 -12
- DLMS_SPODES/cosem_interface_classes/push_setup/ver1.py +10 -10
- DLMS_SPODES/cosem_interface_classes/push_setup/ver2.py +166 -166
- DLMS_SPODES/cosem_interface_classes/register.py +45 -45
- DLMS_SPODES/cosem_interface_classes/register_activation/ver0.py +80 -80
- DLMS_SPODES/cosem_interface_classes/register_monitor.py +46 -46
- DLMS_SPODES/cosem_interface_classes/reports.py +70 -70
- DLMS_SPODES/cosem_interface_classes/schedule.py +176 -176
- DLMS_SPODES/cosem_interface_classes/script_table.py +87 -87
- DLMS_SPODES/cosem_interface_classes/security_setup/ver0.py +68 -68
- DLMS_SPODES/cosem_interface_classes/security_setup/ver1.py +158 -158
- DLMS_SPODES/cosem_interface_classes/single_action_schedule.py +50 -50
- DLMS_SPODES/cosem_interface_classes/special_days_table.py +84 -84
- DLMS_SPODES/cosem_interface_classes/tcp_udp_setup.py +42 -42
- DLMS_SPODES/cosem_pdu.py +93 -93
- DLMS_SPODES/enums.py +625 -625
- DLMS_SPODES/exceptions.py +106 -106
- DLMS_SPODES/firmwares.py +99 -99
- DLMS_SPODES/hdlc/frame.py +875 -875
- DLMS_SPODES/hdlc/sub_layer.py +54 -54
- DLMS_SPODES/literals.py +17 -17
- DLMS_SPODES/obis/__init__.py +1 -1
- DLMS_SPODES/obis/media_id.py +931 -931
- DLMS_SPODES/pardata.py +22 -22
- DLMS_SPODES/pdu_enums.py +98 -98
- DLMS_SPODES/relation_to_OBIS.py +465 -463
- DLMS_SPODES/settings.py +551 -551
- DLMS_SPODES/types/choices.py +142 -142
- DLMS_SPODES/types/common_data_types.py +2401 -2401
- DLMS_SPODES/types/cosem_service_types.py +109 -109
- DLMS_SPODES/types/implementations/arrays.py +25 -25
- DLMS_SPODES/types/implementations/bitstrings.py +97 -97
- DLMS_SPODES/types/implementations/double_long_usingneds.py +35 -35
- DLMS_SPODES/types/implementations/enums.py +57 -57
- DLMS_SPODES/types/implementations/integers.py +11 -11
- DLMS_SPODES/types/implementations/long_unsigneds.py +127 -127
- DLMS_SPODES/types/implementations/octet_string.py +11 -11
- DLMS_SPODES/types/implementations/structs.py +64 -64
- DLMS_SPODES/types/useful_types.py +677 -677
- {dlms_spodes-0.87.13.dist-info → dlms_spodes-0.87.16.dist-info}/METADATA +30 -30
- dlms_spodes-0.87.16.dist-info/RECORD +117 -0
- {dlms_spodes-0.87.13.dist-info → dlms_spodes-0.87.16.dist-info}/WHEEL +1 -1
- dlms_spodes-0.87.13.dist-info/RECORD +0 -117
- {dlms_spodes-0.87.13.dist-info → dlms_spodes-0.87.16.dist-info}/top_level.txt +0 -0
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
from typing import Self
|
|
2
|
-
import re
|
|
3
|
-
from ..types import common_data_types as cdt
|
|
4
|
-
import datetime
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class LogicalName(cdt.ReportMixin, cdt.OctetString, size=6):
|
|
8
|
-
""" Logical Name type. Default is CLock#1 """
|
|
9
|
-
__pattern = re.compile("(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})")
|
|
10
|
-
__match_args__ = ('a', 'b', 'c', 'd', 'e', 'f')
|
|
11
|
-
DEFAULT = b'\x00\x00\x01\x00\x00\xff'
|
|
12
|
-
|
|
13
|
-
@classmethod
|
|
14
|
-
def from_obis(cls, value: str) -> Self:
|
|
15
|
-
""" create logical_name: octet_string from string type ddd.ddd.ddd.ddd.ddd.ddd, ex.: 0.0.1.0.0.255 """
|
|
16
|
-
if (res := cls.__pattern.search(value)) is None:
|
|
17
|
-
raise ValueError(F"got wrong obis: {value}")
|
|
18
|
-
else:
|
|
19
|
-
return cls(bytearray(map(int, res.groups())))
|
|
20
|
-
|
|
21
|
-
def get_report(self) -> cdt.Report:
|
|
22
|
-
return cdt.Report('.'.join(map(str, self.contents))) # todo: add error handle
|
|
23
|
-
|
|
24
|
-
def validate_from(self, value: str, cursor_position=None) -> tuple[str, int]:
|
|
25
|
-
try:
|
|
26
|
-
possible = type(self)(value)
|
|
27
|
-
return value, cursor_position # TODO: wrong position ???
|
|
28
|
-
except ValueError:
|
|
29
|
-
with_separator = F'{value[:-1]}.{value[-1]}'
|
|
30
|
-
type(self)(with_separator) # check possible
|
|
31
|
-
return with_separator, cursor_position
|
|
32
|
-
|
|
33
|
-
def __hash__(self) -> int:
|
|
34
|
-
return int.from_bytes(self.contents, 'big')
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def a(self) -> int:
|
|
38
|
-
""" group A """
|
|
39
|
-
return self.contents[0]
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def b(self) -> int:
|
|
43
|
-
""" group B """
|
|
44
|
-
return self.contents[1]
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def c(self) -> int:
|
|
48
|
-
""" group C """
|
|
49
|
-
return self.contents[2]
|
|
50
|
-
|
|
51
|
-
@property
|
|
52
|
-
def d(self) -> int:
|
|
53
|
-
""" group D """
|
|
54
|
-
return self.contents[3]
|
|
55
|
-
|
|
56
|
-
@property
|
|
57
|
-
def e(self) -> int:
|
|
58
|
-
""" group E """
|
|
59
|
-
return self.contents[4]
|
|
60
|
-
|
|
61
|
-
@property
|
|
62
|
-
def f(self) -> int:
|
|
63
|
-
""" group F """
|
|
64
|
-
return self.contents[5]
|
|
65
|
-
|
|
66
|
-
def __lt__(self, other: Self) -> bool:
|
|
67
|
-
return self.contents < other.contents
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class OctetStringDateTime(cdt.DateTime, tag=9, size=12):
|
|
71
|
-
""" type Time in OctetString(SIZE(12)) """
|
|
72
|
-
|
|
73
|
-
def __init__(self, value: bytes | bytearray | str | int | datetime.datetime | datetime.date | datetime.time = b'\x09\x0c\x07\xe4\x01\x01\xff\xff\xff\xff\xff\x80\x00\xff'):
|
|
74
|
-
match value: # TODO: common for all OctetDateTimes
|
|
75
|
-
case bytes() if value[1] == len(self): super().__init__(self.TAG+value[2:])
|
|
76
|
-
case bytes(): raise ValueError(F'in create {self.__class__.__name__} got tag, size: {cdt.TAG(value[0])} {value[1]}, expected {self.TAG} {len(self)}')
|
|
77
|
-
case _: super().__init__(value)
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def encoding(self) -> bytes:
|
|
81
|
-
return b'\x09\x0c' + self.contents
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class OctetStringDate(cdt.Date, tag=9, size=5):
|
|
85
|
-
""" type Time in OctetString(SIZE(5)) """
|
|
86
|
-
|
|
87
|
-
def __init__(self, value: bytes | bytearray | str | int | datetime.datetime | datetime.date = b'\x09\x05\x07\xe4\x01\x01\xff'):
|
|
88
|
-
match value: # TODO: replace priority case
|
|
89
|
-
case bytes() if value[1] == len(self): super().__init__(self.TAG+value[2:])
|
|
90
|
-
case bytes(): raise ValueError(F'in create {self.__class__.__name__} got tag, size: {cdt.TAG(value[0])} {value[1]}, expected {self.TAG} {len(self)}')
|
|
91
|
-
case _: super().__init__(value)
|
|
92
|
-
|
|
93
|
-
@property
|
|
94
|
-
def encoding(self) -> bytes:
|
|
95
|
-
return b'\x09\x05' + self.contents
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
class OctetStringTime(cdt.Time, tag=9, size=4):
|
|
99
|
-
""" type Time in OctetString(SIZE(4)) """
|
|
100
|
-
|
|
101
|
-
def __init__(self, value: bytes | bytearray | str | int | datetime.datetime | datetime.time = b'\x09\x04\x00\x00\x00\x00'):
|
|
102
|
-
match value: # TODO: replace priority case
|
|
103
|
-
case bytes() if value[1] == len(self): super().__init__(self.TAG+value[2:])
|
|
104
|
-
case bytes(): raise ValueError(F'in create {self.__class__.__name__} got tag, size: {cdt.TAG(value[0])} {value[1]}, expected {self.TAG} {len(self)}')
|
|
105
|
-
case _: super().__init__(value)
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def encoding(self) -> bytes:
|
|
109
|
-
return b'\x09\x04' + self.contents
|
|
1
|
+
from typing import Self
|
|
2
|
+
import re
|
|
3
|
+
from ..types import common_data_types as cdt
|
|
4
|
+
import datetime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LogicalName(cdt.ReportMixin, cdt.OctetString, size=6):
|
|
8
|
+
""" Logical Name type. Default is CLock#1 """
|
|
9
|
+
__pattern = re.compile("(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})")
|
|
10
|
+
__match_args__ = ('a', 'b', 'c', 'd', 'e', 'f')
|
|
11
|
+
DEFAULT = b'\x00\x00\x01\x00\x00\xff'
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def from_obis(cls, value: str) -> Self:
|
|
15
|
+
""" create logical_name: octet_string from string type ddd.ddd.ddd.ddd.ddd.ddd, ex.: 0.0.1.0.0.255 """
|
|
16
|
+
if (res := cls.__pattern.search(value)) is None:
|
|
17
|
+
raise ValueError(F"got wrong obis: {value}")
|
|
18
|
+
else:
|
|
19
|
+
return cls(bytearray(map(int, res.groups())))
|
|
20
|
+
|
|
21
|
+
def get_report(self) -> cdt.Report:
|
|
22
|
+
return cdt.Report('.'.join(map(str, self.contents))) # todo: add error handle
|
|
23
|
+
|
|
24
|
+
def validate_from(self, value: str, cursor_position=None) -> tuple[str, int]:
|
|
25
|
+
try:
|
|
26
|
+
possible = type(self)(value)
|
|
27
|
+
return value, cursor_position # TODO: wrong position ???
|
|
28
|
+
except ValueError:
|
|
29
|
+
with_separator = F'{value[:-1]}.{value[-1]}'
|
|
30
|
+
type(self)(with_separator) # check possible
|
|
31
|
+
return with_separator, cursor_position
|
|
32
|
+
|
|
33
|
+
def __hash__(self) -> int:
|
|
34
|
+
return int.from_bytes(self.contents, 'big')
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def a(self) -> int:
|
|
38
|
+
""" group A """
|
|
39
|
+
return self.contents[0]
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def b(self) -> int:
|
|
43
|
+
""" group B """
|
|
44
|
+
return self.contents[1]
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def c(self) -> int:
|
|
48
|
+
""" group C """
|
|
49
|
+
return self.contents[2]
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def d(self) -> int:
|
|
53
|
+
""" group D """
|
|
54
|
+
return self.contents[3]
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def e(self) -> int:
|
|
58
|
+
""" group E """
|
|
59
|
+
return self.contents[4]
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def f(self) -> int:
|
|
63
|
+
""" group F """
|
|
64
|
+
return self.contents[5]
|
|
65
|
+
|
|
66
|
+
def __lt__(self, other: Self) -> bool:
|
|
67
|
+
return self.contents < other.contents
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class OctetStringDateTime(cdt.DateTime, tag=9, size=12):
|
|
71
|
+
""" type Time in OctetString(SIZE(12)) """
|
|
72
|
+
|
|
73
|
+
def __init__(self, value: bytes | bytearray | str | int | datetime.datetime | datetime.date | datetime.time = b'\x09\x0c\x07\xe4\x01\x01\xff\xff\xff\xff\xff\x80\x00\xff'):
|
|
74
|
+
match value: # TODO: common for all OctetDateTimes
|
|
75
|
+
case bytes() if value[1] == len(self): super().__init__(self.TAG+value[2:])
|
|
76
|
+
case bytes(): raise ValueError(F'in create {self.__class__.__name__} got tag, size: {cdt.TAG(value[0])} {value[1]}, expected {self.TAG} {len(self)}')
|
|
77
|
+
case _: super().__init__(value)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def encoding(self) -> bytes:
|
|
81
|
+
return b'\x09\x0c' + self.contents
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class OctetStringDate(cdt.Date, tag=9, size=5):
|
|
85
|
+
""" type Time in OctetString(SIZE(5)) """
|
|
86
|
+
|
|
87
|
+
def __init__(self, value: bytes | bytearray | str | int | datetime.datetime | datetime.date = b'\x09\x05\x07\xe4\x01\x01\xff'):
|
|
88
|
+
match value: # TODO: replace priority case
|
|
89
|
+
case bytes() if value[1] == len(self): super().__init__(self.TAG+value[2:])
|
|
90
|
+
case bytes(): raise ValueError(F'in create {self.__class__.__name__} got tag, size: {cdt.TAG(value[0])} {value[1]}, expected {self.TAG} {len(self)}')
|
|
91
|
+
case _: super().__init__(value)
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def encoding(self) -> bytes:
|
|
95
|
+
return b'\x09\x05' + self.contents
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class OctetStringTime(cdt.Time, tag=9, size=4):
|
|
99
|
+
""" type Time in OctetString(SIZE(4)) """
|
|
100
|
+
|
|
101
|
+
def __init__(self, value: bytes | bytearray | str | int | datetime.datetime | datetime.time = b'\x09\x04\x00\x00\x00\x00'):
|
|
102
|
+
match value: # TODO: replace priority case
|
|
103
|
+
case bytes() if value[1] == len(self): super().__init__(self.TAG+value[2:])
|
|
104
|
+
case bytes(): raise ValueError(F'in create {self.__class__.__name__} got tag, size: {cdt.TAG(value[0])} {value[1]}, expected {self.TAG} {len(self)}')
|
|
105
|
+
case _: super().__init__(value)
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def encoding(self) -> bytes:
|
|
109
|
+
return b'\x09\x04' + self.contents
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from abc import ABC, abstractmethod
|
|
3
|
-
from ...types import common_data_types as cdt
|
|
4
|
-
from .structs import UserListEntry
|
|
5
|
-
from .double_long_usingneds import IPAddress
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SelectionAccess(cdt.Array):
|
|
9
|
-
"""Use as buffer in ProfileGeneric and object_list in AssociationLN"""
|
|
10
|
-
selective_access: Any | None = None
|
|
11
|
-
TYPE: cdt.Structure
|
|
12
|
-
|
|
13
|
-
# @abstractmethod
|
|
14
|
-
# def is_writable(self, ln: cst.LogicalName, indexes: set[int]) -> bool:
|
|
15
|
-
# """ index - DLMS object attribute index.
|
|
16
|
-
# True: DLMS object with ln and index has writable Access"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class MulticastIPAddress(cdt.Array):
|
|
20
|
-
TYPE = IPAddress
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class UserList(cdt.Array):
|
|
24
|
-
"""user_list. for AssociationLN(SN)"""
|
|
25
|
-
TYPE = UserListEntry
|
|
1
|
+
from typing import Any
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from ...types import common_data_types as cdt
|
|
4
|
+
from .structs import UserListEntry
|
|
5
|
+
from .double_long_usingneds import IPAddress
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SelectionAccess(cdt.Array):
|
|
9
|
+
"""Use as buffer in ProfileGeneric and object_list in AssociationLN"""
|
|
10
|
+
selective_access: Any | None = None
|
|
11
|
+
TYPE: cdt.Structure
|
|
12
|
+
|
|
13
|
+
# @abstractmethod
|
|
14
|
+
# def is_writable(self, ln: cst.LogicalName, indexes: set[int]) -> bool:
|
|
15
|
+
# """ index - DLMS object attribute index.
|
|
16
|
+
# True: DLMS object with ln and index has writable Access"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MulticastIPAddress(cdt.Array):
|
|
20
|
+
TYPE = IPAddress
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class UserList(cdt.Array):
|
|
24
|
+
"""user_list. for AssociationLN(SN)"""
|
|
25
|
+
TYPE = UserListEntry
|
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
from ...types import common_data_types as cdt
|
|
2
|
-
from ...config_parser import get_values
|
|
3
|
-
from ...settings import settings
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
base = get_values("DLMS", "Conformance")
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# TODO: join with cdt.FlagMixin
|
|
10
|
-
class Conformance(cdt.BitString):
|
|
11
|
-
ELEMENTS = ("reserved-zero",
|
|
12
|
-
"general-protection",
|
|
13
|
-
"general-block-transfer",
|
|
14
|
-
"read",
|
|
15
|
-
"write",
|
|
16
|
-
"unconfirmed-write",
|
|
17
|
-
"reserved-six",
|
|
18
|
-
"reserved-seven",
|
|
19
|
-
"attribute0-supported-with-set",
|
|
20
|
-
"priority-mgmt-supported",
|
|
21
|
-
"attribute0-supported-with-get",
|
|
22
|
-
"block-transfer-with-get-or-read",
|
|
23
|
-
"block-transfer-with-set-or-write",
|
|
24
|
-
"block-transfer-with-action",
|
|
25
|
-
"multiple-references",
|
|
26
|
-
"information-report",
|
|
27
|
-
"data-notification",
|
|
28
|
-
"access",
|
|
29
|
-
"parameterized-access",
|
|
30
|
-
"get",
|
|
31
|
-
"set",
|
|
32
|
-
"selective-access",
|
|
33
|
-
"event-notification",
|
|
34
|
-
"action")
|
|
35
|
-
if base is not None:
|
|
36
|
-
ELEMENTS = tuple(base[el] for el in ELEMENTS)
|
|
37
|
-
default = '011111111111111111111111' # zero only 1 bit
|
|
38
|
-
|
|
39
|
-
def __init__(self, value: bytes | bytearray | str | int | cdt.BitString = None):
|
|
40
|
-
super(Conformance, self).__init__(value)
|
|
41
|
-
if self.ELEMENTS is not None and len(self) != len(self.ELEMENTS):
|
|
42
|
-
raise ValueError(F'For {self.__class__.__name__} get {len(self)} bits, expected {len(self.ELEMENTS)}')
|
|
43
|
-
|
|
44
|
-
def __len__(self):
|
|
45
|
-
return 24
|
|
46
|
-
|
|
47
|
-
def from_bytes(self, value: bytes) -> bytes:
|
|
48
|
-
length, pdu = cdt.get_length_and_pdu(value[1:])
|
|
49
|
-
if length != len(self):
|
|
50
|
-
raise ValueError(F'Got {length=}, expected {len(self)}')
|
|
51
|
-
match value[:1]:
|
|
52
|
-
case self.TAG if len(self) <= len(pdu) * 8: return pdu[:3]
|
|
53
|
-
case self.TAG: raise ValueError(F'Got pdu length:{len(pdu)}, expected at least {len(self) >> 3}')
|
|
54
|
-
case _ as error: raise TypeError(F'Expected {self.NAME} type, got {cdt.get_common_data_type_from(error).NAME}')
|
|
55
|
-
|
|
56
|
-
def from_str(self, value: str) -> bytes:
|
|
57
|
-
value = value + '0' * ((8 - len(self)) % 8)
|
|
58
|
-
list_ = [value[count:(count + 8)] for count in range(0, len(self), 8)]
|
|
59
|
-
value = b''
|
|
60
|
-
for byte in list_:
|
|
61
|
-
value += int(byte, base=2).to_bytes(1, byteorder='little')
|
|
62
|
-
return value
|
|
63
|
-
|
|
64
|
-
def from_int(self, value: int) -> bytes:
|
|
65
|
-
if value < 0:
|
|
66
|
-
raise ValueError
|
|
67
|
-
res = 0
|
|
68
|
-
start_bit = 2 ** (len(self) - 1)
|
|
69
|
-
for i in range(len(self)):
|
|
70
|
-
if value & (1 << i):
|
|
71
|
-
res += start_bit >> i
|
|
72
|
-
return res.to_bytes(len(self) // 8, byteorder='big')
|
|
73
|
-
|
|
74
|
-
def from_bytearray(self, value: bytearray) -> bytes:
|
|
75
|
-
return bytes(value)
|
|
76
|
-
|
|
77
|
-
@classmethod
|
|
78
|
-
def get_values(cls) -> list[str]:
|
|
79
|
-
""" TODO: """
|
|
80
|
-
return cls.ELEMENTS
|
|
81
|
-
|
|
82
|
-
def validate_from(self, value: str, cursor_position: int) -> tuple[str, int]:
|
|
83
|
-
""" return validated value and cursor position. TODO: copypast FlagMixin """
|
|
84
|
-
type(self)(value=value.zfill(len(self)))
|
|
85
|
-
return value, cursor_position
|
|
86
|
-
|
|
87
|
-
@property
|
|
88
|
-
def general_protection(self) -> int:
|
|
89
|
-
return tuple(self)[1]
|
|
90
|
-
|
|
91
|
-
@property
|
|
92
|
-
def general_block_transfer(self) -> int:
|
|
93
|
-
return tuple(self)[2]
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def selective_access(self) -> int:
|
|
97
|
-
return tuple(self)[21]
|
|
1
|
+
from ...types import common_data_types as cdt
|
|
2
|
+
from ...config_parser import get_values
|
|
3
|
+
from ...settings import settings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
base = get_values("DLMS", "Conformance")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# TODO: join with cdt.FlagMixin
|
|
10
|
+
class Conformance(cdt.BitString):
|
|
11
|
+
ELEMENTS = ("reserved-zero",
|
|
12
|
+
"general-protection",
|
|
13
|
+
"general-block-transfer",
|
|
14
|
+
"read",
|
|
15
|
+
"write",
|
|
16
|
+
"unconfirmed-write",
|
|
17
|
+
"reserved-six",
|
|
18
|
+
"reserved-seven",
|
|
19
|
+
"attribute0-supported-with-set",
|
|
20
|
+
"priority-mgmt-supported",
|
|
21
|
+
"attribute0-supported-with-get",
|
|
22
|
+
"block-transfer-with-get-or-read",
|
|
23
|
+
"block-transfer-with-set-or-write",
|
|
24
|
+
"block-transfer-with-action",
|
|
25
|
+
"multiple-references",
|
|
26
|
+
"information-report",
|
|
27
|
+
"data-notification",
|
|
28
|
+
"access",
|
|
29
|
+
"parameterized-access",
|
|
30
|
+
"get",
|
|
31
|
+
"set",
|
|
32
|
+
"selective-access",
|
|
33
|
+
"event-notification",
|
|
34
|
+
"action")
|
|
35
|
+
if base is not None:
|
|
36
|
+
ELEMENTS = tuple(base[el] for el in ELEMENTS)
|
|
37
|
+
default = '011111111111111111111111' # zero only 1 bit
|
|
38
|
+
|
|
39
|
+
def __init__(self, value: bytes | bytearray | str | int | cdt.BitString = None):
|
|
40
|
+
super(Conformance, self).__init__(value)
|
|
41
|
+
if self.ELEMENTS is not None and len(self) != len(self.ELEMENTS):
|
|
42
|
+
raise ValueError(F'For {self.__class__.__name__} get {len(self)} bits, expected {len(self.ELEMENTS)}')
|
|
43
|
+
|
|
44
|
+
def __len__(self):
|
|
45
|
+
return 24
|
|
46
|
+
|
|
47
|
+
def from_bytes(self, value: bytes) -> bytes:
|
|
48
|
+
length, pdu = cdt.get_length_and_pdu(value[1:])
|
|
49
|
+
if length != len(self):
|
|
50
|
+
raise ValueError(F'Got {length=}, expected {len(self)}')
|
|
51
|
+
match value[:1]:
|
|
52
|
+
case self.TAG if len(self) <= len(pdu) * 8: return pdu[:3]
|
|
53
|
+
case self.TAG: raise ValueError(F'Got pdu length:{len(pdu)}, expected at least {len(self) >> 3}')
|
|
54
|
+
case _ as error: raise TypeError(F'Expected {self.NAME} type, got {cdt.get_common_data_type_from(error).NAME}')
|
|
55
|
+
|
|
56
|
+
def from_str(self, value: str) -> bytes:
|
|
57
|
+
value = value + '0' * ((8 - len(self)) % 8)
|
|
58
|
+
list_ = [value[count:(count + 8)] for count in range(0, len(self), 8)]
|
|
59
|
+
value = b''
|
|
60
|
+
for byte in list_:
|
|
61
|
+
value += int(byte, base=2).to_bytes(1, byteorder='little')
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
def from_int(self, value: int) -> bytes:
|
|
65
|
+
if value < 0:
|
|
66
|
+
raise ValueError
|
|
67
|
+
res = 0
|
|
68
|
+
start_bit = 2 ** (len(self) - 1)
|
|
69
|
+
for i in range(len(self)):
|
|
70
|
+
if value & (1 << i):
|
|
71
|
+
res += start_bit >> i
|
|
72
|
+
return res.to_bytes(len(self) // 8, byteorder='big')
|
|
73
|
+
|
|
74
|
+
def from_bytearray(self, value: bytearray) -> bytes:
|
|
75
|
+
return bytes(value)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def get_values(cls) -> list[str]:
|
|
79
|
+
""" TODO: """
|
|
80
|
+
return cls.ELEMENTS
|
|
81
|
+
|
|
82
|
+
def validate_from(self, value: str, cursor_position: int) -> tuple[str, int]:
|
|
83
|
+
""" return validated value and cursor position. TODO: copypast FlagMixin """
|
|
84
|
+
type(self)(value=value.zfill(len(self)))
|
|
85
|
+
return value, cursor_position
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def general_protection(self) -> int:
|
|
89
|
+
return tuple(self)[1]
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def general_block_transfer(self) -> int:
|
|
93
|
+
return tuple(self)[2]
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def selective_access(self) -> int:
|
|
97
|
+
return tuple(self)[21]
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
from ...types import common_data_types as cdt
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class DoubleLongUnsignedSecond(cdt.DoubleLongUnsigned):
|
|
5
|
-
"""for second implementation"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class IPAddress(cdt.DoubleLongUnsigned):
|
|
9
|
-
"""with string parser"""
|
|
10
|
-
|
|
11
|
-
def from_str(self, value: str) -> bytes:
|
|
12
|
-
""" create ip: integer from string type ddd.ddd.ddd.ddd, ex.: 127.0.0.1 """
|
|
13
|
-
raw_value = bytes()
|
|
14
|
-
for separator in '... ':
|
|
15
|
-
try:
|
|
16
|
-
element, value = value.split(separator, 1)
|
|
17
|
-
except ValueError:
|
|
18
|
-
element, value = value, ''
|
|
19
|
-
raw_value += self.__get_attr_element(element)
|
|
20
|
-
return raw_value
|
|
21
|
-
|
|
22
|
-
@staticmethod
|
|
23
|
-
def __get_attr_element(value: str) -> bytes:
|
|
24
|
-
if isinstance(value, str):
|
|
25
|
-
if value == '':
|
|
26
|
-
return b'\x00'
|
|
27
|
-
try:
|
|
28
|
-
return int(value).to_bytes(1, 'big')
|
|
29
|
-
except OverflowError:
|
|
30
|
-
raise ValueError(F'Int too big to convert {value}')
|
|
31
|
-
else:
|
|
32
|
-
raise TypeError(F'Unsupported type validation from string, got {value.__class__}')
|
|
33
|
-
|
|
34
|
-
def __str__(self):
|
|
35
|
-
return F'{self.contents[0]}.{self.contents[1]}.{self.contents[2]}.{self.contents[3]}'
|
|
1
|
+
from ...types import common_data_types as cdt
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DoubleLongUnsignedSecond(cdt.DoubleLongUnsigned):
|
|
5
|
+
"""for second implementation"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IPAddress(cdt.DoubleLongUnsigned):
|
|
9
|
+
"""with string parser"""
|
|
10
|
+
|
|
11
|
+
def from_str(self, value: str) -> bytes:
|
|
12
|
+
""" create ip: integer from string type ddd.ddd.ddd.ddd, ex.: 127.0.0.1 """
|
|
13
|
+
raw_value = bytes()
|
|
14
|
+
for separator in '... ':
|
|
15
|
+
try:
|
|
16
|
+
element, value = value.split(separator, 1)
|
|
17
|
+
except ValueError:
|
|
18
|
+
element, value = value, ''
|
|
19
|
+
raw_value += self.__get_attr_element(element)
|
|
20
|
+
return raw_value
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def __get_attr_element(value: str) -> bytes:
|
|
24
|
+
if isinstance(value, str):
|
|
25
|
+
if value == '':
|
|
26
|
+
return b'\x00'
|
|
27
|
+
try:
|
|
28
|
+
return int(value).to_bytes(1, 'big')
|
|
29
|
+
except OverflowError:
|
|
30
|
+
raise ValueError(F'Int too big to convert {value}')
|
|
31
|
+
else:
|
|
32
|
+
raise TypeError(F'Unsupported type validation from string, got {value.__class__}')
|
|
33
|
+
|
|
34
|
+
def __str__(self):
|
|
35
|
+
return F'{self.contents[0]}.{self.contents[1]}.{self.contents[2]}.{self.contents[3]}'
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
from ...types import common_data_types as cdt
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class CommSpeed(cdt.Enum, elements=tuple(range(10))):
|
|
5
|
-
""" The communication speed supported by the corresponding port. This communication speed can be overridden if the HDLC mode of a devive is entered through a special mode
|
|
6
|
-
of another protocol. """
|
|
7
|
-
|
|
8
|
-
def to_transcript(self) -> int:
|
|
9
|
-
""" override enum key to enum value"""
|
|
10
|
-
match self.contents:
|
|
11
|
-
case b'\x00': return 300
|
|
12
|
-
case b'\x01': return 600
|
|
13
|
-
case b'\x02': return 1200
|
|
14
|
-
case b'\x03': return 2400
|
|
15
|
-
case b'\x04': return 4800
|
|
16
|
-
case b'\x05': return 9600
|
|
17
|
-
case b'\x06': return 19200
|
|
18
|
-
case b'\x07': return 38400
|
|
19
|
-
case b'\x08': return 57600
|
|
20
|
-
case b'\x09': return 115200
|
|
21
|
-
case _ as err: raise ValueError(F'Wrong CommSpeed: {err}, expected {CommSpeed.ELEMENTS.keys()}')
|
|
22
|
-
|
|
23
|
-
def from_int(self, value: int) -> bytes:
|
|
24
|
-
""" additional cases with Speed Values"""
|
|
25
|
-
match value:
|
|
26
|
-
case 300: return b'\x00'
|
|
27
|
-
case 600: return b'\x01'
|
|
28
|
-
case 1200: return b'\x02'
|
|
29
|
-
case 2400: return b'\x03'
|
|
30
|
-
case 4800: return b'\x04'
|
|
31
|
-
case 9600: return b'\x05'
|
|
32
|
-
case 19200: return b'\x06'
|
|
33
|
-
case 38400: return b'\x07'
|
|
34
|
-
case 57600: return b'\x08'
|
|
35
|
-
case 115200: return b'\x09'
|
|
36
|
-
case other: return super(CommSpeed, self).from_int(other)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class RestrictionType(cdt.Enum, elements=(0, 1, 2)):
|
|
40
|
-
""""""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class KeyInfoType(cdt.Enum, elements=(0, 1, 2)):
|
|
44
|
-
""""""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class ProtectionType(cdt.Enum, elements=(0, 1, 2, 3)):
|
|
49
|
-
""""""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class ClientSAP(cdt.Enum, elements=(0, 1, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60)): # TODO: REWRITE elements here
|
|
53
|
-
""" IEC 62056-46 2002 6.4.2.3 Reserved special HDLC addresses p.40. IS15952ver2 """
|
|
54
|
-
TAG = b'\x0f'
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
configurator_client = ClientSAP(0x30)
|
|
1
|
+
from ...types import common_data_types as cdt
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CommSpeed(cdt.Enum, elements=tuple(range(10))):
|
|
5
|
+
""" The communication speed supported by the corresponding port. This communication speed can be overridden if the HDLC mode of a devive is entered through a special mode
|
|
6
|
+
of another protocol. """
|
|
7
|
+
|
|
8
|
+
def to_transcript(self) -> int:
|
|
9
|
+
""" override enum key to enum value"""
|
|
10
|
+
match self.contents:
|
|
11
|
+
case b'\x00': return 300
|
|
12
|
+
case b'\x01': return 600
|
|
13
|
+
case b'\x02': return 1200
|
|
14
|
+
case b'\x03': return 2400
|
|
15
|
+
case b'\x04': return 4800
|
|
16
|
+
case b'\x05': return 9600
|
|
17
|
+
case b'\x06': return 19200
|
|
18
|
+
case b'\x07': return 38400
|
|
19
|
+
case b'\x08': return 57600
|
|
20
|
+
case b'\x09': return 115200
|
|
21
|
+
case _ as err: raise ValueError(F'Wrong CommSpeed: {err}, expected {CommSpeed.ELEMENTS.keys()}')
|
|
22
|
+
|
|
23
|
+
def from_int(self, value: int) -> bytes:
|
|
24
|
+
""" additional cases with Speed Values"""
|
|
25
|
+
match value:
|
|
26
|
+
case 300: return b'\x00'
|
|
27
|
+
case 600: return b'\x01'
|
|
28
|
+
case 1200: return b'\x02'
|
|
29
|
+
case 2400: return b'\x03'
|
|
30
|
+
case 4800: return b'\x04'
|
|
31
|
+
case 9600: return b'\x05'
|
|
32
|
+
case 19200: return b'\x06'
|
|
33
|
+
case 38400: return b'\x07'
|
|
34
|
+
case 57600: return b'\x08'
|
|
35
|
+
case 115200: return b'\x09'
|
|
36
|
+
case other: return super(CommSpeed, self).from_int(other)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RestrictionType(cdt.Enum, elements=(0, 1, 2)):
|
|
40
|
+
""""""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class KeyInfoType(cdt.Enum, elements=(0, 1, 2)):
|
|
44
|
+
""""""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ProtectionType(cdt.Enum, elements=(0, 1, 2, 3)):
|
|
49
|
+
""""""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ClientSAP(cdt.Enum, elements=(0, 1, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60)): # TODO: REWRITE elements here
|
|
53
|
+
""" IEC 62056-46 2002 6.4.2.3 Reserved special HDLC addresses p.40. IS15952ver2 """
|
|
54
|
+
TAG = b'\x0f'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
configurator_client = ClientSAP(0x30)
|