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.
Files changed (100) hide show
  1. DLMS_SPODES/Values/EN/__init__.py +1 -1
  2. DLMS_SPODES/Values/EN/actors.py +8 -8
  3. DLMS_SPODES/Values/EN/relation_to_obis_names.py +387 -387
  4. DLMS_SPODES/Values/RU/__init__.py +1 -1
  5. DLMS_SPODES/Values/RU/actors.py +8 -8
  6. DLMS_SPODES/Values/RU/relation_to_obis_names.py +396 -396
  7. DLMS_SPODES/__init__.py +6 -6
  8. DLMS_SPODES/configEN.ini +126 -126
  9. DLMS_SPODES/config_parser.py +53 -53
  10. DLMS_SPODES/cosem_interface_classes/__class_init__.py +3 -3
  11. DLMS_SPODES/cosem_interface_classes/__init__.py +1 -1
  12. DLMS_SPODES/cosem_interface_classes/a_parameter.py +20 -20
  13. DLMS_SPODES/cosem_interface_classes/activity_calendar.py +254 -254
  14. DLMS_SPODES/cosem_interface_classes/arbitrator.py +105 -105
  15. DLMS_SPODES/cosem_interface_classes/association_ln/abstract.py +34 -34
  16. DLMS_SPODES/cosem_interface_classes/association_ln/authentication_mechanism_name.py +25 -25
  17. DLMS_SPODES/cosem_interface_classes/association_ln/mechanism_id.py +25 -25
  18. DLMS_SPODES/cosem_interface_classes/association_ln/method.py +5 -5
  19. DLMS_SPODES/cosem_interface_classes/association_ln/ver0.py +485 -485
  20. DLMS_SPODES/cosem_interface_classes/association_ln/ver1.py +133 -133
  21. DLMS_SPODES/cosem_interface_classes/association_ln/ver2.py +36 -36
  22. DLMS_SPODES/cosem_interface_classes/association_ln/ver3.py +4 -4
  23. DLMS_SPODES/cosem_interface_classes/association_sn/ver0.py +12 -12
  24. DLMS_SPODES/cosem_interface_classes/attr_indexes.py +12 -12
  25. DLMS_SPODES/cosem_interface_classes/clock.py +131 -131
  26. DLMS_SPODES/cosem_interface_classes/collection.py +2122 -2122
  27. DLMS_SPODES/cosem_interface_classes/cosem_interface_class.py +583 -583
  28. DLMS_SPODES/cosem_interface_classes/data.py +21 -21
  29. DLMS_SPODES/cosem_interface_classes/demand_register/ver0.py +59 -59
  30. DLMS_SPODES/cosem_interface_classes/disconnect_control.py +74 -74
  31. DLMS_SPODES/cosem_interface_classes/extended_register.py +27 -27
  32. DLMS_SPODES/cosem_interface_classes/gprs_modem_setup.py +43 -43
  33. DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver0.py +103 -103
  34. DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver1.py +40 -40
  35. DLMS_SPODES/cosem_interface_classes/gsm_diagnostic/ver2.py +9 -9
  36. DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver0.py +11 -11
  37. DLMS_SPODES/cosem_interface_classes/iec_hdlc_setup/ver1.py +53 -53
  38. DLMS_SPODES/cosem_interface_classes/iec_local_port_setup.py +11 -11
  39. DLMS_SPODES/cosem_interface_classes/image_transfer/image_transfer_status.py +15 -15
  40. DLMS_SPODES/cosem_interface_classes/image_transfer/ver0.py +126 -126
  41. DLMS_SPODES/cosem_interface_classes/implementations/__init__.py +3 -3
  42. DLMS_SPODES/cosem_interface_classes/implementations/arbitrator.py +19 -19
  43. DLMS_SPODES/cosem_interface_classes/implementations/data.py +487 -487
  44. DLMS_SPODES/cosem_interface_classes/implementations/profile_generic.py +83 -83
  45. DLMS_SPODES/cosem_interface_classes/ipv4_setup.py +72 -72
  46. DLMS_SPODES/cosem_interface_classes/limiter.py +111 -111
  47. DLMS_SPODES/cosem_interface_classes/ln_pattern.py +333 -333
  48. DLMS_SPODES/cosem_interface_classes/modem_configuration/ver0.py +65 -65
  49. DLMS_SPODES/cosem_interface_classes/modem_configuration/ver1.py +39 -39
  50. DLMS_SPODES/cosem_interface_classes/ntp_setup/ver0.py +67 -67
  51. DLMS_SPODES/cosem_interface_classes/obis.py +23 -23
  52. DLMS_SPODES/cosem_interface_classes/overview.py +197 -197
  53. DLMS_SPODES/cosem_interface_classes/parameter.py +547 -547
  54. DLMS_SPODES/cosem_interface_classes/parameters.py +172 -172
  55. DLMS_SPODES/cosem_interface_classes/profile_generic/ver0.py +122 -122
  56. DLMS_SPODES/cosem_interface_classes/profile_generic/ver1.py +277 -277
  57. DLMS_SPODES/cosem_interface_classes/push_setup/ver0.py +12 -12
  58. DLMS_SPODES/cosem_interface_classes/push_setup/ver1.py +10 -10
  59. DLMS_SPODES/cosem_interface_classes/push_setup/ver2.py +166 -166
  60. DLMS_SPODES/cosem_interface_classes/register.py +45 -45
  61. DLMS_SPODES/cosem_interface_classes/register_activation/ver0.py +80 -80
  62. DLMS_SPODES/cosem_interface_classes/register_monitor.py +46 -46
  63. DLMS_SPODES/cosem_interface_classes/reports.py +70 -70
  64. DLMS_SPODES/cosem_interface_classes/schedule.py +176 -176
  65. DLMS_SPODES/cosem_interface_classes/script_table.py +87 -87
  66. DLMS_SPODES/cosem_interface_classes/security_setup/ver0.py +68 -68
  67. DLMS_SPODES/cosem_interface_classes/security_setup/ver1.py +158 -158
  68. DLMS_SPODES/cosem_interface_classes/single_action_schedule.py +50 -50
  69. DLMS_SPODES/cosem_interface_classes/special_days_table.py +84 -84
  70. DLMS_SPODES/cosem_interface_classes/tcp_udp_setup.py +42 -42
  71. DLMS_SPODES/cosem_pdu.py +93 -93
  72. DLMS_SPODES/enums.py +625 -625
  73. DLMS_SPODES/exceptions.py +106 -106
  74. DLMS_SPODES/firmwares.py +99 -99
  75. DLMS_SPODES/hdlc/frame.py +875 -875
  76. DLMS_SPODES/hdlc/sub_layer.py +54 -54
  77. DLMS_SPODES/literals.py +17 -17
  78. DLMS_SPODES/obis/__init__.py +1 -1
  79. DLMS_SPODES/obis/media_id.py +931 -931
  80. DLMS_SPODES/pardata.py +22 -22
  81. DLMS_SPODES/pdu_enums.py +98 -98
  82. DLMS_SPODES/relation_to_OBIS.py +465 -463
  83. DLMS_SPODES/settings.py +551 -551
  84. DLMS_SPODES/types/choices.py +142 -142
  85. DLMS_SPODES/types/common_data_types.py +2401 -2399
  86. DLMS_SPODES/types/cosem_service_types.py +109 -109
  87. DLMS_SPODES/types/implementations/arrays.py +25 -25
  88. DLMS_SPODES/types/implementations/bitstrings.py +97 -97
  89. DLMS_SPODES/types/implementations/double_long_usingneds.py +35 -35
  90. DLMS_SPODES/types/implementations/enums.py +57 -57
  91. DLMS_SPODES/types/implementations/integers.py +11 -11
  92. DLMS_SPODES/types/implementations/long_unsigneds.py +127 -127
  93. DLMS_SPODES/types/implementations/octet_string.py +11 -11
  94. DLMS_SPODES/types/implementations/structs.py +64 -64
  95. DLMS_SPODES/types/useful_types.py +677 -677
  96. {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/METADATA +30 -30
  97. dlms_spodes-0.87.15.dist-info/RECORD +117 -0
  98. {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/WHEEL +1 -1
  99. dlms_spodes-0.87.12.dist-info/RECORD +0 -117
  100. {dlms_spodes-0.87.12.dist-info → dlms_spodes-0.87.15.dist-info}/top_level.txt +0 -0
@@ -1,46 +1,46 @@
1
- from .__class_init__ import *
2
- from ..types import choices
3
- from ..types.implementations import structs
4
- from .overview import VERSION_0
5
- threshold_scaler_unit = cdt.ScalUnitType(b'\x02\x02\x0f\x00\x16\x07')
6
-
7
-
8
- class Thresholds(cdt.Array):
9
- TYPE = choices.simple_dt
10
-
11
-
12
- class ActionSet(cdt.Structure):
13
- """TODO:"""
14
- action_up: structs.ActionItem
15
- action_down: structs.ActionItem
16
-
17
-
18
- class Actions(cdt.Array):
19
- """Defines the scripts to be executed when the monitored attribute of the referenced object crosses the corresponding threshold. The attribute actions has exactly
20
- the same number of elements as the attribute thresholds. The ordering of the action_items corresponds to the ordering of the thresholds (see above)."""
21
- TYPE = ActionSet
22
-
23
-
24
- class RegisterMonitor(ic.COSEMInterfaceClasses):
25
- """ DLMS UA 1000-1 Ed.14. 4.5.6. This IC allows modelling the function of monitoring of values modelled by “Data”, “Register”, “Extended register” or “Demand register” objects.
26
- It allows specifying thresholds, the value monitored, and a set of scripts (see 4.5.2) that are executed when the value monitored crosses a threshold """
27
- CLASS_ID = ClassID.REGISTER_MONITOR
28
- VERSION = VERSION_0
29
- A_ELEMENTS = (ic.ICAElement("thresholds", Thresholds),
30
- ic.ICAElement("monitored_value", structs.ValueDefinition),
31
- ic.ICAElement("actions", Actions))
32
-
33
- def characteristics_init(self):
34
- ...
35
-
36
- @property
37
- def thresholds(self) -> Thresholds:
38
- return self.get_attr(2)
39
-
40
- @property
41
- def monitored_value(self) -> structs.ValueDefinition:
42
- return self.get_attr(3)
43
-
44
- @property
45
- def threshold_normal(self) -> Actions:
46
- return self.get_attr(4)
1
+ from .__class_init__ import *
2
+ from ..types import choices
3
+ from ..types.implementations import structs
4
+ from .overview import VERSION_0
5
+ threshold_scaler_unit = cdt.ScalUnitType(b'\x02\x02\x0f\x00\x16\x07')
6
+
7
+
8
+ class Thresholds(cdt.Array):
9
+ TYPE = choices.simple_dt
10
+
11
+
12
+ class ActionSet(cdt.Structure):
13
+ """TODO:"""
14
+ action_up: structs.ActionItem
15
+ action_down: structs.ActionItem
16
+
17
+
18
+ class Actions(cdt.Array):
19
+ """Defines the scripts to be executed when the monitored attribute of the referenced object crosses the corresponding threshold. The attribute actions has exactly
20
+ the same number of elements as the attribute thresholds. The ordering of the action_items corresponds to the ordering of the thresholds (see above)."""
21
+ TYPE = ActionSet
22
+
23
+
24
+ class RegisterMonitor(ic.COSEMInterfaceClasses):
25
+ """ DLMS UA 1000-1 Ed.14. 4.5.6. This IC allows modelling the function of monitoring of values modelled by “Data”, “Register”, “Extended register” or “Demand register” objects.
26
+ It allows specifying thresholds, the value monitored, and a set of scripts (see 4.5.2) that are executed when the value monitored crosses a threshold """
27
+ CLASS_ID = ClassID.REGISTER_MONITOR
28
+ VERSION = VERSION_0
29
+ A_ELEMENTS = (ic.ICAElement("thresholds", Thresholds),
30
+ ic.ICAElement("monitored_value", structs.ValueDefinition),
31
+ ic.ICAElement("actions", Actions))
32
+
33
+ def characteristics_init(self):
34
+ ...
35
+
36
+ @property
37
+ def thresholds(self) -> Thresholds:
38
+ return self.get_attr(2)
39
+
40
+ @property
41
+ def monitored_value(self) -> structs.ValueDefinition:
42
+ return self.get_attr(3)
43
+
44
+ @property
45
+ def threshold_normal(self) -> Actions:
46
+ return self.get_attr(4)
@@ -1,70 +1,70 @@
1
- from typing import Any, Iterable
2
- from StructResult import result
3
- from . import cosem_interface_class as ic
4
- from ..types import cdt
5
- from .parameter import Parameter
6
- from ..cosem_interface_classes import collection
7
- from ..config_parser import get_values
8
- from ..settings import settings
9
-
10
-
11
- def from_obj(
12
- col: collection.Collection,
13
- obj: ic.COSEMInterfaceClasses,
14
- attr_index_par: tuple[int, ...]
15
- ) -> result.SimpleOrError[str]:
16
- if not hasattr(from_obj, "struct_pattern"):
17
- from_obj.struct_pattern = dict(settings.report.struct)
18
- ret: str = F"[{collection.get_name(obj.logical_name)}]\n"
19
- for i in attr_index_par:
20
- par = Parameter(obj.logical_name.contents).set_i(i)
21
- if isinstance(res_data := col.par2data(par), result.Error):
22
- return res_data
23
- a_data = res_data.value
24
- if isinstance(a_data, cdt.SimpleDataType):
25
- rep = col.par2rep(par, a_data)
26
- ret += F" {obj.get_attr_element(i)}: {rep.msg}{f" {rep.unit}" if rep.unit else ""}\n"
27
- elif isinstance(a_data, cdt.ComplexDataType):
28
- ret += F" [{obj.get_attr_element(i)}]\n"
29
- stack: list[tuple[Any, Any]] = [("", iter(a_data))]
30
- while stack:
31
- name, value_it = stack[-1]
32
- indent = F"{' ' * (len(stack) + 1)}"
33
- data = next(value_it, None)
34
- if data:
35
- if not isinstance(name, str):
36
- name = str(next(name))
37
- if isinstance(data, cdt.Array):
38
- ret += F"{indent}[{name}]\n"
39
- stack.append(("*", iter(data)))
40
- elif isinstance(data, cdt.Structure):
41
- if (pattern := from_obj.struct_pattern.get(data.__class__.__name__)):
42
- val = list(pattern)
43
- val.reverse()
44
- result_ = str()
45
- while val:
46
- match val.pop():
47
- case "%":
48
- par_ = val.pop()
49
- index = int(val.pop() + val.pop())
50
- match par_:
51
- case "n":
52
- result_ += str(data.ELEMENTS[index])
53
- case "v":
54
- result_ += str(data[index])
55
- case err:
56
- raise ValueError(F"unknown macros &{err}{index}")
57
- case symbol:
58
- result_ += symbol
59
- ret += F"{indent}{result_}\n"
60
- else:
61
- if name=="":
62
- ret += "\n"
63
- else:
64
- ret += F"{indent}[{name}]\n"
65
- stack.append((iter(data.ELEMENTS), iter(data)))
66
- else:
67
- ret += F"{indent}{name}: {data}\n"
68
- else:
69
- stack.pop()
70
- return result.Simple(ret)
1
+ from typing import Any, Iterable
2
+ from StructResult import result
3
+ from . import cosem_interface_class as ic
4
+ from ..types import cdt
5
+ from .parameter import Parameter
6
+ from ..cosem_interface_classes import collection
7
+ from ..config_parser import get_values
8
+ from ..settings import settings
9
+
10
+
11
+ def from_obj(
12
+ col: collection.Collection,
13
+ obj: ic.COSEMInterfaceClasses,
14
+ attr_index_par: tuple[int, ...]
15
+ ) -> result.SimpleOrError[str]:
16
+ if not hasattr(from_obj, "struct_pattern"):
17
+ from_obj.struct_pattern = dict(settings.report.struct)
18
+ ret: str = F"[{collection.get_name(obj.logical_name)}]\n"
19
+ for i in attr_index_par:
20
+ par = Parameter(obj.logical_name.contents).set_i(i)
21
+ if isinstance(res_data := col.par2data(par), result.Error):
22
+ return res_data
23
+ a_data = res_data.value
24
+ if isinstance(a_data, cdt.SimpleDataType):
25
+ rep = col.par2rep(par, a_data)
26
+ ret += F" {obj.get_attr_element(i)}: {rep.msg}{f" {rep.unit}" if rep.unit else ""}\n"
27
+ elif isinstance(a_data, cdt.ComplexDataType):
28
+ ret += F" [{obj.get_attr_element(i)}]\n"
29
+ stack: list[tuple[Any, Any]] = [("", iter(a_data))]
30
+ while stack:
31
+ name, value_it = stack[-1]
32
+ indent = F"{' ' * (len(stack) + 1)}"
33
+ data = next(value_it, None)
34
+ if data:
35
+ if not isinstance(name, str):
36
+ name = str(next(name))
37
+ if isinstance(data, cdt.Array):
38
+ ret += F"{indent}[{name}]\n"
39
+ stack.append(("*", iter(data)))
40
+ elif isinstance(data, cdt.Structure):
41
+ if (pattern := from_obj.struct_pattern.get(data.__class__.__name__)):
42
+ val = list(pattern)
43
+ val.reverse()
44
+ result_ = str()
45
+ while val:
46
+ match val.pop():
47
+ case "%":
48
+ par_ = val.pop()
49
+ index = int(val.pop() + val.pop())
50
+ match par_:
51
+ case "n":
52
+ result_ += str(data.ELEMENTS[index])
53
+ case "v":
54
+ result_ += str(data[index])
55
+ case err:
56
+ raise ValueError(F"unknown macros &{err}{index}")
57
+ case symbol:
58
+ result_ += symbol
59
+ ret += F"{indent}{result_}\n"
60
+ else:
61
+ if name=="":
62
+ ret += "\n"
63
+ else:
64
+ ret += F"{indent}[{name}]\n"
65
+ stack.append((iter(data.ELEMENTS), iter(data)))
66
+ else:
67
+ ret += F"{indent}{name}: {data}\n"
68
+ else:
69
+ stack.pop()
70
+ return result.Simple(ret)
@@ -1,176 +1,176 @@
1
- from typing import Callable, Self
2
- from itertools import count
3
- from .__class_init__ import *
4
- from .overview import VERSION_0
5
-
6
-
7
- class Index(cdt.LongUnsigned, min=1, max=9999):
8
- """ LongUnsigned type with validation """
9
- __cb_get_indexes: Callable
10
- DEFAULT = 1
11
-
12
- def set_callback(self, cb: Callable):
13
- self.__cb_get_indexes = cb
14
-
15
- def get_indexes(self) -> list[int]:
16
- """ return indexes container """
17
- return self.__cb_get_indexes()
18
-
19
- def check(self, string: str):
20
- """ raise ValueError with message if string not is valid """
21
- instance = type(self)(value=string)
22
- if int(instance) in self.__cb_get_indexes():
23
- raise ValueError('New index not unique')
24
-
25
- @classmethod
26
- def with_cb(cls, value, cb: Callable) -> Self:
27
- """ get instance with callback """
28
- ret = cls(value)
29
- ret.set_callback(cb)
30
- return ret
31
-
32
-
33
- class ScheduleTableEntry(cdt.Structure):
34
- """ Specifies the scripts to be executed at given times. There is only one script that can be executed per entry. """
35
- index: Index
36
- enable: cdt.Boolean
37
- script_logical_name: cst.LogicalName
38
- script_selector: cdt.LongUnsigned
39
- switch_time: cst.OctetStringTime
40
- validity_window: cdt.LongUnsigned
41
- exec_weekdays: cdt.BitString
42
- exec_specdays: cdt.BitString
43
- begin_date: cst.OctetStringDate
44
- end_date: cst.OctetStringDate
45
-
46
-
47
- # TODO: rewrite to new API
48
- class Entries(cdt.Array):
49
- """ Specifies the list of schedule_table_entry. Todo: validate by unique index"""
50
- TYPE = ScheduleTableEntry
51
- unique = True
52
-
53
- def __init__(self, value: bytes = None):
54
- super(Entries, self).__init__(value)
55
- # setting callback for validate schedule_table_entry index
56
- for schedule_table_entry in self:
57
- schedule_table_entry: ScheduleTableEntry
58
- schedule_table_entry.index.set_callback(self.get_indexes)
59
-
60
- def get_indexes(self) -> list[int]:
61
- """ getter for callback Index """
62
- return [entries_element.index.decode() for entries_element in self]
63
-
64
-
65
- class DataED(cdt.Structure):
66
- """ Sets the disabled bit of range A entries to true and then enables the entries of range B.
67
- * firstIndexA/B < lastIndexA/B: all entries of the range A/B are disabled/enabled
68
- * firstIndexA/B == lastIndexA/B: one entry is disabled/enabled,
69
- * firstIndexA/B > lastIndexA/B: nothing disabled/enabled,
70
- * firstIndexA/B and lastIndexA/B > 9999: no entry is disabled/enabled """
71
- firstIndexA: Index
72
- lastIndexA: Index
73
- firstIndexB: Index
74
- lastIndexB: Index
75
-
76
-
77
- class DataDelete(cdt.Structure):
78
- """ Deletes a range of entries in the table.
79
- * firstIndex < lastIndex: all entries of the range A/B are deleted,
80
- * firstIndex ::= lastIndex: one entry is deleted,
81
- * firstIndex > lastIndex: nothing deleted """
82
- firstIndex: Index
83
- lastIndex: Index
84
-
85
-
86
- class Schedule(ic.COSEMInterfaceClasses):
87
- """ The IC “Schedule” together with an object of the IC “Special days” table handles time and date driven activities within a device.
88
- The following picture gives an overview and shows the interactions between them:
89
- Schedule:
90
-
91
- Index | enable | action | Switch_time | validity_window | exec_weekdays | exec_specdays | date range
92
- | |(script)| | | Mo Tu We Th Fr Sa Su | S1 S2 ... S8 S9 | begin_date | end_date
93
- 120 Yes xxxx:yy 06:00 0xFFFF x x x x x x xx-04-01 xx-09-30
94
- 121 Yes xxxx:yy 22:00 15 x x x x x xx-04-01 xx-09-30
95
- 122 Yes xxxx:yy 12:00 0 x xx-04-01 xx-09-30
96
- 200 No xxxx:yy 06:30 x x x x x x xx-04-01 xx-09-30
97
- 201 No xxxx:yy 21:30 x x x x x xx-04-01 xx-09-30
98
- 202 No xxxx:yy 11:00 x xx-04-01 xx-09-30
99
-
100
- Special days table:
101
-
102
- Index | special_day_date | day_id
103
- 12 xx-12-24 S1
104
- 33 xx-12-25 S3
105
- 77 97-03-31 S3
106
-
107
- Recovery after power failure
108
- After a power failure, the whole schedule is processed to execute all the necessary scripts that would get lost during a power failure. For this,
109
- the entries that were not executed during the power failure must be detected. Depending on the validity window attribute they are executed in
110
- the correct order (as they would have been executed in normal operation).
111
-
112
- Handling of time changes
113
- There are four different "actions" of time changes:
114
- a) time setting forward; b) time setting backwards; c) time synchronization; d) daylight saving action.
115
- All these four actions need a different handling executed by the schedule in interaction with the time setting activity.
116
-
117
- Time setting forward*
118
- This is handled the same way as a power failure. All entries missed are executed depending on the validity window attribute.
119
- A (manufacturer specific defined) short time setting can be handled like time synchronization.
120
- * Writing to the attribute “time” of the “Clock” object.
121
-
122
- Time setting backward*
123
- This results in a repetition of those entries that are activated during the repeated time. A (manufacturer specific defined) short time setting
124
- can be handled like time synchronization.
125
- * Writing to the attribute “time” of the “Clock” object.
126
-
127
- Time synchronization*
128
- Time synchronization is used to correct small deviations between a master clock and the local clock. The algorithm is manufacturer specific.
129
- It shall guarantee that no entry of the schedule gets lost, or is executed twice. The validity window attribute has no effect, because all
130
- entries must be executed in normal operation.
131
- * Using the method “adjust_to_quarter” of the “Clock” object.
132
-
133
- Daylight saving
134
- If the clock is put forward, then all scripts, which fall into the forwarding interval (and would therefore get lost) are executed.
135
- If the clock is put back, re-execution of the scripts, which fall into the backwarding interval is suppressed. """
136
- CLASS_ID = ClassID.SCHEDULE
137
- VERSION = VERSION_0
138
- A_ELEMENTS = ic.ICAElement("entries", Entries),
139
- M_ELEMENTS = (ic.ICMElement("enable_disable", DataED),
140
- ic.ICMElement("insert", ScheduleTableEntry),
141
- ic.ICMElement("delete", DataDelete))
142
-
143
- def characteristics_init(self):
144
- self.set_attr(2, None)
145
- self._cbs_attr_post_init.update({2: self.__set_index_cbs})
146
-
147
- @property
148
- def entries(self) -> Entries:
149
- return self.get_attr(2)
150
-
151
- @property
152
- def enable_disable(self) -> DataED:
153
- return self.get_meth(1)
154
-
155
- @property
156
- def insert(self) -> ScheduleTableEntry:
157
- return self.get_meth(2)
158
-
159
- @property
160
- def delete(self) -> DataDelete:
161
- return self.get_meth(3)
162
-
163
- def __set_index_cbs(self):
164
- """ set callbacks to methods """
165
- try:
166
- indexes: Callable = self.entries.get_indexes
167
- self.enable_disable.firstIndexA.set_callback(indexes)
168
- self.enable_disable.firstIndexB.set_callback(indexes)
169
- self.enable_disable.lastIndexA.set_callback(indexes)
170
- self.enable_disable.lastIndexB.set_callback(indexes)
171
- self.insert.index.set_callback(indexes)
172
- self.delete.firstIndex.set_callback(indexes)
173
- self.delete.lastIndex.set_callback(indexes)
174
- # print('set delete')
175
- except KeyError: # At init time
176
- print('set delete NO:')
1
+ from typing import Callable, Self
2
+ from itertools import count
3
+ from .__class_init__ import *
4
+ from .overview import VERSION_0
5
+
6
+
7
+ class Index(cdt.LongUnsigned, min=1, max=9999):
8
+ """ LongUnsigned type with validation """
9
+ __cb_get_indexes: Callable
10
+ DEFAULT = 1
11
+
12
+ def set_callback(self, cb: Callable):
13
+ self.__cb_get_indexes = cb
14
+
15
+ def get_indexes(self) -> list[int]:
16
+ """ return indexes container """
17
+ return self.__cb_get_indexes()
18
+
19
+ def check(self, string: str):
20
+ """ raise ValueError with message if string not is valid """
21
+ instance = type(self)(value=string)
22
+ if int(instance) in self.__cb_get_indexes():
23
+ raise ValueError('New index not unique')
24
+
25
+ @classmethod
26
+ def with_cb(cls, value, cb: Callable) -> Self:
27
+ """ get instance with callback """
28
+ ret = cls(value)
29
+ ret.set_callback(cb)
30
+ return ret
31
+
32
+
33
+ class ScheduleTableEntry(cdt.Structure):
34
+ """ Specifies the scripts to be executed at given times. There is only one script that can be executed per entry. """
35
+ index: Index
36
+ enable: cdt.Boolean
37
+ script_logical_name: cst.LogicalName
38
+ script_selector: cdt.LongUnsigned
39
+ switch_time: cst.OctetStringTime
40
+ validity_window: cdt.LongUnsigned
41
+ exec_weekdays: cdt.BitString
42
+ exec_specdays: cdt.BitString
43
+ begin_date: cst.OctetStringDate
44
+ end_date: cst.OctetStringDate
45
+
46
+
47
+ # TODO: rewrite to new API
48
+ class Entries(cdt.Array):
49
+ """ Specifies the list of schedule_table_entry. Todo: validate by unique index"""
50
+ TYPE = ScheduleTableEntry
51
+ unique = True
52
+
53
+ def __init__(self, value: bytes = None):
54
+ super(Entries, self).__init__(value)
55
+ # setting callback for validate schedule_table_entry index
56
+ for schedule_table_entry in self:
57
+ schedule_table_entry: ScheduleTableEntry
58
+ schedule_table_entry.index.set_callback(self.get_indexes)
59
+
60
+ def get_indexes(self) -> list[int]:
61
+ """ getter for callback Index """
62
+ return [entries_element.index.decode() for entries_element in self]
63
+
64
+
65
+ class DataED(cdt.Structure):
66
+ """ Sets the disabled bit of range A entries to true and then enables the entries of range B.
67
+ * firstIndexA/B < lastIndexA/B: all entries of the range A/B are disabled/enabled
68
+ * firstIndexA/B == lastIndexA/B: one entry is disabled/enabled,
69
+ * firstIndexA/B > lastIndexA/B: nothing disabled/enabled,
70
+ * firstIndexA/B and lastIndexA/B > 9999: no entry is disabled/enabled """
71
+ firstIndexA: Index
72
+ lastIndexA: Index
73
+ firstIndexB: Index
74
+ lastIndexB: Index
75
+
76
+
77
+ class DataDelete(cdt.Structure):
78
+ """ Deletes a range of entries in the table.
79
+ * firstIndex < lastIndex: all entries of the range A/B are deleted,
80
+ * firstIndex ::= lastIndex: one entry is deleted,
81
+ * firstIndex > lastIndex: nothing deleted """
82
+ firstIndex: Index
83
+ lastIndex: Index
84
+
85
+
86
+ class Schedule(ic.COSEMInterfaceClasses):
87
+ """ The IC “Schedule” together with an object of the IC “Special days” table handles time and date driven activities within a device.
88
+ The following picture gives an overview and shows the interactions between them:
89
+ Schedule:
90
+
91
+ Index | enable | action | Switch_time | validity_window | exec_weekdays | exec_specdays | date range
92
+ | |(script)| | | Mo Tu We Th Fr Sa Su | S1 S2 ... S8 S9 | begin_date | end_date
93
+ 120 Yes xxxx:yy 06:00 0xFFFF x x x x x x xx-04-01 xx-09-30
94
+ 121 Yes xxxx:yy 22:00 15 x x x x x xx-04-01 xx-09-30
95
+ 122 Yes xxxx:yy 12:00 0 x xx-04-01 xx-09-30
96
+ 200 No xxxx:yy 06:30 x x x x x x xx-04-01 xx-09-30
97
+ 201 No xxxx:yy 21:30 x x x x x xx-04-01 xx-09-30
98
+ 202 No xxxx:yy 11:00 x xx-04-01 xx-09-30
99
+
100
+ Special days table:
101
+
102
+ Index | special_day_date | day_id
103
+ 12 xx-12-24 S1
104
+ 33 xx-12-25 S3
105
+ 77 97-03-31 S3
106
+
107
+ Recovery after power failure
108
+ After a power failure, the whole schedule is processed to execute all the necessary scripts that would get lost during a power failure. For this,
109
+ the entries that were not executed during the power failure must be detected. Depending on the validity window attribute they are executed in
110
+ the correct order (as they would have been executed in normal operation).
111
+
112
+ Handling of time changes
113
+ There are four different "actions" of time changes:
114
+ a) time setting forward; b) time setting backwards; c) time synchronization; d) daylight saving action.
115
+ All these four actions need a different handling executed by the schedule in interaction with the time setting activity.
116
+
117
+ Time setting forward*
118
+ This is handled the same way as a power failure. All entries missed are executed depending on the validity window attribute.
119
+ A (manufacturer specific defined) short time setting can be handled like time synchronization.
120
+ * Writing to the attribute “time” of the “Clock” object.
121
+
122
+ Time setting backward*
123
+ This results in a repetition of those entries that are activated during the repeated time. A (manufacturer specific defined) short time setting
124
+ can be handled like time synchronization.
125
+ * Writing to the attribute “time” of the “Clock” object.
126
+
127
+ Time synchronization*
128
+ Time synchronization is used to correct small deviations between a master clock and the local clock. The algorithm is manufacturer specific.
129
+ It shall guarantee that no entry of the schedule gets lost, or is executed twice. The validity window attribute has no effect, because all
130
+ entries must be executed in normal operation.
131
+ * Using the method “adjust_to_quarter” of the “Clock” object.
132
+
133
+ Daylight saving
134
+ If the clock is put forward, then all scripts, which fall into the forwarding interval (and would therefore get lost) are executed.
135
+ If the clock is put back, re-execution of the scripts, which fall into the backwarding interval is suppressed. """
136
+ CLASS_ID = ClassID.SCHEDULE
137
+ VERSION = VERSION_0
138
+ A_ELEMENTS = ic.ICAElement("entries", Entries),
139
+ M_ELEMENTS = (ic.ICMElement("enable_disable", DataED),
140
+ ic.ICMElement("insert", ScheduleTableEntry),
141
+ ic.ICMElement("delete", DataDelete))
142
+
143
+ def characteristics_init(self):
144
+ self.set_attr(2, None)
145
+ self._cbs_attr_post_init.update({2: self.__set_index_cbs})
146
+
147
+ @property
148
+ def entries(self) -> Entries:
149
+ return self.get_attr(2)
150
+
151
+ @property
152
+ def enable_disable(self) -> DataED:
153
+ return self.get_meth(1)
154
+
155
+ @property
156
+ def insert(self) -> ScheduleTableEntry:
157
+ return self.get_meth(2)
158
+
159
+ @property
160
+ def delete(self) -> DataDelete:
161
+ return self.get_meth(3)
162
+
163
+ def __set_index_cbs(self):
164
+ """ set callbacks to methods """
165
+ try:
166
+ indexes: Callable = self.entries.get_indexes
167
+ self.enable_disable.firstIndexA.set_callback(indexes)
168
+ self.enable_disable.firstIndexB.set_callback(indexes)
169
+ self.enable_disable.lastIndexA.set_callback(indexes)
170
+ self.enable_disable.lastIndexB.set_callback(indexes)
171
+ self.insert.index.set_callback(indexes)
172
+ self.delete.firstIndex.set_callback(indexes)
173
+ self.delete.lastIndex.set_callback(indexes)
174
+ # print('set delete')
175
+ except KeyError: # At init time
176
+ print('set delete NO:')