1
1
import re
2
- import typing
2
+ from typing import *
3
+ import attr
3
4
4
5
from iec62056_21 .exceptions import Iec6205621ParseError , ValidationError
5
6
from iec62056_21 import constants , utils
@@ -40,6 +41,7 @@ def from_bytes(cls, bytes_data):
40
41
return cls .from_representation (bytes_data .decode (constants .ENCODING ))
41
42
42
43
44
+ @attr .s (auto_attribs = True )
43
45
class DataSet (Iec6205621Data ):
44
46
45
47
"""
@@ -50,13 +52,9 @@ class DataSet(Iec6205621Data):
50
52
51
53
EXCLUDE_CHARS = ["(" , ")" , "/" , "!" ]
52
54
53
- def __init__ (self , value : str , address : str = None , unit : str = None ):
54
-
55
- # TODO: in programming mode, protocol mode C the value can be up to 128 chars
56
-
57
- self .address = address
58
- self .value = value
59
- self .unit = unit
55
+ value : str
56
+ address : Optional [str ] = attr .ib (default = None )
57
+ unit : Optional [str ] = attr .ib (default = None )
60
58
61
59
def to_representation (self ) -> str :
62
60
if self .unit is not None and self .address is not None :
@@ -70,7 +68,7 @@ def to_representation(self) -> str:
70
68
return f"({ self .value } )"
71
69
72
70
@classmethod
73
- def from_representation (cls , data_set_string ):
71
+ def from_representation (cls , data_set_string : str ):
74
72
just_value = regex_data_just_value .search (data_set_string )
75
73
76
74
if just_value :
@@ -92,30 +90,21 @@ def from_representation(cls, data_set_string):
92
90
else :
93
91
return cls (address = address , value = values_data , unit = None )
94
92
95
- def __repr__ (self ):
96
- return (
97
- f"{ self .__class__ .__name__ } ("
98
- f"value={ self .value !r} , "
99
- f"address={ self .address !r} , "
100
- f"unit={ self .unit !r} "
101
- f")"
102
- )
103
-
104
93
94
+ @attr .s (auto_attribs = True )
105
95
class DataLine (Iec6205621Data ):
106
96
"""
107
97
A data line is a list of data sets.
108
98
"""
109
99
110
- def __init__ (self , data_sets ):
111
- self .data_sets : typing .List [DataSet ] = data_sets
100
+ data_sets : List [DataSet ]
112
101
113
102
def to_representation (self ):
114
103
sets_representation = [_set .to_representation () for _set in self .data_sets ]
115
104
return "" .join (sets_representation )
116
105
117
106
@classmethod
118
- def from_representation (cls , string_data ):
107
+ def from_representation (cls , string_data : str ):
119
108
"""
120
109
Is a list of data sets id(value*unit)id(value*unit)
121
110
need to split after each ")"
@@ -132,20 +121,17 @@ def from_representation(cls, string_data):
132
121
133
122
return cls (data_sets = data_sets )
134
123
135
- def __repr__ (self ):
136
- return f"{ self .__class__ .__name__ } (" f"data_sets={ self .data_sets !r} " f")"
137
-
138
124
125
+ @attr .s (auto_attribs = True )
139
126
class DataBlock (Iec6205621Data ):
140
127
"""
141
128
A data block is a list of DataLines, each ended with a the line end characters
142
129
\n \r
143
130
"""
144
131
145
- def __init__ (self , data_lines ):
146
- self .data_lines = data_lines
132
+ data_lines : List [DataLine ]
147
133
148
- def to_representation (self ):
134
+ def to_representation (self ) -> str :
149
135
lines_rep = [
150
136
(line .to_representation () + constants .LINE_END ) for line in self .data_lines
151
137
]
@@ -157,15 +143,12 @@ def from_representation(cls, string_data: str):
157
143
data_lines = [DataLine .from_representation (line ) for line in lines ]
158
144
return cls (data_lines )
159
145
160
- def __repr__ (self ):
161
- return f"{ self .__class__ .__name__ } (data_lines={ self .data_lines !r} )"
162
-
163
146
147
+ @attr .s (auto_attribs = True )
164
148
class ReadoutDataMessage (Iec6205621Data ):
165
- def __init__ (self , data_block ):
166
- self .data_block = data_block
149
+ data_block : DataBlock
167
150
168
- def to_representation (self ):
151
+ def to_representation (self ) -> str :
169
152
data = (
170
153
f"{ constants .STX } { self .data_block .to_representation ()} { constants .END_CHAR } "
171
154
f"{ constants .LINE_END } { constants .ETX } "
@@ -186,27 +169,28 @@ def from_representation(cls, string_data: str):
186
169
187
170
return cls (data_block = data_block )
188
171
189
- def __repr__ (self ):
190
- return f"{ self .__class__ .__name__ } (data_block={ self .data_block !r} )"
191
-
192
172
173
+ @attr .s (auto_attribs = True )
193
174
class CommandMessage (Iec6205621Data ):
194
- allowed_commands = ["P" , "W" , "R" , "E" , "B" ]
195
- allowed_command_types = ["0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" ]
175
+ allowed_commands : ClassVar [List [str ]] = ["P" , "W" , "R" , "E" , "B" ]
176
+ allowed_command_types : ClassVar [List [str ]] = [
177
+ "0" ,
178
+ "1" ,
179
+ "2" ,
180
+ "3" ,
181
+ "4" ,
182
+ "5" ,
183
+ "6" ,
184
+ "7" ,
185
+ "8" ,
186
+ "9" ,
187
+ ]
188
+
189
+ command : str
190
+ command_type : str
191
+ data_set : Optional [DataSet ] = attr .ib (default = None )
196
192
197
- def __init__ (
198
- self , command : str , command_type : str , data_set : typing .Optional [DataSet ]
199
- ):
200
- self .command = command
201
- self .command_type = command_type
202
- self .data_set = data_set
203
-
204
- if command not in self .allowed_commands :
205
- raise ValueError (f"{ command } is not an allowed command" )
206
- if command_type not in self .allowed_command_types :
207
- raise ValueError (f"{ command_type } is not an allowed command type" )
208
-
209
- def to_representation (self ):
193
+ def to_representation (self ) -> str :
210
194
header = f"{ constants .SOH } { self .command } { self .command_type } "
211
195
if self .data_set :
212
196
body = f"{ constants .STX } { self .data_set .to_representation ()} { constants .ETX } "
@@ -245,36 +229,28 @@ def for_single_write(cls, address, value):
245
229
data_set = DataSet (value = value , address = address )
246
230
return cls (command = "W" , command_type = "1" , data_set = data_set )
247
231
248
- def __repr__ (self ):
249
- return (
250
- f"{ self .__class__ .__name__ } ("
251
- f"command={ self .command !r} , "
252
- f"command_type={ self .command_type !r} , "
253
- f"data_set={ self .data_set !r} "
254
- f")"
255
- )
256
-
257
232
233
+ @attr .s (auto_attribs = True )
258
234
class AnswerDataMessage (Iec6205621Data ):
259
- def __init__ ( self , data_block ):
260
- self . data_block = data_block
261
- self . _data = None
235
+
236
+ data_block : DataBlock
237
+ cached_data : Optional [ List [ DataSet ]] = attr . ib ( default = None , init = False )
262
238
263
239
@property
264
240
def data (self ):
265
- if not self ._data :
266
- self ._get_all_data_sets ()
241
+ if not self .cached_data :
242
+ self .get_all_data_sets ()
267
243
268
- return self ._data
244
+ return self .cached_data
269
245
270
- def _get_all_data_sets (self ):
246
+ def get_all_data_sets (self ):
271
247
data_sets = list ()
272
248
273
249
for line in self .data_block .data_lines :
274
- for set in line .data_sets :
275
- data_sets .append (set )
250
+ for data_set in line .data_sets :
251
+ data_sets .append (data_set )
276
252
277
- self ._data = data_sets
253
+ self .cached_data = data_sets
278
254
279
255
def to_representation (self ):
280
256
# TODO: this is not valid in case reading out partial blocks.
@@ -295,15 +271,12 @@ def from_representation(cls, string_data):
295
271
296
272
return cls (data_block = data_block )
297
273
298
- def __repr__ (self ):
299
- return f"{ self .__class__ .__name__ } (data_block={ self .data_block !r} )"
300
-
301
274
275
+ @attr .s (auto_attribs = True )
302
276
class RequestMessage (Iec6205621Data ):
303
- def __init__ (self , device_address = "" ):
304
- self .device_address = device_address
277
+ device_address : str = attr .ib (default = "" )
305
278
306
- def to_representation (self ):
279
+ def to_representation (self ) -> str :
307
280
return (
308
281
f"{ constants .START_CHAR } { constants .REQUEST_CHAR } { self .device_address } "
309
282
f"{ constants .END_CHAR } { constants .LINE_END } "
@@ -314,44 +287,32 @@ def from_representation(cls, string_data):
314
287
device_address = string_data [2 :- 3 ]
315
288
return cls (device_address )
316
289
317
- def __repr__ (self ):
318
- return f"{ self .__class__ .__name__ } (device_address={ self .device_address !r} )"
319
-
320
290
291
+ @attr .s (auto_attribs = True )
321
292
class AckOptionSelectMessage (Iec6205621Data ):
322
- """
323
- Only support protocol mode 0: Normal
324
- """
293
+ """"""
325
294
326
- def __init__ ( self , baud_char , mode_char ):
327
- self . baud_char = baud_char
328
- self . mode_char = mode_char
295
+ baud_char : str
296
+ mode_char : str
297
+ protocol_char : str = attr . ib ( default = "0" )
329
298
330
299
def to_representation (self ):
331
- return f"{ constants .ACK } 0 { self .baud_char } { self .mode_char } { constants .LINE_END } "
300
+ return f"{ constants .ACK } { self . protocol_char } { self .baud_char } { self .mode_char } { constants .LINE_END } "
332
301
333
302
@classmethod
334
303
def from_representation (cls , string_data ):
304
+ protocol_char = string_data [1 ]
335
305
baud_char = string_data [2 ]
336
306
mode_char = string_data [3 ]
337
- return cls (baud_char , mode_char )
338
-
339
- def __repr__ (self ):
340
- return (
341
- f"{ self .__class__ .__name__ } ("
342
- f"baud_char={ self .baud_char !r} , "
343
- f"mode_char={ self .mode_char !r} "
344
- f")"
345
- )
307
+ return cls (baud_char , mode_char , protocol_char = protocol_char )
346
308
347
309
310
+ @attr .s (auto_attribs = True )
348
311
class IdentificationMessage (Iec6205621Data ):
349
- def __init__ (
350
- self , identification : str , manufacturer : str , switchover_baudrate_char : str
351
- ):
352
- self .identification : str = identification
353
- self .manufacturer : str = manufacturer
354
- self .switchover_baudrate_char : str = switchover_baudrate_char
312
+
313
+ identification : str
314
+ manufacturer : str
315
+ switchover_baudrate_char : str
355
316
356
317
def to_representation (self ):
357
318
return (
@@ -366,12 +327,3 @@ def from_representation(cls, string_data):
366
327
identification = string_data [6 :- 2 ]
367
328
368
329
return cls (identification , manufacturer , switchover_baudrate_char )
369
-
370
- def __repr__ (self ):
371
- return (
372
- f"{ self .__class__ .__name__ } ("
373
- f"identification={ self .identification !r} , "
374
- f"manufacturer={ self .manufacturer !r} , "
375
- f"switchover_baudrate_char={ self .switchover_baudrate_char !r} "
376
- f")"
377
- )
0 commit comments