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,485 +1,485 @@
1
- from typing import Iterator
2
- from enum import IntFlag, auto
3
- from ..parameters import Parameter
4
- from ... import exceptions as exc
5
- from ..__class_init__ import *
6
- from ...types import choices
7
- from ...types.implementations import arrays, enums, bitstrings, long_unsigneds, structs
8
- from ... import pdu_enums as pdu
9
- from . import mechanism_id, authentication_mechanism_name
10
- from . import method
11
- from . import abstract
12
- from ..overview import VERSION_0
13
-
14
-
15
- class AccessMode(abstract.AccessMode, elements=(0, 1, 2, 3)):
16
- """ TODO: """
17
- def is_writable(self) -> bool:
18
- return True if int(self) >= 2 else False
19
-
20
- def is_readable(self) -> bool:
21
- return True if int(self) in (1, 3) else False
22
-
23
-
24
- class AttributeAccessItem(abstract.AttributeAccessItem):
25
- """ Implemented attribute and it access . Use in Association LN """
26
- DEFAULT = b'\x02\x03\x0f\x01\x16\x00\x00'
27
- attribute_id: cdt.Integer
28
- access_mode: AccessMode
29
- access_selectors: choices.access_selectors
30
-
31
- def abstract_marker(self):
32
- ...
33
-
34
-
35
- class AttributeAccessDescriptor(abstract.AttributeAccessDescriptor):
36
- """ Array of attribute_access_item """
37
- TYPE = AttributeAccessItem
38
-
39
- def set_read_access(self, attribute_id: cdt.Integer):
40
- it: AttributeAccessItem
41
- for it in self:
42
- if it.attribute_id == attribute_id:
43
- it.access_mode.set(1)
44
- break
45
- else:
46
- self.append(AttributeAccessItem((attribute_id, AccessMode.parse("1"), None)))
47
-
48
-
49
- class MethodAccessItem(cdt.Structure):
50
- """ Implemented method and it access . Use in Association LN """
51
- method_id: cdt.Integer
52
- access_mode: cdt.Boolean
53
-
54
-
55
- class MethodAccessDescriptor(cdt.Array):
56
- """ Contain all implemented methods """
57
- TYPE = MethodAccessItem
58
-
59
-
60
- class AccessRight(cdt.Structure):
61
- """ TODO: """
62
- attribute_access: AttributeAccessDescriptor
63
- method_access: MethodAccessDescriptor
64
-
65
-
66
- class ObjectListElement(structs.ObjectListElement, access_rights=AccessRight):
67
- """ Visible COSEM objects with their class_id, version, logical name and the access rights to their attributes and methods within the given application association"""
68
-
69
-
70
- class ObjectListType(arrays.SelectionAccess):
71
- """ Array of object_list_element. The range for the client_SAP is 0…0x7F. The range for the server_SAP is 0x000…0x3FFF."""
72
- TYPE = ObjectListElement
73
- __getitem__: ObjectListElement
74
-
75
- def is_writable(self, ln: cst.LogicalName, indexes: set[int]) -> bool:
76
- """ index - DLMS object attribute index.
77
- True: AccessRight is WriteOnly or ReadAndWrite """
78
- el: ObjectListElement = next(filter(lambda it: it.logical_name == ln, self), None)
79
- if el is None:
80
- raise exc.NoObject(F"not find {ln} in object_list")
81
- item: AttributeAccessItem
82
- for index in indexes:
83
- for item in el.access_rights.attribute_access:
84
- if int(item.attribute_id) == index:
85
- if int(item.access_mode) not in (2, 3):
86
- return False
87
- else:
88
- break
89
- else:
90
- continue
91
- else:
92
- raise ValueError(F"not find in {ln} attribute index: {index}")
93
- return True
94
-
95
- def __get_access_right(self, ln: cst.LogicalName | ut.CosemObjectInstanceId) -> AccessRight:
96
- """return object_list_element of object_list AssociationLN"""
97
- el: ObjectListElement = next(filter(lambda it: it.logical_name == ln, self), None)
98
- if el is None:
99
- raise exc.NoObject(F"not find {ln} in object_list")
100
- else:
101
- return el.access_rights
102
-
103
- def get_attr_access(self, ln: cst.LogicalName, index: int) -> pdu.AttributeAccess:
104
- """ index - DLMS object attribute index """
105
- for item in self.__get_access_right(ln).attribute_access: # item: AttributeAccessItem
106
- item: AttributeAccessItem
107
- if int(item.attribute_id) == index:
108
- return pdu.AttributeAccess(int(item.access_mode))
109
- else:
110
- continue
111
- else:
112
- raise ValueError(F"access for {ln}: {index} is absense")
113
-
114
- def get_access_mode(self, ln: cst.LogicalName, i: int) -> AccessMode:
115
- """ index - DLMS object attribute index """
116
- for item in self.__get_access_right(ln).attribute_access: # item: AttributeAccessItem
117
- item: AttributeAccessItem
118
- if int(item.attribute_id) == i:
119
- return item.access_mode
120
- else:
121
- continue
122
- else:
123
- raise ValueError(F"access for {ln}: {i} is absense")
124
-
125
- def get_meth_access(self, ln: cst.LogicalName | ut.CosemObjectInstanceId, index: int) -> pdu.MethodAccess:
126
- """ index - DLMS object method index """
127
- for item in self.__get_access_right(ln).method_access: # item: MethodAccessItem
128
- item: MethodAccessItem
129
- if int(item.method_id) == index:
130
- return pdu.MethodAccess(int(item.access_mode))
131
- else:
132
- continue
133
- else:
134
- raise exc.ITEApplication(F"not find method access rules in object_list for {ln.get_report()}:{index}") # todo: make custom error
135
-
136
-
137
- class AssociatedPartnersType(cdt.Structure):
138
- """ Contains the identifiers of the COSEM client and the COSEM server (logical device) application processes within the physical devices
139
- hosting these processes, which belong to the application association modelled by the “Association LN” object. """
140
- DEFAULT = (0x10, 1)
141
- client_SAP: enums.ClientSAP
142
- server_SAP: long_unsigneds.ServerSAP
143
-
144
-
145
- class ApplicationContextName(cdt.Structure):
146
- """ In the COSEM environment, it is intended that an application context pre-exists and is referenced by its name during the establishment of an
147
- application association. This attribute contains the name of the application context for that association."""
148
- DEFAULT = (2, 16, 116, 5, 8, 1, 1)
149
- joint_iso_ctt_element: cdt.Unsigned
150
- country_element: cdt.Unsigned
151
- country_name_element: cdt.LongUnsigned
152
- identified_organization_element: cdt.Unsigned
153
- DLMS_UA_element: cdt.Unsigned
154
- application_context_element: cdt.Unsigned
155
- context_id_element: cdt.Unsigned
156
-
157
-
158
- class XDLMSContextType(cdt.Structure):
159
- """ Contains all the necessary information on the xDLMS context for the given association. """
160
- DEFAULT = (None, 1024, 1024, 6, 0, b'\x09\x07\x60\x85\x74\x05\x08\x02\x00')
161
- conformance: bitstrings.Conformance
162
- max_receive_pdu_size: cdt.LongUnsigned
163
- max_send_pdu_size: cdt.LongUnsigned
164
- dlms_version_number: cdt.Unsigned
165
- quality_of_service: cdt.Integer
166
- cyphering_info: cdt.OctetString
167
-
168
-
169
- class AssociationStatus(cdt.Enum, elements=(0, 1, 2)):
170
- """ Enum of access mode for methods """
171
-
172
-
173
- class ClassList(cdt.Array):
174
- """ Access by class. In this case, only those object_list_elements of the object_list shall be included in the response, which have a class_id
175
- equal to one of the class_id-s of the class-list. No access_right information is included """
176
- TYPE = long_unsigneds.ClassId
177
-
178
-
179
- class ObjectId(cdt.Structure):
180
- DEFAULT = b'\x02\x02\x12\x00\x08\x09\x06\x00\x00\x01\x00\x00\xff'
181
- class_id: long_unsigneds.ClassId
182
- logical_name: cst.LogicalName
183
-
184
-
185
- class ObjectIdList(cdt.Array):
186
- """ Access by object. The full information record of object instances on the object_Id_list shall be returned. """
187
- TYPE = ObjectId
188
-
189
-
190
- class Representation(IntFlag):
191
- HEX = 0
192
- ASCII = auto()
193
- HIDDEN = auto()
194
- ASCII_HIDDEN = ASCII | HIDDEN
195
-
196
-
197
- class LLCSecret(cdt.OctetString):
198
- """ representation for secret """
199
- __representation: Representation = Representation.HEX
200
- # used for set class property from instance
201
-
202
- def __init__(self, value: bytes | bytearray | str | int = None):
203
-
204
- super(LLCSecret, self).__init__(value)
205
- # self.representation = Representation(0)
206
-
207
- def __setattr__(self, key, value):
208
- match key:
209
- case 'representation' if not isinstance(value, Representation): raise ValueError(F"Error representation type")
210
- case 'representation' as rep: LLCSecret.__representation = value
211
- case _: super().__setattr__(key, value)
212
-
213
- @property
214
- def representation(self) -> Representation:
215
- """used for set class property from instance"""
216
- return self.__representation
217
-
218
- def __getattr__(self, item):
219
- raise AttributeError(F'LLCSecret not has {item}')
220
-
221
- @staticmethod
222
- def __hide_all(value: str) -> str:
223
- for char_ in filter(lambda it: it != ' ', value):
224
- value = value.replace(char_, '*')
225
- return value
226
-
227
- def from_str(self, value: str) -> bytes:
228
- if self.__representation & Representation.ASCII:
229
- return cdt.VisibleString.from_str(self, value)
230
- else:
231
- return super(LLCSecret, self).from_str(value)
232
-
233
- def __str__(self):
234
- match self.__representation:
235
- case Representation.ASCII: return cdt.VisibleString.__str__(self)
236
- case Representation.HIDDEN: return self.__hide_all(super(LLCSecret, self).__str__())
237
- case Representation.ASCII_HIDDEN: return self.__hide_all(cdt.VisibleString.__str__(self))
238
- case _: return super(LLCSecret, self).__str__()
239
-
240
-
241
- class LLCSecretHigh(LLCSecret):
242
- DEFAULT = b'0000000000000000'
243
-
244
- def validation(self):
245
- """ check for length equal 16 """
246
- if len(self) != 16:
247
- raise ValueError(F'Got length of High secret: {len(self)}, expected 16')
248
-
249
- def validate_from(self, value: str, cursor_position=None) -> tuple[str, int]:
250
- try:
251
- correct = type(self)(value)
252
- return str(correct), cursor_position + (len(str(correct))-len(value))
253
- except ValueError:
254
- match self.representation & 0b1:
255
- case Representation.HEX:
256
- cursor_position: int = len(value)-1 if cursor_position is None else cursor_position
257
- type(self)(F'{value[:cursor_position]}0{value[cursor_position:]}') # check possible
258
- case Representation.ASCII:
259
- type(self)(value.zfill(16))
260
- return value, cursor_position
261
-
262
-
263
- class AccessSelector(ut.Unsigned8):
264
- """ Unsigned8 1..4 """
265
- def __init__(self, value: int | str | ut.Unsigned8 = 1):
266
- super(AccessSelector, self).__init__(value)
267
- if int(self) > 4 or int(self) < 1:
268
- raise ValueError(F'The {self.__class__.__name__} got {self}, expected 1..4')
269
-
270
-
271
- class Data(ut.Data):
272
- ELEMENTS = {1: ut.SequenceElement('All information', cdt.NullData),
273
- 2: ut.SequenceElement('Access by class', ClassList),
274
- 3: ut.SequenceElement('Access by object', ObjectIdList),
275
- 4: ut.SequenceElement('Full object information', ObjectId)}
276
-
277
-
278
- class SelectiveAccessDescriptor(ut.SelectiveAccessDescriptor):
279
- """ Selective access specification always starts with an access selector, followed by an access-specific access parameter list.
280
- Specified IS/IEC 62056-53 : 2006, 7.4.1.6 Selective access """
281
- access_selector: AccessSelector
282
- access_parameters: Data
283
- ELEMENTS = (ut.SequenceElement('access_selector', AccessSelector),
284
- ut.SequenceElement('access_parameters', Data))
285
-
286
-
287
- class CosemAttributeDescriptorWithSelection(ut.CosemAttributeDescriptorWithSelection):
288
- access_selection: SelectiveAccessDescriptor
289
- ELEMENTS = (ut.SequenceElement('cosem_attribute_descriptor', ut.CosemAttributeDescriptor),
290
- ut.SequenceElement('access_selection', SelectiveAccessDescriptor))
291
-
292
-
293
- class AssociationLN(ic.COSEMInterfaceClasses):
294
- """5.4.5 Association LN"""
295
- CLASS_ID = ClassID.ASSOCIATION_LN
296
- VERSION = VERSION_0
297
- A_ELEMENTS = (ic.ICAElement("object_list", ObjectListType, selective_access=SelectiveAccessDescriptor),
298
- ic.ICAElement("associated_partners_id", AssociatedPartnersType),
299
- ic.ICAElement("application_context_name", ApplicationContextName),
300
- ic.ICAElement("xDLMS_context_info", XDLMSContextType),
301
- ic.ICAElement("authentication_mechanism_name", authentication_mechanism_name.AuthenticationMechanismName),
302
- ic.ICAElement("LLS_secret", LLCSecret, classifier=ic.Classifier.NOT_SPECIFIC),
303
- ic.ICAElement("association_status", AssociationStatus, classifier=ic.Classifier.DYNAMIC))
304
- M_ELEMENTS = (ic.ICMElement("reply_to_HLS_authentication", method.ReplyToHLSAuthentication),
305
- ic.ICMElement("change_HLS_secret", LLCSecret),
306
- ic.ICMElement("add_object", ObjectListElement),
307
- ic.ICMElement("remove_object", ObjectListElement))
308
-
309
- def characteristics_init(self):
310
- # self.object_list.selective_access = SelectiveAccessDescriptor()
311
- self.set_attr(4, None)
312
- self.set_attr(5, None)
313
- self.set_attr(8, None)
314
- # init secret after set authentication_mechanism_name(6)
315
- self._cbs_attr_post_init.update({
316
- 6: self.__init_secret,
317
- 7: self.__check_mechanism_id_existing})
318
-
319
- @property
320
- def object_list(self) -> ObjectListType:
321
- return self.get_attr(2)
322
-
323
- @property
324
- def associated_partners_id(self) -> AssociatedPartnersType:
325
- return self.get_attr(3)
326
-
327
- @property
328
- def application_context_name(self) -> ApplicationContextName:
329
- return self.get_attr(4)
330
-
331
- @property
332
- def xDLMS_context_info(self) -> XDLMSContextType:
333
- return self.get_attr(5)
334
-
335
- @property
336
- def authentication_mechanism_name(self) -> authentication_mechanism_name.AuthenticationMechanismName:
337
- return self.get_attr(6)
338
-
339
- @property
340
- def LLS_secret(self) -> LLCSecret:
341
- return self.get_attr(7)
342
-
343
- @property
344
- def association_status(self) -> AssociationStatus:
345
- return self.get_attr(8)
346
-
347
- @property
348
- def reply_to_HLS_authentication(self) -> method.ReplyToHLSAuthentication:
349
- return self.get_meth(1)
350
-
351
- @property
352
- def change_HLS_secret(self) -> LLS_secret:
353
- return self.get_meth(2)
354
-
355
- @property
356
- def add_object(self) -> ObjectListElement:
357
- return self.get_meth(3)
358
-
359
- @property
360
- def remove_object(self) -> ObjectListElement:
361
- return self.get_meth(4)
362
-
363
- def __check_mechanism_id_existing(self):
364
- """check for existing mechanism ID else ERASE setting"""
365
- if self.authentication_mechanism_name is None:
366
- self.clear_attr(7)
367
- self._cbs_attr_post_init[7] = self.__check_mechanism_id_existing
368
- else:
369
- """nothing do it"""
370
-
371
- def __init_secret(self):
372
- """ before initiating secret need knowledge what kind of mechanism ID """
373
- match self.authentication_mechanism_name.mechanism_id_element, self.LLS_secret:
374
- case mechanism_id.NONE | mechanism_id.LOW, LLCSecret(): """keep secret value"""
375
- case mechanism_id.NONE | mechanism_id.LOW, _: self.set_attr_link(7, LLCSecret())
376
- case mechanism_id.HIGH, LLCSecretHigh(): """keep secret value"""
377
- case mechanism_id.HIGH, _: self.set_attr_link(7, LLCSecretHigh())
378
- case unknown, _: raise ValueError(F'Not support Secret with {unknown}')
379
-
380
- def get_attr_descriptor(self,
381
- value: int,
382
- with_selection: bool = False) -> ut.CosemAttributeDescriptor | CosemAttributeDescriptorWithSelection:
383
- """ with selection for object_list. TODO: Copypast ProfileGeneric"""
384
- descriptor: ut.CosemAttributeDescriptor = super(AssociationLN, self).get_attr_descriptor(value)
385
- if value == 2 and with_selection and self.object_list:
386
- return CosemAttributeDescriptorWithSelection((descriptor, self.object_list.selective_access))
387
- else:
388
- return descriptor
389
-
390
- def get_lns(self) -> list[cst.LogicalName]:
391
- """return all LogicalNames"""
392
- el: ObjectListElement
393
- return [el.logical_name for el in self.object_list]
394
-
395
- def iter_pars(self) -> Iterator[Parameter]:
396
- return (Parameter(el.logical_name.contents) for el in self.object_list)
397
-
398
- def __check_empty_object_list(self):
399
- if self.object_list is None:
400
- raise exc.ITEApplication(F"empty <{self.get_attr_element(2).NAME}> in {self}")
401
-
402
- def is_readable(self,
403
- ln: cst.LogicalName,
404
- index: int,
405
- security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
406
- ) -> bool:
407
- self.__check_empty_object_list()
408
- match self.object_list.get_attr_access(ln, index):
409
- case pdu.AttributeAccess.NO_ACCESS | pdu.AttributeAccess.WRITE_ONLY | pdu.AttributeAccess.AUTHENTICATED_WRITE_ONLY:
410
- return False
411
- case pdu.AttributeAccess.READ_ONLY | pdu.AttributeAccess.READ_AND_WRITE:
412
- return True
413
- case pdu.AttributeAccess.AUTHENTICATED_READ_ONLY | pdu.AttributeAccess.AUTHENTICATED_READ_AND_WRITE:
414
- if isinstance(security_policy, pdu.SecurityPolicyVer0):
415
- match security_policy:
416
- case pdu.SecurityPolicyVer0.AUTHENTICATED | pdu.SecurityPolicyVer0.AUTHENTICATED_AND_ENCRYPTED:
417
- return True
418
- case _:
419
- return False
420
- elif isinstance(security_policy, pdu.SecurityPolicyVer1):
421
- if bool(security_policy & (pdu.SecurityPolicyVer1.AUTHENTICATED_REQUEST | pdu.SecurityPolicyVer1.AUTHENTICATED_RESPONSE)):
422
- return True
423
- else:
424
- return False
425
- else:
426
- raise TypeError(F"unknown {security_policy.__class__}: {security_policy}")
427
- case err:
428
- raise exc.ITEApplication(F"unsupport access: {err}")
429
-
430
- def is_writable(self,
431
- ln: cst.LogicalName,
432
- index: int,
433
- security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
434
- ) -> bool:
435
- self.__check_empty_object_list()
436
- return is_attr_writable(
437
- mode=self.object_list.get_access_mode(ln, index),
438
- security_policy=security_policy)
439
-
440
- def is_accessible(self, ln: cst.LogicalName,
441
- index: int,
442
- m_id: mechanism_id.MechanismIdElement = None
443
- ) -> bool:
444
- """for ver 0 and 1 only"""
445
- self.__check_empty_object_list()
446
- match self.object_list.get_meth_access(ln, index):
447
- case pdu.MethodAccess.NO_ACCESS:
448
- return False
449
- case pdu.MethodAccess.ACCESS:
450
- return True
451
- case pdu.MethodAccess.AUTHENTICATED_ACCESS:
452
- if not m_id:
453
- m_id = self.authentication_mechanism_name.mechanism_id_element
454
- if m_id > mechanism_id.NONE:
455
- return True
456
- else:
457
- return False
458
- case err:
459
- raise exc.ITEApplication(F"unsupport access: {err}")
460
-
461
-
462
- def is_attr_writable(
463
- mode: AccessMode,
464
- security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING) -> bool:
465
- match int(mode):
466
- case pdu.AttributeAccess.NO_ACCESS | pdu.AttributeAccess.READ_ONLY | pdu.AttributeAccess.AUTHENTICATED_READ_ONLY:
467
- return False
468
- case pdu.AttributeAccess.WRITE_ONLY | pdu.AttributeAccess.READ_AND_WRITE:
469
- return True
470
- case pdu.AttributeAccess.AUTHENTICATED_WRITE_ONLY | pdu.AttributeAccess.AUTHENTICATED_READ_AND_WRITE:
471
- if isinstance(security_policy, pdu.SecurityPolicyVer0):
472
- match security_policy:
473
- case pdu.SecurityPolicyVer0.AUTHENTICATED | pdu.SecurityPolicyVer0.AUTHENTICATED_AND_ENCRYPTED:
474
- return True
475
- case _:
476
- return False
477
- elif isinstance(security_policy, pdu.SecurityPolicyVer1):
478
- if bool(security_policy & (pdu.SecurityPolicyVer1.AUTHENTICATED_REQUEST | pdu.SecurityPolicyVer1.AUTHENTICATED_RESPONSE)):
479
- return True
480
- else:
481
- return False
482
- else:
483
- raise TypeError(F"unknown {security_policy.__class__}: {security_policy}")
484
- case err:
485
- raise exc.ITEApplication(F"unsupport access: {err}")
1
+ from typing import Iterator
2
+ from enum import IntFlag, auto
3
+ from ..parameters import Parameter
4
+ from ... import exceptions as exc
5
+ from ..__class_init__ import *
6
+ from ...types import choices
7
+ from ...types.implementations import arrays, enums, bitstrings, long_unsigneds, structs
8
+ from ... import pdu_enums as pdu
9
+ from . import mechanism_id, authentication_mechanism_name
10
+ from . import method
11
+ from . import abstract
12
+ from ..overview import VERSION_0
13
+
14
+
15
+ class AccessMode(abstract.AccessMode, elements=(0, 1, 2, 3)):
16
+ """ TODO: """
17
+ def is_writable(self) -> bool:
18
+ return True if int(self) >= 2 else False
19
+
20
+ def is_readable(self) -> bool:
21
+ return True if int(self) in (1, 3) else False
22
+
23
+
24
+ class AttributeAccessItem(abstract.AttributeAccessItem):
25
+ """ Implemented attribute and it access . Use in Association LN """
26
+ DEFAULT = b'\x02\x03\x0f\x01\x16\x00\x00'
27
+ attribute_id: cdt.Integer
28
+ access_mode: AccessMode
29
+ access_selectors: choices.access_selectors
30
+
31
+ def abstract_marker(self):
32
+ ...
33
+
34
+
35
+ class AttributeAccessDescriptor(abstract.AttributeAccessDescriptor):
36
+ """ Array of attribute_access_item """
37
+ TYPE = AttributeAccessItem
38
+
39
+ def set_read_access(self, attribute_id: cdt.Integer):
40
+ it: AttributeAccessItem
41
+ for it in self:
42
+ if it.attribute_id == attribute_id:
43
+ it.access_mode.set(1)
44
+ break
45
+ else:
46
+ self.append(AttributeAccessItem((attribute_id, AccessMode.parse("1"), None)))
47
+
48
+
49
+ class MethodAccessItem(cdt.Structure):
50
+ """ Implemented method and it access . Use in Association LN """
51
+ method_id: cdt.Integer
52
+ access_mode: cdt.Boolean
53
+
54
+
55
+ class MethodAccessDescriptor(cdt.Array):
56
+ """ Contain all implemented methods """
57
+ TYPE = MethodAccessItem
58
+
59
+
60
+ class AccessRight(cdt.Structure):
61
+ """ TODO: """
62
+ attribute_access: AttributeAccessDescriptor
63
+ method_access: MethodAccessDescriptor
64
+
65
+
66
+ class ObjectListElement(structs.ObjectListElement, access_rights=AccessRight):
67
+ """ Visible COSEM objects with their class_id, version, logical name and the access rights to their attributes and methods within the given application association"""
68
+
69
+
70
+ class ObjectListType(arrays.SelectionAccess):
71
+ """ Array of object_list_element. The range for the client_SAP is 0…0x7F. The range for the server_SAP is 0x000…0x3FFF."""
72
+ TYPE = ObjectListElement
73
+ __getitem__: ObjectListElement
74
+
75
+ def is_writable(self, ln: cst.LogicalName, indexes: set[int]) -> bool:
76
+ """ index - DLMS object attribute index.
77
+ True: AccessRight is WriteOnly or ReadAndWrite """
78
+ el: ObjectListElement = next(filter(lambda it: it.logical_name == ln, self), None)
79
+ if el is None:
80
+ raise exc.NoObject(F"not find {ln} in object_list")
81
+ item: AttributeAccessItem
82
+ for index in indexes:
83
+ for item in el.access_rights.attribute_access:
84
+ if int(item.attribute_id) == index:
85
+ if int(item.access_mode) not in (2, 3):
86
+ return False
87
+ else:
88
+ break
89
+ else:
90
+ continue
91
+ else:
92
+ raise ValueError(F"not find in {ln} attribute index: {index}")
93
+ return True
94
+
95
+ def __get_access_right(self, ln: cst.LogicalName | ut.CosemObjectInstanceId) -> AccessRight:
96
+ """return object_list_element of object_list AssociationLN"""
97
+ el: ObjectListElement = next(filter(lambda it: it.logical_name == ln, self), None)
98
+ if el is None:
99
+ raise exc.NoObject(F"not find {ln} in object_list")
100
+ else:
101
+ return el.access_rights
102
+
103
+ def get_attr_access(self, ln: cst.LogicalName, index: int) -> pdu.AttributeAccess:
104
+ """ index - DLMS object attribute index """
105
+ for item in self.__get_access_right(ln).attribute_access: # item: AttributeAccessItem
106
+ item: AttributeAccessItem
107
+ if int(item.attribute_id) == index:
108
+ return pdu.AttributeAccess(int(item.access_mode))
109
+ else:
110
+ continue
111
+ else:
112
+ raise ValueError(F"access for {ln}: {index} is absense")
113
+
114
+ def get_access_mode(self, ln: cst.LogicalName, i: int) -> AccessMode:
115
+ """ index - DLMS object attribute index """
116
+ for item in self.__get_access_right(ln).attribute_access: # item: AttributeAccessItem
117
+ item: AttributeAccessItem
118
+ if int(item.attribute_id) == i:
119
+ return item.access_mode
120
+ else:
121
+ continue
122
+ else:
123
+ raise ValueError(F"access for {ln}: {i} is absense")
124
+
125
+ def get_meth_access(self, ln: cst.LogicalName | ut.CosemObjectInstanceId, index: int) -> pdu.MethodAccess:
126
+ """ index - DLMS object method index """
127
+ for item in self.__get_access_right(ln).method_access: # item: MethodAccessItem
128
+ item: MethodAccessItem
129
+ if int(item.method_id) == index:
130
+ return pdu.MethodAccess(int(item.access_mode))
131
+ else:
132
+ continue
133
+ else:
134
+ raise exc.ITEApplication(F"not find method access rules in object_list for {ln.get_report()}:{index}") # todo: make custom error
135
+
136
+
137
+ class AssociatedPartnersType(cdt.Structure):
138
+ """ Contains the identifiers of the COSEM client and the COSEM server (logical device) application processes within the physical devices
139
+ hosting these processes, which belong to the application association modelled by the “Association LN” object. """
140
+ DEFAULT = (0x10, 1)
141
+ client_SAP: enums.ClientSAP
142
+ server_SAP: long_unsigneds.ServerSAP
143
+
144
+
145
+ class ApplicationContextName(cdt.Structure):
146
+ """ In the COSEM environment, it is intended that an application context pre-exists and is referenced by its name during the establishment of an
147
+ application association. This attribute contains the name of the application context for that association."""
148
+ DEFAULT = (2, 16, 116, 5, 8, 1, 1)
149
+ joint_iso_ctt_element: cdt.Unsigned
150
+ country_element: cdt.Unsigned
151
+ country_name_element: cdt.LongUnsigned
152
+ identified_organization_element: cdt.Unsigned
153
+ DLMS_UA_element: cdt.Unsigned
154
+ application_context_element: cdt.Unsigned
155
+ context_id_element: cdt.Unsigned
156
+
157
+
158
+ class XDLMSContextType(cdt.Structure):
159
+ """ Contains all the necessary information on the xDLMS context for the given association. """
160
+ DEFAULT = (None, 1024, 1024, 6, 0, b'\x09\x07\x60\x85\x74\x05\x08\x02\x00')
161
+ conformance: bitstrings.Conformance
162
+ max_receive_pdu_size: cdt.LongUnsigned
163
+ max_send_pdu_size: cdt.LongUnsigned
164
+ dlms_version_number: cdt.Unsigned
165
+ quality_of_service: cdt.Integer
166
+ cyphering_info: cdt.OctetString
167
+
168
+
169
+ class AssociationStatus(cdt.Enum, elements=(0, 1, 2)):
170
+ """ Enum of access mode for methods """
171
+
172
+
173
+ class ClassList(cdt.Array):
174
+ """ Access by class. In this case, only those object_list_elements of the object_list shall be included in the response, which have a class_id
175
+ equal to one of the class_id-s of the class-list. No access_right information is included """
176
+ TYPE = long_unsigneds.ClassId
177
+
178
+
179
+ class ObjectId(cdt.Structure):
180
+ DEFAULT = b'\x02\x02\x12\x00\x08\x09\x06\x00\x00\x01\x00\x00\xff'
181
+ class_id: long_unsigneds.ClassId
182
+ logical_name: cst.LogicalName
183
+
184
+
185
+ class ObjectIdList(cdt.Array):
186
+ """ Access by object. The full information record of object instances on the object_Id_list shall be returned. """
187
+ TYPE = ObjectId
188
+
189
+
190
+ class Representation(IntFlag):
191
+ HEX = 0
192
+ ASCII = auto()
193
+ HIDDEN = auto()
194
+ ASCII_HIDDEN = ASCII | HIDDEN
195
+
196
+
197
+ class LLCSecret(cdt.OctetString):
198
+ """ representation for secret """
199
+ __representation: Representation = Representation.HEX
200
+ # used for set class property from instance
201
+
202
+ def __init__(self, value: bytes | bytearray | str | int = None):
203
+
204
+ super(LLCSecret, self).__init__(value)
205
+ # self.representation = Representation(0)
206
+
207
+ def __setattr__(self, key, value):
208
+ match key:
209
+ case 'representation' if not isinstance(value, Representation): raise ValueError(F"Error representation type")
210
+ case 'representation' as rep: LLCSecret.__representation = value
211
+ case _: super().__setattr__(key, value)
212
+
213
+ @property
214
+ def representation(self) -> Representation:
215
+ """used for set class property from instance"""
216
+ return self.__representation
217
+
218
+ def __getattr__(self, item):
219
+ raise AttributeError(F'LLCSecret not has {item}')
220
+
221
+ @staticmethod
222
+ def __hide_all(value: str) -> str:
223
+ for char_ in filter(lambda it: it != ' ', value):
224
+ value = value.replace(char_, '*')
225
+ return value
226
+
227
+ def from_str(self, value: str) -> bytes:
228
+ if self.__representation & Representation.ASCII:
229
+ return cdt.VisibleString.from_str(self, value)
230
+ else:
231
+ return super(LLCSecret, self).from_str(value)
232
+
233
+ def __str__(self):
234
+ match self.__representation:
235
+ case Representation.ASCII: return cdt.VisibleString.__str__(self)
236
+ case Representation.HIDDEN: return self.__hide_all(super(LLCSecret, self).__str__())
237
+ case Representation.ASCII_HIDDEN: return self.__hide_all(cdt.VisibleString.__str__(self))
238
+ case _: return super(LLCSecret, self).__str__()
239
+
240
+
241
+ class LLCSecretHigh(LLCSecret):
242
+ DEFAULT = b'0000000000000000'
243
+
244
+ def validation(self):
245
+ """ check for length equal 16 """
246
+ if len(self) != 16:
247
+ raise ValueError(F'Got length of High secret: {len(self)}, expected 16')
248
+
249
+ def validate_from(self, value: str, cursor_position=None) -> tuple[str, int]:
250
+ try:
251
+ correct = type(self)(value)
252
+ return str(correct), cursor_position + (len(str(correct))-len(value))
253
+ except ValueError:
254
+ match self.representation & 0b1:
255
+ case Representation.HEX:
256
+ cursor_position: int = len(value)-1 if cursor_position is None else cursor_position
257
+ type(self)(F'{value[:cursor_position]}0{value[cursor_position:]}') # check possible
258
+ case Representation.ASCII:
259
+ type(self)(value.zfill(16))
260
+ return value, cursor_position
261
+
262
+
263
+ class AccessSelector(ut.Unsigned8):
264
+ """ Unsigned8 1..4 """
265
+ def __init__(self, value: int | str | ut.Unsigned8 = 1):
266
+ super(AccessSelector, self).__init__(value)
267
+ if int(self) > 4 or int(self) < 1:
268
+ raise ValueError(F'The {self.__class__.__name__} got {self}, expected 1..4')
269
+
270
+
271
+ class Data(ut.Data):
272
+ ELEMENTS = {1: ut.SequenceElement('All information', cdt.NullData),
273
+ 2: ut.SequenceElement('Access by class', ClassList),
274
+ 3: ut.SequenceElement('Access by object', ObjectIdList),
275
+ 4: ut.SequenceElement('Full object information', ObjectId)}
276
+
277
+
278
+ class SelectiveAccessDescriptor(ut.SelectiveAccessDescriptor):
279
+ """ Selective access specification always starts with an access selector, followed by an access-specific access parameter list.
280
+ Specified IS/IEC 62056-53 : 2006, 7.4.1.6 Selective access """
281
+ access_selector: AccessSelector
282
+ access_parameters: Data
283
+ ELEMENTS = (ut.SequenceElement('access_selector', AccessSelector),
284
+ ut.SequenceElement('access_parameters', Data))
285
+
286
+
287
+ class CosemAttributeDescriptorWithSelection(ut.CosemAttributeDescriptorWithSelection):
288
+ access_selection: SelectiveAccessDescriptor
289
+ ELEMENTS = (ut.SequenceElement('cosem_attribute_descriptor', ut.CosemAttributeDescriptor),
290
+ ut.SequenceElement('access_selection', SelectiveAccessDescriptor))
291
+
292
+
293
+ class AssociationLN(ic.COSEMInterfaceClasses):
294
+ """5.4.5 Association LN"""
295
+ CLASS_ID = ClassID.ASSOCIATION_LN
296
+ VERSION = VERSION_0
297
+ A_ELEMENTS = (ic.ICAElement("object_list", ObjectListType, selective_access=SelectiveAccessDescriptor),
298
+ ic.ICAElement("associated_partners_id", AssociatedPartnersType),
299
+ ic.ICAElement("application_context_name", ApplicationContextName),
300
+ ic.ICAElement("xDLMS_context_info", XDLMSContextType),
301
+ ic.ICAElement("authentication_mechanism_name", authentication_mechanism_name.AuthenticationMechanismName),
302
+ ic.ICAElement("LLS_secret", LLCSecret, classifier=ic.Classifier.NOT_SPECIFIC),
303
+ ic.ICAElement("association_status", AssociationStatus, classifier=ic.Classifier.DYNAMIC))
304
+ M_ELEMENTS = (ic.ICMElement("reply_to_HLS_authentication", method.ReplyToHLSAuthentication),
305
+ ic.ICMElement("change_HLS_secret", LLCSecret),
306
+ ic.ICMElement("add_object", ObjectListElement),
307
+ ic.ICMElement("remove_object", ObjectListElement))
308
+
309
+ def characteristics_init(self):
310
+ # self.object_list.selective_access = SelectiveAccessDescriptor()
311
+ self.set_attr(4, None)
312
+ self.set_attr(5, None)
313
+ self.set_attr(8, None)
314
+ # init secret after set authentication_mechanism_name(6)
315
+ self._cbs_attr_post_init.update({
316
+ 6: self.__init_secret,
317
+ 7: self.__check_mechanism_id_existing})
318
+
319
+ @property
320
+ def object_list(self) -> ObjectListType:
321
+ return self.get_attr(2)
322
+
323
+ @property
324
+ def associated_partners_id(self) -> AssociatedPartnersType:
325
+ return self.get_attr(3)
326
+
327
+ @property
328
+ def application_context_name(self) -> ApplicationContextName:
329
+ return self.get_attr(4)
330
+
331
+ @property
332
+ def xDLMS_context_info(self) -> XDLMSContextType:
333
+ return self.get_attr(5)
334
+
335
+ @property
336
+ def authentication_mechanism_name(self) -> authentication_mechanism_name.AuthenticationMechanismName:
337
+ return self.get_attr(6)
338
+
339
+ @property
340
+ def LLS_secret(self) -> LLCSecret:
341
+ return self.get_attr(7)
342
+
343
+ @property
344
+ def association_status(self) -> AssociationStatus:
345
+ return self.get_attr(8)
346
+
347
+ @property
348
+ def reply_to_HLS_authentication(self) -> method.ReplyToHLSAuthentication:
349
+ return self.get_meth(1)
350
+
351
+ @property
352
+ def change_HLS_secret(self) -> LLS_secret:
353
+ return self.get_meth(2)
354
+
355
+ @property
356
+ def add_object(self) -> ObjectListElement:
357
+ return self.get_meth(3)
358
+
359
+ @property
360
+ def remove_object(self) -> ObjectListElement:
361
+ return self.get_meth(4)
362
+
363
+ def __check_mechanism_id_existing(self):
364
+ """check for existing mechanism ID else ERASE setting"""
365
+ if self.authentication_mechanism_name is None:
366
+ self.clear_attr(7)
367
+ self._cbs_attr_post_init[7] = self.__check_mechanism_id_existing
368
+ else:
369
+ """nothing do it"""
370
+
371
+ def __init_secret(self):
372
+ """ before initiating secret need knowledge what kind of mechanism ID """
373
+ match self.authentication_mechanism_name.mechanism_id_element, self.LLS_secret:
374
+ case mechanism_id.NONE | mechanism_id.LOW, LLCSecret(): """keep secret value"""
375
+ case mechanism_id.NONE | mechanism_id.LOW, _: self.set_attr_link(7, LLCSecret())
376
+ case mechanism_id.HIGH, LLCSecretHigh(): """keep secret value"""
377
+ case mechanism_id.HIGH, _: self.set_attr_link(7, LLCSecretHigh())
378
+ case unknown, _: raise ValueError(F'Not support Secret with {unknown}')
379
+
380
+ def get_attr_descriptor(self,
381
+ value: int,
382
+ with_selection: bool = False) -> ut.CosemAttributeDescriptor | CosemAttributeDescriptorWithSelection:
383
+ """ with selection for object_list. TODO: Copypast ProfileGeneric"""
384
+ descriptor: ut.CosemAttributeDescriptor = super(AssociationLN, self).get_attr_descriptor(value)
385
+ if value == 2 and with_selection and self.object_list:
386
+ return CosemAttributeDescriptorWithSelection((descriptor, self.object_list.selective_access))
387
+ else:
388
+ return descriptor
389
+
390
+ def get_lns(self) -> list[cst.LogicalName]:
391
+ """return all LogicalNames"""
392
+ el: ObjectListElement
393
+ return [el.logical_name for el in self.object_list]
394
+
395
+ def iter_pars(self) -> Iterator[Parameter]:
396
+ return (Parameter(el.logical_name.contents) for el in self.object_list)
397
+
398
+ def __check_empty_object_list(self):
399
+ if self.object_list is None:
400
+ raise exc.ITEApplication(F"empty <{self.get_attr_element(2).NAME}> in {self}")
401
+
402
+ def is_readable(self,
403
+ ln: cst.LogicalName,
404
+ index: int,
405
+ security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
406
+ ) -> bool:
407
+ self.__check_empty_object_list()
408
+ match self.object_list.get_attr_access(ln, index):
409
+ case pdu.AttributeAccess.NO_ACCESS | pdu.AttributeAccess.WRITE_ONLY | pdu.AttributeAccess.AUTHENTICATED_WRITE_ONLY:
410
+ return False
411
+ case pdu.AttributeAccess.READ_ONLY | pdu.AttributeAccess.READ_AND_WRITE:
412
+ return True
413
+ case pdu.AttributeAccess.AUTHENTICATED_READ_ONLY | pdu.AttributeAccess.AUTHENTICATED_READ_AND_WRITE:
414
+ if isinstance(security_policy, pdu.SecurityPolicyVer0):
415
+ match security_policy:
416
+ case pdu.SecurityPolicyVer0.AUTHENTICATED | pdu.SecurityPolicyVer0.AUTHENTICATED_AND_ENCRYPTED:
417
+ return True
418
+ case _:
419
+ return False
420
+ elif isinstance(security_policy, pdu.SecurityPolicyVer1):
421
+ if bool(security_policy & (pdu.SecurityPolicyVer1.AUTHENTICATED_REQUEST | pdu.SecurityPolicyVer1.AUTHENTICATED_RESPONSE)):
422
+ return True
423
+ else:
424
+ return False
425
+ else:
426
+ raise TypeError(F"unknown {security_policy.__class__}: {security_policy}")
427
+ case err:
428
+ raise exc.ITEApplication(F"unsupport access: {err}")
429
+
430
+ def is_writable(self,
431
+ ln: cst.LogicalName,
432
+ index: int,
433
+ security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING
434
+ ) -> bool:
435
+ self.__check_empty_object_list()
436
+ return is_attr_writable(
437
+ mode=self.object_list.get_access_mode(ln, index),
438
+ security_policy=security_policy)
439
+
440
+ def is_accessible(self, ln: cst.LogicalName,
441
+ index: int,
442
+ m_id: mechanism_id.MechanismIdElement = None
443
+ ) -> bool:
444
+ """for ver 0 and 1 only"""
445
+ self.__check_empty_object_list()
446
+ match self.object_list.get_meth_access(ln, index):
447
+ case pdu.MethodAccess.NO_ACCESS:
448
+ return False
449
+ case pdu.MethodAccess.ACCESS:
450
+ return True
451
+ case pdu.MethodAccess.AUTHENTICATED_ACCESS:
452
+ if not m_id:
453
+ m_id = self.authentication_mechanism_name.mechanism_id_element
454
+ if m_id > mechanism_id.NONE:
455
+ return True
456
+ else:
457
+ return False
458
+ case err:
459
+ raise exc.ITEApplication(F"unsupport access: {err}")
460
+
461
+
462
+ def is_attr_writable(
463
+ mode: AccessMode,
464
+ security_policy: pdu.SecurityPolicy = pdu.SecurityPolicyVer0.NOTHING) -> bool:
465
+ match int(mode):
466
+ case pdu.AttributeAccess.NO_ACCESS | pdu.AttributeAccess.READ_ONLY | pdu.AttributeAccess.AUTHENTICATED_READ_ONLY:
467
+ return False
468
+ case pdu.AttributeAccess.WRITE_ONLY | pdu.AttributeAccess.READ_AND_WRITE:
469
+ return True
470
+ case pdu.AttributeAccess.AUTHENTICATED_WRITE_ONLY | pdu.AttributeAccess.AUTHENTICATED_READ_AND_WRITE:
471
+ if isinstance(security_policy, pdu.SecurityPolicyVer0):
472
+ match security_policy:
473
+ case pdu.SecurityPolicyVer0.AUTHENTICATED | pdu.SecurityPolicyVer0.AUTHENTICATED_AND_ENCRYPTED:
474
+ return True
475
+ case _:
476
+ return False
477
+ elif isinstance(security_policy, pdu.SecurityPolicyVer1):
478
+ if bool(security_policy & (pdu.SecurityPolicyVer1.AUTHENTICATED_REQUEST | pdu.SecurityPolicyVer1.AUTHENTICATED_RESPONSE)):
479
+ return True
480
+ else:
481
+ return False
482
+ else:
483
+ raise TypeError(F"unknown {security_policy.__class__}: {security_policy}")
484
+ case err:
485
+ raise exc.ITEApplication(F"unsupport access: {err}")