DLMS-SPODES 0.87.12__py3-none-any.whl → 0.87.15__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 -2399
- 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.12.dist-info → dlms_spodes-0.87.15.dist-info}/METADATA +30 -30
- dlms_spodes-0.87.15.dist-info/RECORD +117 -0
- {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/WHEEL +1 -1
- dlms_spodes-0.87.12.dist-info/RECORD +0 -117
- {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/top_level.txt +0 -0
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
from .__class_init__ import *
|
|
2
|
-
from ..types.implementations import structs
|
|
3
|
-
from .overview import VERSION_0
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class TYPE(cdt.Enum, elements=(1, 2, 3, 4, 5)):
|
|
7
|
-
""""""
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ExecutionTimeDate(cdt.Structure):
|
|
11
|
-
""" Specifies the time and teh date when the script is executed. The two octet-string s contain time and date, in this order; time and date are
|
|
12
|
-
formatted as specified in DLMS UA 1000-1 Ed.12.0 4.1.6.1. Hundredths of second shall be zero. """
|
|
13
|
-
time: cst.OctetStringTime
|
|
14
|
-
date: cst.OctetStringDate
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class ExecutionTime(cdt.Array):
|
|
18
|
-
""" Specifies the list of execution time and date """
|
|
19
|
-
TYPE = ExecutionTimeDate
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class SingleActionSchedule(ic.COSEMInterfaceClasses):
|
|
23
|
-
""" This IC allows modelling the execution of periodic actions within a meter. Such actions are not necessarily linked to tariffication
|
|
24
|
-
(see “Activity calendar” or “Schedule”). """
|
|
25
|
-
CLASS_ID = ClassID.SINGLE_ACTION_SCHEDULE
|
|
26
|
-
VERSION = VERSION_0
|
|
27
|
-
A_ELEMENTS = (ic.ICAElement("executed_script", structs.ActionItem),
|
|
28
|
-
ic.ICAElement("type", TYPE),
|
|
29
|
-
ic.ICAElement("execution_time", ExecutionTime))
|
|
30
|
-
|
|
31
|
-
def characteristics_init(self):
|
|
32
|
-
"""nothing do it"""
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def executed_script(self) -> structs.ActionItem:
|
|
36
|
-
return self.get_attr(2)
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def type_(self) -> TYPE:
|
|
40
|
-
return self.get_attr(3)
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def execution_time(self) -> ExecutionTime:
|
|
44
|
-
return self.get_attr(4)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if __name__ == '__main__':
|
|
48
|
-
a = ExecutionTimeDate(b'\x02\x02\t\x04\x12\x007\x00\t\x05\x07\xe5\x05\x1f\x01')
|
|
49
|
-
a = SingleActionSchedule('0.0.15.0.0.255')
|
|
50
|
-
print(a)
|
|
1
|
+
from .__class_init__ import *
|
|
2
|
+
from ..types.implementations import structs
|
|
3
|
+
from .overview import VERSION_0
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TYPE(cdt.Enum, elements=(1, 2, 3, 4, 5)):
|
|
7
|
+
""""""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ExecutionTimeDate(cdt.Structure):
|
|
11
|
+
""" Specifies the time and teh date when the script is executed. The two octet-string s contain time and date, in this order; time and date are
|
|
12
|
+
formatted as specified in DLMS UA 1000-1 Ed.12.0 4.1.6.1. Hundredths of second shall be zero. """
|
|
13
|
+
time: cst.OctetStringTime
|
|
14
|
+
date: cst.OctetStringDate
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExecutionTime(cdt.Array):
|
|
18
|
+
""" Specifies the list of execution time and date """
|
|
19
|
+
TYPE = ExecutionTimeDate
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SingleActionSchedule(ic.COSEMInterfaceClasses):
|
|
23
|
+
""" This IC allows modelling the execution of periodic actions within a meter. Such actions are not necessarily linked to tariffication
|
|
24
|
+
(see “Activity calendar” or “Schedule”). """
|
|
25
|
+
CLASS_ID = ClassID.SINGLE_ACTION_SCHEDULE
|
|
26
|
+
VERSION = VERSION_0
|
|
27
|
+
A_ELEMENTS = (ic.ICAElement("executed_script", structs.ActionItem),
|
|
28
|
+
ic.ICAElement("type", TYPE),
|
|
29
|
+
ic.ICAElement("execution_time", ExecutionTime))
|
|
30
|
+
|
|
31
|
+
def characteristics_init(self):
|
|
32
|
+
"""nothing do it"""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def executed_script(self) -> structs.ActionItem:
|
|
36
|
+
return self.get_attr(2)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def type_(self) -> TYPE:
|
|
40
|
+
return self.get_attr(3)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def execution_time(self) -> ExecutionTime:
|
|
44
|
+
return self.get_attr(4)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == '__main__':
|
|
48
|
+
a = ExecutionTimeDate(b'\x02\x02\t\x04\x12\x007\x00\t\x05\x07\xe5\x05\x1f\x01')
|
|
49
|
+
a = SingleActionSchedule('0.0.15.0.0.255')
|
|
50
|
+
print(a)
|
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
from .__class_init__ import *
|
|
3
|
-
from .overview import VERSION_0
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class SpecDayEntry(cdt.Structure):
|
|
7
|
-
""" Specifies a special day identifier for a given date. The date may have wildcards for repeating special days like Christmas. """
|
|
8
|
-
index: cdt.LongUnsigned
|
|
9
|
-
specialday_date: cst.OctetStringDate
|
|
10
|
-
day_id: cdt.Unsigned
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Entries(cdt.Array):
|
|
14
|
-
""" Specifies the list of spec_day_entry """
|
|
15
|
-
TYPE = SpecDayEntry
|
|
16
|
-
values: list[SpecDayEntry]
|
|
17
|
-
__getitem__: SpecDayEntry
|
|
18
|
-
|
|
19
|
-
def new_element(self) -> SpecDayEntry:
|
|
20
|
-
indexes: list[int] = [int(el.index) for el in self.values]
|
|
21
|
-
for i in range(0x1_00_00):
|
|
22
|
-
if i not in indexes:
|
|
23
|
-
return SpecDayEntry((i, None, None)) # TODO: insert first DayID from ActiveCalendar as 3 element
|
|
24
|
-
raise ValueError(F'in {self} all indexes is busy')
|
|
25
|
-
|
|
26
|
-
def __check_index(self, value):
|
|
27
|
-
"""validate day_id from DayProfile"""
|
|
28
|
-
if cdt.LongUnsigned(value) in (entry.index for entry in self.values):
|
|
29
|
-
raise ValueError(F'{cdt.LongUnsigned(value)} already exist in {self}')
|
|
30
|
-
else:
|
|
31
|
-
"""validate OK"""
|
|
32
|
-
|
|
33
|
-
def validate_exist_index(self, value):
|
|
34
|
-
"""pass if value in indexes"""
|
|
35
|
-
if cdt.LongUnsigned(value) not in (entry.index for entry in self.values):
|
|
36
|
-
raise ValueError(F'{cdt.LongUnsigned(value)} not exist in {self}')
|
|
37
|
-
else:
|
|
38
|
-
"""validate OK"""
|
|
39
|
-
|
|
40
|
-
def get_indexes(self) -> list[int]:
|
|
41
|
-
""" getter for callback Index """
|
|
42
|
-
return [int(entries_element.index) for entries_element in self.values]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class SpecialDaysTable(ic.COSEMInterfaceClasses):
|
|
46
|
-
""" The interface class allows defining dates, which will override normal switching behaviour for special days. The interface class works in
|
|
47
|
-
conjunction with the class "Schedule" or "Activity calendar" and the linking data item is day_id """
|
|
48
|
-
CLASS_ID = ClassID.SPECIAL_DAYS_TABLE
|
|
49
|
-
VERSION = VERSION_0
|
|
50
|
-
A_ELEMENTS = ic.ICAElement("entries", Entries),
|
|
51
|
-
M_ELEMENTS = (ic.ICMElement("insert", SpecDayEntry),
|
|
52
|
-
ic.ICMElement("delete", cdt.LongUnsigned)) # Todo: was Delete.with_cb(None, self.entries.get_indexes)
|
|
53
|
-
|
|
54
|
-
def characteristics_init(self):
|
|
55
|
-
self._cbs_attr_post_init.update({2: self.__set_delete})
|
|
56
|
-
self.set_attr(2, None)
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def entries(self) -> Entries:
|
|
60
|
-
return self.get_attr(2)
|
|
61
|
-
|
|
62
|
-
@property
|
|
63
|
-
def insert(self) -> SpecDayEntry:
|
|
64
|
-
return self.get_meth(1)
|
|
65
|
-
|
|
66
|
-
@property
|
|
67
|
-
def delete(self) -> cdt.LongUnsigned:
|
|
68
|
-
return self.get_meth(2)
|
|
69
|
-
|
|
70
|
-
def __set_delete(self):
|
|
71
|
-
try:
|
|
72
|
-
self.delete.register_cb_preset(self.entries.validate_exist_index)
|
|
73
|
-
self.insert.index.register_cb_preset(self.entries.validate_exist_index)
|
|
74
|
-
except KeyError: # At init time
|
|
75
|
-
print('set delete NO:')
|
|
76
|
-
|
|
77
|
-
def __delete_entry(self):
|
|
78
|
-
"""remove one entry by according delete method index. Call after execute"""
|
|
79
|
-
for entry in self.entries.values:
|
|
80
|
-
if entry.index == self.delete:
|
|
81
|
-
self.entries.values.remove(entry)
|
|
82
|
-
return
|
|
83
|
-
else:
|
|
84
|
-
raise ValueError(F'not found entry with index {self.delete} for remove')
|
|
1
|
+
import datetime
|
|
2
|
+
from .__class_init__ import *
|
|
3
|
+
from .overview import VERSION_0
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SpecDayEntry(cdt.Structure):
|
|
7
|
+
""" Specifies a special day identifier for a given date. The date may have wildcards for repeating special days like Christmas. """
|
|
8
|
+
index: cdt.LongUnsigned
|
|
9
|
+
specialday_date: cst.OctetStringDate
|
|
10
|
+
day_id: cdt.Unsigned
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Entries(cdt.Array):
|
|
14
|
+
""" Specifies the list of spec_day_entry """
|
|
15
|
+
TYPE = SpecDayEntry
|
|
16
|
+
values: list[SpecDayEntry]
|
|
17
|
+
__getitem__: SpecDayEntry
|
|
18
|
+
|
|
19
|
+
def new_element(self) -> SpecDayEntry:
|
|
20
|
+
indexes: list[int] = [int(el.index) for el in self.values]
|
|
21
|
+
for i in range(0x1_00_00):
|
|
22
|
+
if i not in indexes:
|
|
23
|
+
return SpecDayEntry((i, None, None)) # TODO: insert first DayID from ActiveCalendar as 3 element
|
|
24
|
+
raise ValueError(F'in {self} all indexes is busy')
|
|
25
|
+
|
|
26
|
+
def __check_index(self, value):
|
|
27
|
+
"""validate day_id from DayProfile"""
|
|
28
|
+
if cdt.LongUnsigned(value) in (entry.index for entry in self.values):
|
|
29
|
+
raise ValueError(F'{cdt.LongUnsigned(value)} already exist in {self}')
|
|
30
|
+
else:
|
|
31
|
+
"""validate OK"""
|
|
32
|
+
|
|
33
|
+
def validate_exist_index(self, value):
|
|
34
|
+
"""pass if value in indexes"""
|
|
35
|
+
if cdt.LongUnsigned(value) not in (entry.index for entry in self.values):
|
|
36
|
+
raise ValueError(F'{cdt.LongUnsigned(value)} not exist in {self}')
|
|
37
|
+
else:
|
|
38
|
+
"""validate OK"""
|
|
39
|
+
|
|
40
|
+
def get_indexes(self) -> list[int]:
|
|
41
|
+
""" getter for callback Index """
|
|
42
|
+
return [int(entries_element.index) for entries_element in self.values]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SpecialDaysTable(ic.COSEMInterfaceClasses):
|
|
46
|
+
""" The interface class allows defining dates, which will override normal switching behaviour for special days. The interface class works in
|
|
47
|
+
conjunction with the class "Schedule" or "Activity calendar" and the linking data item is day_id """
|
|
48
|
+
CLASS_ID = ClassID.SPECIAL_DAYS_TABLE
|
|
49
|
+
VERSION = VERSION_0
|
|
50
|
+
A_ELEMENTS = ic.ICAElement("entries", Entries),
|
|
51
|
+
M_ELEMENTS = (ic.ICMElement("insert", SpecDayEntry),
|
|
52
|
+
ic.ICMElement("delete", cdt.LongUnsigned)) # Todo: was Delete.with_cb(None, self.entries.get_indexes)
|
|
53
|
+
|
|
54
|
+
def characteristics_init(self):
|
|
55
|
+
self._cbs_attr_post_init.update({2: self.__set_delete})
|
|
56
|
+
self.set_attr(2, None)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def entries(self) -> Entries:
|
|
60
|
+
return self.get_attr(2)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def insert(self) -> SpecDayEntry:
|
|
64
|
+
return self.get_meth(1)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def delete(self) -> cdt.LongUnsigned:
|
|
68
|
+
return self.get_meth(2)
|
|
69
|
+
|
|
70
|
+
def __set_delete(self):
|
|
71
|
+
try:
|
|
72
|
+
self.delete.register_cb_preset(self.entries.validate_exist_index)
|
|
73
|
+
self.insert.index.register_cb_preset(self.entries.validate_exist_index)
|
|
74
|
+
except KeyError: # At init time
|
|
75
|
+
print('set delete NO:')
|
|
76
|
+
|
|
77
|
+
def __delete_entry(self):
|
|
78
|
+
"""remove one entry by according delete method index. Call after execute"""
|
|
79
|
+
for entry in self.entries.values:
|
|
80
|
+
if entry.index == self.delete:
|
|
81
|
+
self.entries.values.remove(entry)
|
|
82
|
+
return
|
|
83
|
+
else:
|
|
84
|
+
raise ValueError(F'not found entry with index {self.delete} for remove')
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
from .__class_init__ import *
|
|
2
|
-
from .overview import VERSION_0
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class TCPUDPSetup(ic.COSEMInterfaceClasses):
|
|
6
|
-
""" This IC allows modelling the setup of the TCP or UDP sub-layer of the COSEM TCP or UDP based transport layer of a TCP-UDP/IP based communication profile.
|
|
7
|
-
In TCP-UDP/IP based communication profiles, all AAs between a physical device hosting one or more COSEM client application processes and a physical device hosting one or more
|
|
8
|
-
COSEM server APs rely on a single TCP or UDP connection. The TCP or UDP entity is wrapped in the COSEM TCP-UDP based transport layer. Within a physical device, each
|
|
9
|
-
AP - client AP or server logical device - is bound to a Wrapper Port (WPort). The binding is done with the help of the SAP Assignment object. See 4.4.5 DLMS 1000-1 Ed. 12.0.
|
|
10
|
-
On the other hand, COSEM TCP or UDP based transport layer may be capable to support more than one TCP or UDP connections, between a physical device and several peer physical
|
|
11
|
-
devices hosting COSEM APs.
|
|
12
|
-
When a COSEM physical device supports various data link layers - for example Ethernet and PPP - an instance of the TCP-UDP setup object is necessary for each of them. """
|
|
13
|
-
CLASS_ID = ClassID.TCP_UDP_SETUP
|
|
14
|
-
VERSION = VERSION_0
|
|
15
|
-
A_ELEMENTS = (ic.ICAElement("TCP_UDP_port", cdt.LongUnsigned, default=4059),
|
|
16
|
-
ic.ICAElement("IP_reference", cst.LogicalName),
|
|
17
|
-
ic.ICAElement("MMS", cdt.LongUnsigned, 40, 535, 535), # TODO: max, def not according by BlueBook
|
|
18
|
-
ic.ICAElement("nb_of_sim_conn", cdt.Unsigned, 1),
|
|
19
|
-
ic.ICAElement("inactivity_time_out", cdt.LongUnsigned, default=180))
|
|
20
|
-
|
|
21
|
-
def characteristics_init(self):
|
|
22
|
-
"""nothing do it"""
|
|
23
|
-
|
|
24
|
-
@property
|
|
25
|
-
def TCP_UDP_port(self) -> cdt.LongUnsigned:
|
|
26
|
-
return self.get_attr(2)
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def IP_reference(self) -> cst.LogicalName:
|
|
30
|
-
return self.get_attr(3)
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def MMS(self) -> cdt.LongUnsigned:
|
|
34
|
-
return self.get_attr(4)
|
|
35
|
-
|
|
36
|
-
@property
|
|
37
|
-
def nb_of_sim_conn(self) -> cdt.Unsigned:
|
|
38
|
-
return self.get_attr(5)
|
|
39
|
-
|
|
40
|
-
@property
|
|
41
|
-
def inactivity_time_out(self) -> cdt.LongUnsigned:
|
|
42
|
-
return self.get_attr(6)
|
|
1
|
+
from .__class_init__ import *
|
|
2
|
+
from .overview import VERSION_0
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TCPUDPSetup(ic.COSEMInterfaceClasses):
|
|
6
|
+
""" This IC allows modelling the setup of the TCP or UDP sub-layer of the COSEM TCP or UDP based transport layer of a TCP-UDP/IP based communication profile.
|
|
7
|
+
In TCP-UDP/IP based communication profiles, all AAs between a physical device hosting one or more COSEM client application processes and a physical device hosting one or more
|
|
8
|
+
COSEM server APs rely on a single TCP or UDP connection. The TCP or UDP entity is wrapped in the COSEM TCP-UDP based transport layer. Within a physical device, each
|
|
9
|
+
AP - client AP or server logical device - is bound to a Wrapper Port (WPort). The binding is done with the help of the SAP Assignment object. See 4.4.5 DLMS 1000-1 Ed. 12.0.
|
|
10
|
+
On the other hand, COSEM TCP or UDP based transport layer may be capable to support more than one TCP or UDP connections, between a physical device and several peer physical
|
|
11
|
+
devices hosting COSEM APs.
|
|
12
|
+
When a COSEM physical device supports various data link layers - for example Ethernet and PPP - an instance of the TCP-UDP setup object is necessary for each of them. """
|
|
13
|
+
CLASS_ID = ClassID.TCP_UDP_SETUP
|
|
14
|
+
VERSION = VERSION_0
|
|
15
|
+
A_ELEMENTS = (ic.ICAElement("TCP_UDP_port", cdt.LongUnsigned, default=4059),
|
|
16
|
+
ic.ICAElement("IP_reference", cst.LogicalName),
|
|
17
|
+
ic.ICAElement("MMS", cdt.LongUnsigned, 40, 535, 535), # TODO: max, def not according by BlueBook
|
|
18
|
+
ic.ICAElement("nb_of_sim_conn", cdt.Unsigned, 1),
|
|
19
|
+
ic.ICAElement("inactivity_time_out", cdt.LongUnsigned, default=180))
|
|
20
|
+
|
|
21
|
+
def characteristics_init(self):
|
|
22
|
+
"""nothing do it"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def TCP_UDP_port(self) -> cdt.LongUnsigned:
|
|
26
|
+
return self.get_attr(2)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def IP_reference(self) -> cst.LogicalName:
|
|
30
|
+
return self.get_attr(3)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def MMS(self) -> cdt.LongUnsigned:
|
|
34
|
+
return self.get_attr(4)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def nb_of_sim_conn(self) -> cdt.Unsigned:
|
|
38
|
+
return self.get_attr(5)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def inactivity_time_out(self) -> cdt.LongUnsigned:
|
|
42
|
+
return self.get_attr(6)
|
DLMS_SPODES/cosem_pdu.py
CHANGED
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
from typing import Dict, Type, Union
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from struct import pack
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
_services: Dict[int, Type[Union[AARE, ]]] = dict()
|
|
8
|
-
""" COSEM Services dictionary """
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Tag:
|
|
12
|
-
""" APDU tag TODO: """
|
|
13
|
-
value: int
|
|
14
|
-
|
|
15
|
-
def __init__(self, value: int):
|
|
16
|
-
self.value = value
|
|
17
|
-
|
|
18
|
-
def __get__(self, instance, owner: COSEMPdu) -> int:
|
|
19
|
-
return self.value
|
|
20
|
-
|
|
21
|
-
def __set__(self, instance, value):
|
|
22
|
-
raise ValueError("Tag no supported change")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class COSEMPdu(ABC):
|
|
26
|
-
""" TODO: """
|
|
27
|
-
tag: Tag
|
|
28
|
-
|
|
29
|
-
def __init__(self, content: bytearray):
|
|
30
|
-
if content.pop(0) != self.tag:
|
|
31
|
-
raise ValueError(F'Wrong {self.__class__.__name__} tag, expected {self.tag}')
|
|
32
|
-
length = content.pop(0)
|
|
33
|
-
if len(content) != length:
|
|
34
|
-
raise ValueError(F'Wrong PDU length, expected {length}, got {len(content)}')
|
|
35
|
-
|
|
36
|
-
def __init_subclass__(cls, **kwargs):
|
|
37
|
-
""" register subclass in _types with unique tag """
|
|
38
|
-
super().__init_subclass__(**kwargs)
|
|
39
|
-
if hasattr(cls, 'tag'):
|
|
40
|
-
_services.setdefault(cls.tag, cls)
|
|
41
|
-
else:
|
|
42
|
-
""" Handler for subclass without tag """
|
|
43
|
-
|
|
44
|
-
@classmethod
|
|
45
|
-
def from_content(cls, content: bytearray):
|
|
46
|
-
tag = content[0]
|
|
47
|
-
service = _services.get(tag, None)(content)
|
|
48
|
-
if service is None:
|
|
49
|
-
raise ValueError(F'service with tag:{tag} is absence in Service Dictionary')
|
|
50
|
-
else:
|
|
51
|
-
return service
|
|
52
|
-
|
|
53
|
-
@property
|
|
54
|
-
@abstractmethod
|
|
55
|
-
def info(self) -> bytes:
|
|
56
|
-
""" return info. All services, objects etc... TODO: """
|
|
57
|
-
|
|
58
|
-
def content(self) -> bytes:
|
|
59
|
-
return pack('BB', self.tag, len(self.info)) + self.info
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class ACSE(COSEMPdu, ABC):
|
|
63
|
-
""" TODO: """
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class AARE(ACSE):
|
|
67
|
-
""" TODO: """
|
|
68
|
-
tag = Tag(61)
|
|
69
|
-
|
|
70
|
-
def __init__(self, content: bytearray):
|
|
71
|
-
super(AARE, self).__init__(content)
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def info(self) -> bytes:
|
|
75
|
-
return bytes()
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class AARQ(ACSE):
|
|
79
|
-
""" TODO: """
|
|
80
|
-
tag = Tag(60)
|
|
81
|
-
|
|
82
|
-
def __init__(self, content: bytearray):
|
|
83
|
-
super(AARQ, self).__init__(content)
|
|
84
|
-
|
|
85
|
-
@property
|
|
86
|
-
def info(self) -> bytes:
|
|
87
|
-
return bytes()
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if __name__ == '__main__':
|
|
91
|
-
a = COSEMPdu.from_content(bytearray((61,3,1,2,4)))
|
|
92
|
-
b = a.content().hex(' ')
|
|
93
|
-
print(a)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Dict, Type, Union
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from struct import pack
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_services: Dict[int, Type[Union[AARE, ]]] = dict()
|
|
8
|
+
""" COSEM Services dictionary """
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Tag:
|
|
12
|
+
""" APDU tag TODO: """
|
|
13
|
+
value: int
|
|
14
|
+
|
|
15
|
+
def __init__(self, value: int):
|
|
16
|
+
self.value = value
|
|
17
|
+
|
|
18
|
+
def __get__(self, instance, owner: COSEMPdu) -> int:
|
|
19
|
+
return self.value
|
|
20
|
+
|
|
21
|
+
def __set__(self, instance, value):
|
|
22
|
+
raise ValueError("Tag no supported change")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class COSEMPdu(ABC):
|
|
26
|
+
""" TODO: """
|
|
27
|
+
tag: Tag
|
|
28
|
+
|
|
29
|
+
def __init__(self, content: bytearray):
|
|
30
|
+
if content.pop(0) != self.tag:
|
|
31
|
+
raise ValueError(F'Wrong {self.__class__.__name__} tag, expected {self.tag}')
|
|
32
|
+
length = content.pop(0)
|
|
33
|
+
if len(content) != length:
|
|
34
|
+
raise ValueError(F'Wrong PDU length, expected {length}, got {len(content)}')
|
|
35
|
+
|
|
36
|
+
def __init_subclass__(cls, **kwargs):
|
|
37
|
+
""" register subclass in _types with unique tag """
|
|
38
|
+
super().__init_subclass__(**kwargs)
|
|
39
|
+
if hasattr(cls, 'tag'):
|
|
40
|
+
_services.setdefault(cls.tag, cls)
|
|
41
|
+
else:
|
|
42
|
+
""" Handler for subclass without tag """
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_content(cls, content: bytearray):
|
|
46
|
+
tag = content[0]
|
|
47
|
+
service = _services.get(tag, None)(content)
|
|
48
|
+
if service is None:
|
|
49
|
+
raise ValueError(F'service with tag:{tag} is absence in Service Dictionary')
|
|
50
|
+
else:
|
|
51
|
+
return service
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def info(self) -> bytes:
|
|
56
|
+
""" return info. All services, objects etc... TODO: """
|
|
57
|
+
|
|
58
|
+
def content(self) -> bytes:
|
|
59
|
+
return pack('BB', self.tag, len(self.info)) + self.info
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ACSE(COSEMPdu, ABC):
|
|
63
|
+
""" TODO: """
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AARE(ACSE):
|
|
67
|
+
""" TODO: """
|
|
68
|
+
tag = Tag(61)
|
|
69
|
+
|
|
70
|
+
def __init__(self, content: bytearray):
|
|
71
|
+
super(AARE, self).__init__(content)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def info(self) -> bytes:
|
|
75
|
+
return bytes()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AARQ(ACSE):
|
|
79
|
+
""" TODO: """
|
|
80
|
+
tag = Tag(60)
|
|
81
|
+
|
|
82
|
+
def __init__(self, content: bytearray):
|
|
83
|
+
super(AARQ, self).__init__(content)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def info(self) -> bytes:
|
|
87
|
+
return bytes()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == '__main__':
|
|
91
|
+
a = COSEMPdu.from_content(bytearray((61,3,1,2,4)))
|
|
92
|
+
b = a.content().hex(' ')
|
|
93
|
+
print(a)
|