DLMS-SPODES-client 0.19.12__py3-none-any.whl → 0.19.14__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.
@@ -14,6 +14,7 @@ import datetime
14
14
  import os
15
15
  import hashlib
16
16
  from Cryptodome.Cipher import AES
17
+ from StructResult import result
17
18
  from DLMS_SPODES_communications import Network, Serial, RS485, BLEKPZ, base
18
19
  from DLMS_SPODES.cosem_interface_classes import overview
19
20
  from DLMS_SPODES.cosem_interface_classes.collection import Collection, InterfaceClass, ic, cdt, ut, Data, AssociationLN
@@ -192,9 +193,6 @@ class Client:
192
193
  self.connection_time_release = 10
193
194
  """ number of second for port release after inactivity """
194
195
 
195
- self.reply = GXReplyData()
196
- """ use gurux reply class now """
197
-
198
196
  self.received_frames = deque()
199
197
  """ HDLC frames container from server """
200
198
 
@@ -261,19 +259,19 @@ class Client:
261
259
  case BLEKPZ(): return 3
262
260
  case _: raise ValueError(F"can't calculate channel index by media: {self.media}")
263
261
 
264
- def get_frame(self, read_data: bytearray) -> frame.Frame | None:
262
+ def get_frame(self, read_data: bytearray, reply: GXReplyData) -> frame.Frame | None:
265
263
  while len(read_data) != 0:
266
264
  new_frame = frame.Frame.try_from(read_data)
267
- self.reply.complete = True
265
+ reply.complete = True
268
266
  if new_frame is None:
269
- self.reply.complete = False
267
+ reply.complete = False
270
268
  return None
271
269
  if new_frame.is_for_me(self.DA, self.SA):
272
270
  self.received_frames.append(new_frame)
273
271
  if new_frame.is_segmentation:
274
- self.reply.moreData |= RequestTypes.FRAME
272
+ reply.moreData |= RequestTypes.FRAME
275
273
  else:
276
- self.reply.moreData &= ~RequestTypes.FRAME
274
+ reply.moreData &= ~RequestTypes.FRAME
277
275
  # check control TODO: rewrite it
278
276
  if new_frame.control.is_unnumbered():
279
277
  if new_frame.control in (frame.Control.UA_F, frame.Control.SNRM_P):
@@ -306,234 +304,244 @@ class Client:
306
304
  self.log(logL.WARN, F"ALIEN frame {new_frame}, expect with SA:{self.SA}")
307
305
  # FROM GURUX - if new_frame.control == frame.Control.UI_PF: # search next frame in read_data
308
306
 
309
- def handleGbt(self):
310
- index = self.reply.data.position - 1
311
- self.reply.windowSize = self.settings.windowSize
312
- bc = self.reply.data.getUInt8()
313
- self.reply.streaming = (bc & 0x40) != 0
307
+ def handleGbt(self, reply: GXReplyData) -> result.Ok | result.Error:
308
+ index = reply.data.position - 1
309
+ reply.windowSize = self.settings.windowSize
310
+ bc = reply.data.getUInt8()
311
+ reply.streaming = (bc & 0x40) != 0
314
312
  windowSize = int(bc & 0x3F)
315
- bn = self.reply.data.getUInt16()
316
- bna = self.reply.data.getUInt16()
317
- self.reply.blockNumber = bn
318
- self.reply.blockNumberAck = bna
319
- self.settings.blockNumberAck = self.reply.blockNumber
320
- self.reply.command = None
321
- len_ = _GXCommon.getObjectCount(self.reply.data)
322
- if len_ > self.reply.data.size - self.reply.data.position:
323
- self.reply.complete = False
313
+ bn = reply.data.getUInt16()
314
+ bna = reply.data.getUInt16()
315
+ reply.blockNumber = bn
316
+ reply.blockNumberAck = bna
317
+ self.settings.blockNumberAck = reply.blockNumber
318
+ reply.command = None
319
+ len_ = _GXCommon.getObjectCount(reply.data)
320
+ if len_ > reply.data.size - reply.data.position:
321
+ reply.complete = False
324
322
  return
325
- GXDLMS.getDataFromBlock(self.reply.data, index)
323
+ GXDLMS.getDataFromBlock(reply.data, index)
326
324
  if (bc & 0x80) == 0:
327
- self.reply.moreData = (RequestTypes(self.reply.moreData | RequestTypes.GBT))
325
+ reply.moreData = (RequestTypes(reply.moreData | RequestTypes.GBT))
328
326
  else:
329
- self.reply.moreData = (RequestTypes(self.reply.moreData & ~RequestTypes.GBT))
330
- if self.reply.data.size != 0:
331
- self.reply.data.position = 0
332
- self.getPdu()
333
- # if self.reply.data.position != self.reply.data.size and (self.reply.command == XDLMSAPDU.READ_RESPONSE or self.reply.command == XDLMSAPDU.GET_RESPONSE) and (self.reply.moreData == RequestTypes.NONE or self.reply.peek):
334
- # self.reply.data.position = 0
335
- # cls.getValueFromData(settings, self.reply)
336
-
337
- def getPdu(self) -> Any:
327
+ reply.moreData = (RequestTypes(reply.moreData & ~RequestTypes.GBT))
328
+ if reply.data.size != 0:
329
+ reply.data.position = 0
330
+ if isinstance(res_pdu := self.getPdu(), result.Error):
331
+ return res_pdu.with_msg("handle GBT")
332
+ # if reply.data.position != reply.data.size and (reply.command == XDLMSAPDU.READ_RESPONSE or reply.command == XDLMSAPDU.GET_RESPONSE) and (reply.moreData == RequestTypes.NONE or reply.peek):
333
+ # reply.data.position = 0
334
+ # cls.getValueFromData(settings, reply)
335
+ return result.OK
336
+
337
+ def getPdu(self, reply: GXReplyData) -> result.Ok | result.Error:
338
338
  # TODO: make return pdu
339
- if self.reply.command is None:
340
- if self.reply.data.size - self.reply.data.position == 0:
341
- raise ValueError("Invalid PDU")
342
- index = self.reply.data.position
343
- self.reply.command = XDLMSAPDU(self.reply.data.getUInt8())
344
- match self.reply.command:
339
+ if reply.command is None:
340
+ if reply.data.size - reply.data.position == 0:
341
+ return result.Error(ValueError("Invalid PDU"), "getpdu")
342
+ index = reply.data.position
343
+ reply.command = XDLMSAPDU(reply.data.getUInt8())
344
+ match reply.command:
345
345
  case XDLMSAPDU.GET_RESPONSE:
346
- response_type: int = self.reply.data.getUInt8()
347
- invoke_id_and_priority = self.reply.data.getUInt8() # TODO: matching with setting params
346
+ response_type: int = reply.data.getUInt8()
347
+ invoke_id_and_priority = reply.data.getUInt8() # TODO: matching with setting params
348
348
  match response_type:
349
349
  case pdu.GetResponse.NORMAL:
350
- match self.reply.data.getUInt8(): # Get-Data-Result[0]
351
- case 0: GXDLMS.getDataFromBlock(self.reply.data, 0)
350
+ match reply.data.getUInt8(): # Get-Data-Result[0]
351
+ case 0:
352
+ GXDLMS.getDataFromBlock(reply.data, 0)
352
353
  case 1:
353
- self.reply.error = pdu.DataAccessResult(self.reply.data.getUInt8())
354
- if self.reply.error != 0:
355
- raise exc.ResultError(self.reply.error)
356
- case err: raise ValueError(F'Got Get-Data-Result[0] {err}, expect 0 or 1')
357
- GXDLMS.getDataFromBlock(self.reply.data, 0)
354
+ reply.error = pdu.DataAccessResult(reply.data.getUInt8())
355
+ if reply.error != 0:
356
+ return result.Error.from_e(exc.ResultError(reply.error), "get pdu")
357
+ case err:
358
+ return result.Error.from_e(ValueError(F'Got Get-Data-Result[0] {err}, expect 0 or 1'), "get pdu")
359
+ GXDLMS.getDataFromBlock(reply.data, 0)
358
360
  case pdu.GetResponse.WITH_DATABLOCK:
359
- last_block = self.reply.data.getUInt8()
361
+ last_block = reply.data.getUInt8()
360
362
  if last_block == 0:
361
- self.reply.moreData |= RequestTypes.DATABLOCK
363
+ reply.moreData |= RequestTypes.DATABLOCK
362
364
  else:
363
- self.reply.moreData &= ~RequestTypes.DATABLOCK
364
- block_number = self.reply.data.getUInt32()
365
+ reply.moreData &= ~RequestTypes.DATABLOCK
366
+ block_number = reply.data.getUInt32()
365
367
  if block_number == 0 and self.settings.blockIndex == 1: # if start block_index == 0
366
368
  self.settings.setBlockIndex(0)
367
369
  if block_number != self.settings.blockIndex:
368
- raise ValueError(F"Invalid Block number. It is {block_number} and it should be {self.settings.blockIndex}.")
369
- match self.reply.data.getUInt8(): # DataBlock-G.result
370
+ return result.Error.from_e(ValueError(F"Invalid Block number. It is {block_number} and it should be {self.settings.blockIndex}."), "get pdu")
371
+ match reply.data.getUInt8(): # DataBlock-G.result,
370
372
  case 0:
371
- if self.reply.data.position != len(self.reply.data):
372
- block_length = _GXCommon.getObjectCount(self.reply.data)
373
- if (self.reply.moreData & RequestTypes.FRAME) == 0:
374
- if block_length > len(self.reply.data) - self.reply.data.position:
375
- raise ValueError("Invalid block length.")
376
- self.reply.command = None
373
+ if reply.data.position != len(reply.data):
374
+ block_length = _GXCommon.getObjectCount(reply.data)
375
+ if (reply.moreData & RequestTypes.FRAME) == 0:
376
+ if block_length > len(reply.data) - reply.data.position:
377
+ return result.Error.from_e(ValueError("Invalid block length."), "get pdu")
378
+ reply.command = None
377
379
  if block_length == 0:
378
- self.reply.data.size = index
380
+ reply.data.size = index
379
381
  else:
380
- GXDLMS.getDataFromBlock(self.reply.data, index)
381
- if self.reply.moreData == RequestTypes.NONE:
382
- if not self.reply.peek:
383
- self.reply.data.position = 0
382
+ GXDLMS.getDataFromBlock(reply.data, index)
383
+ if reply.moreData == RequestTypes.NONE:
384
+ if not reply.peek:
385
+ reply.data.position = 0
384
386
  self.settings.resetBlockIndex()
385
- if self.reply.moreData == RequestTypes.NONE and self.settings and self.settings.command == XDLMSAPDU.GET_REQUEST \
387
+ if reply.moreData == RequestTypes.NONE and self.settings and self.settings.command == XDLMSAPDU.GET_REQUEST \
386
388
  and self.settings.commandType == pdu.GetResponse.WITH_LIST:
387
- GXDLMS.handleGetResponseWithList(self.settings, self.reply)
388
- return
389
+ GXDLMS.handleGetResponseWithList(self.settings, reply)
390
+ return result.OK
389
391
  case 1:
390
- self.reply.error = pdu.DataAccessResult(self.reply.data.getUInt8())
391
- if self.reply.error != 0:
392
- raise exc.ResultError(self.reply.error)
393
- case err: raise ValueError(F'Got DataBlock-G.result {err}, expect 0 or 1')
392
+ reply.error = pdu.DataAccessResult(reply.data.getUInt8())
393
+ if reply.error != 0:
394
+ return result.Error.from_e(exc.ResultError(reply.error), "get pdu")
395
+ case err:
396
+ return result.Error.from_e(ValueError(F'Got DataBlock-G.result {err}, expect 0 or 1'), "get pdu")
394
397
  case pdu.GetResponse.WITH_LIST:
395
- GXDLMS.handleGetResponseWithList(self.settings, self.reply)
396
- return
397
- case err: raise ValueError(F"Got Invalid Get response {err}, expect {', '.join(map(lambda it: F'{it.name} = {it.value}', pdu.GetResponse))}")
398
+ GXDLMS.handleGetResponseWithList(self.settings, reply)
399
+ return result.OK
400
+ case err:
401
+ return result.Error.from_e(ValueError(F"Got Invalid Get response {err}, expect {', '.join(map(lambda it: F'{it.name} = {it.value}', pdu.GetResponse))}"), "get pdu")
398
402
  case XDLMSAPDU.READ_RESPONSE:
399
- if not GXDLMS.handleReadResponse(self.settings, self.reply, index):
400
- return
403
+ if not GXDLMS.handleReadResponse(self.settings, reply, index):
404
+ return result.OK
401
405
  case XDLMSAPDU.SET_RESPONSE:
402
- response_type: int = self.reply.data.getUInt8()
403
- invoke_id_and_priority = self.reply.data.getUInt8() # TODO: matching with setting params
406
+ response_type: int = reply.data.getUInt8()
407
+ invoke_id_and_priority = reply.data.getUInt8() # TODO: matching with setting params
404
408
  match response_type:
405
409
  case pdu.SetResponse.NORMAL:
406
- self.reply.error = pdu.DataAccessResult(self.reply.data.getUInt8())
407
- if self.reply.error != 0:
408
- raise exc.ResultError(self.reply.error)
410
+ reply.error = pdu.DataAccessResult(reply.data.getUInt8())
411
+ if reply.error != 0:
412
+ return result.Error.from_e(exc.ResultError(reply.error), "get pdu")
409
413
  case pdu.SetResponse.DATABLOCK:
410
- block_number = self.reply.data.getUInt32()
414
+ block_number = reply.data.getUInt32()
411
415
  case pdu.SetResponse.LAST_DATABLOCK:
412
- self.reply.error = pdu.DataAccessResult(self.reply.data.getUInt8())
413
- if self.reply.error != 0:
414
- raise exc.ResultError(self.reply.error)
415
- block_number = self.reply.data.getUInt32()
416
+ reply.error = pdu.DataAccessResult(reply.data.getUInt8())
417
+ if reply.error != 0:
418
+ return result.Error.from_e(exc.ResultError(reply.error), "get pdu")
419
+ block_number = reply.data.getUInt32()
416
420
  case pdu.SetResponse.LAST_DATABLOCK_WITH_LIST:
417
- raise ValueError("Not released in Client")
421
+ raise RuntimeError("Not released in Client")
418
422
  case pdu.SetResponse.WITH_LIST:
419
- cnt = _GXCommon.getObjectCount(self.reply.data)
423
+ cnt = _GXCommon.getObjectCount(reply.data)
420
424
  pos = 0
421
425
  while pos != cnt:
422
- self.reply.error = pdu.DataAccessResult(self.reply.data.getUInt8())
423
- if self.reply.error != 0:
424
- raise exc.ResultError(self.reply.error)
426
+ reply.error = pdu.DataAccessResult(reply.data.getUInt8())
427
+ if reply.error != 0:
428
+ return result.Error.from_e(exc.ResultError(reply.error), "get pdu")
425
429
  pos += 1
426
- case err: raise ValueError(F"Got Invalid Set response {err}, expect {', '.join(map(lambda it: F'{it.name} = {it.value}', pdu.SetResponse))}")
430
+ case err:
431
+ return result.Error.from_e(ValueError(F"Got Invalid Set response {err}, expect {', '.join(map(lambda it: F'{it.name} = {it.value}', pdu.SetResponse))}"), "get pdu")
427
432
  case XDLMSAPDU.WRITE_RESPONSE:
428
- cnt = _GXCommon.getObjectCount(self.reply.data)
433
+ cnt = _GXCommon.getObjectCount(reply.data)
429
434
  pos = 0
430
435
  while pos != cnt:
431
- ret = self.reply.data.getUInt8()
436
+ ret = reply.data.getUInt8()
432
437
  if ret != 0:
433
- self.reply.error = self.reply.data.getUInt8()
438
+ reply.error = reply.data.getUInt8()
434
439
  pos += 1
435
440
  case XDLMSAPDU.ACTION_RESPONSE:
436
- action_response = self.reply.data.getUInt8()
437
- invoke_id_and_priority = self.reply.data.getUInt8()
441
+ action_response = reply.data.getUInt8()
442
+ invoke_id_and_priority = reply.data.getUInt8()
438
443
  match action_response:
439
444
  case pdu.ActionResponse.NORMAL:
440
- self.reply.error = pdu.ActionResult(self.reply.data.getUInt8())
441
- if self.reply.error != 0:
442
- raise exc.ResultError(self.reply.error)
443
- if self.reply.data.position < self.reply.data.size:
444
- ret = self.reply.data.getUInt8()
445
+ reply.error = pdu.ActionResult(reply.data.getUInt8())
446
+ if reply.error != 0:
447
+ return result.Error(exc.ResultError(reply.error), "get pdu")
448
+ if reply.data.position < reply.data.size:
449
+ ret = reply.data.getUInt8()
445
450
  if ret == 0:
446
- GXDLMS.getDataFromBlock(self.reply.data, 0)
451
+ GXDLMS.getDataFromBlock(reply.data, 0)
447
452
  elif ret == 1:
448
- ret = int(self.reply.data.getUInt8())
453
+ ret = int(reply.data.getUInt8())
449
454
  if ret != 0:
450
- self.reply.error = self.reply.data.getUInt8()
451
- if ret == 9 and self.reply.getError() == 16:
452
- self.reply.data.position = self.reply.data.position - 2
453
- GXDLMS.getDataFromBlock(self.reply.data, 0)
454
- self.reply.error = 0
455
+ reply.error = reply.data.getUInt8()
456
+ if ret == 9 and reply.error == 16:
457
+ reply.data.position = reply.data.position - 2
458
+ GXDLMS.getDataFromBlock(reply.data, 0)
459
+ reply.error = 0
455
460
  ret = 0
456
461
  else:
457
- GXDLMS.getDataFromBlock(self.reply.data, 0)
462
+ GXDLMS.getDataFromBlock(reply.data, 0)
458
463
  else:
459
- raise Exception("HandleActionResponseNormal failed. " + "Invalid tag.")
464
+ return result.Error.from_e(Exception("HandleActionResponseNormal failed. " + "Invalid tag."), "get pdu")
460
465
  case pdu.ActionResponse.WITH_PBLOCK:
461
- raise ValueError("Not released in Client")
466
+ raise RuntimeError("Not released in Client")
462
467
  case pdu.ActionResponse.WITH_LIST:
463
- raise ValueError("Not released in Client")
468
+ raise RuntimeError("Not released in Client")
464
469
  case pdu.ActionResponse.NEXT_PBLOCK:
465
- raise ValueError("Not released in Client")
470
+ raise RuntimeError("Not released in Client")
466
471
  case err:
467
- raise ValueError(F"got {pdu.ActionResponse}: {err}, expect {', '.join(map(lambda it: F'{it.name} = {it.value}', pdu.ActionResponse))}")
472
+ return result.Error.from_e(ValueError(F"got {pdu.ActionResponse}: {err}, expect {', '.join(map(lambda it: F'{it.name} = {it.value}', pdu.ActionResponse))}"), "get pdu")
468
473
  case XDLMSAPDU.ACCESS_RESPONSE:
469
- data = self.reply.data
470
- invokeId = self.reply.data.getUInt32()
471
- len_ = self.reply.data.getUInt8()
474
+ data = reply.data
475
+ invokeId = reply.data.getUInt32()
476
+ len_ = reply.data.getUInt8()
472
477
  tmp = None
473
478
  if len_ != 0:
474
479
  tmp = bytearray(len_)
475
480
  data.get(tmp)
476
- self.reply.time = _GXCommon.changeType(self.settings, tmp, DataType.DATETIME)
481
+ reply.time = _GXCommon.changeType(self.settings, tmp, DataType.DATETIME)
477
482
  data.getUInt8()
478
483
  case XDLMSAPDU.GENERAL_BLOCK_TRANSFER:
479
- if not self.settings.isServer and (self.reply.moreData & RequestTypes.FRAME) == 0:
480
- self.handleGbt()
484
+ if not self.settings.isServer and (reply.moreData & RequestTypes.FRAME) == 0:
485
+ if isinstance(res_gbt := self.handleGbt(reply), result.Error):
486
+ return res_gbt
481
487
  case ACSEAPDU.AARQ | ACSEAPDU.AARE:
482
488
  # This is parsed later.
483
- self.reply.data.position = self.reply.data.position - 1
489
+ reply.data.position = reply.data.position - 1
484
490
  case ACSEAPDU.RLRE | ACSEAPDU.RLRQ:
485
491
  pass
486
492
  case XDLMSAPDU.CONFIRMED_SERVICE_ERROR:
487
- GXDLMS.handleConfirmedServiceError(self.reply)
493
+ GXDLMS.handleConfirmedServiceError(reply)
488
494
  case XDLMSAPDU.EXCEPTION_RESPONSE:
489
- GXDLMS.handleExceptionResponse(self.reply)
495
+ GXDLMS.handleExceptionResponse(reply)
490
496
  case XDLMSAPDU.GET_REQUEST | XDLMSAPDU.READ_REQUEST | XDLMSAPDU.WRITE_REQUEST | XDLMSAPDU.SET_REQUEST | XDLMSAPDU.ACTION_REQUEST:
491
497
  pass
492
498
  case XDLMSAPDU.GLO_READ_REQUEST | XDLMSAPDU.GLO_WRITE_REQUEST | XDLMSAPDU.GLO_GET_REQUEST | XDLMSAPDU.GLO_SET_REQUEST | XDLMSAPDU.GLO_ACTION_REQUEST | \
493
499
  XDLMSAPDU.DED_GET_REQUEST | XDLMSAPDU.DED_SET_REQUEST | XDLMSAPDU.DED_ACTION_REQUEST:
494
500
  if self.settings.cipher is None:
495
- raise ValueError("Secure connection is not supported.")
496
- if (self.reply.moreData & RequestTypes.FRAME) == 0:
497
- self.reply.data.position = self.reply.data.position - 1
501
+ return result.Error.from_e(ServiceError("Secure connection is not supported."), "get pdu")
502
+ if (reply.moreData & RequestTypes.FRAME) == 0:
503
+ reply.data.position = reply.data.position - 1
498
504
  p = None
499
505
  if self.settings.cipher.dedicatedKey and (OSI.APPLICATION in self.level):
500
506
  p = AesGcmParameter(self.settings.sourceSystemTitle, self.settings.cipher.dedicatedKey, self.settings.cipher.authenticationKey)
501
507
  else:
502
508
  p = AesGcmParameter(self.settings.sourceSystemTitle, self.settings.cipher.blockCipherKey, self.settings.cipher.authenticationKey)
503
- tmp = GXCiphering.decrypt(self.settings.cipher, p, self.reply.data)
504
- self.reply.data.clear()
505
- self.reply.data.set(tmp)
506
- self.reply.command = XDLMSAPDU(self.reply.data.getUInt8())
507
- if self.reply.command == XDLMSAPDU.DATA_NOTIFICATION or self.reply.command == XDLMSAPDU.INFORMATION_REPORT_REQUEST:
508
- self.reply.command = None
509
- self.reply.data.position = self.reply.data.position - 1
510
- self.getPdu()
509
+ tmp = GXCiphering.decrypt(self.settings.cipher, p, reply.data)
510
+ reply.data.clear()
511
+ reply.data.set(tmp)
512
+ reply.command = XDLMSAPDU(reply.data.getUInt8())
513
+ if reply.command == XDLMSAPDU.DATA_NOTIFICATION or reply.command == XDLMSAPDU.INFORMATION_REPORT_REQUEST:
514
+ reply.command = None
515
+ reply.data.position = reply.data.position - 1
516
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
517
+ return res_pdu
511
518
  else:
512
- self.reply.data.position = self.reply.data.position - 1
519
+ reply.data.position = reply.data.position - 1
513
520
  case XDLMSAPDU.GLO_READ_RESPONSE | XDLMSAPDU.GLO_WRITE_RESPONSE | XDLMSAPDU.GLO_GET_RESPONSE | XDLMSAPDU.GLO_SET_RESPONSE | XDLMSAPDU.GLO_ACTION_RESPONSE | \
514
521
  XDLMSAPDU.GENERAL_GLO_CIPHERING | XDLMSAPDU.GLO_EVENT_NOTIFICATION_REQUEST | XDLMSAPDU.DED_GET_RESPONSE | XDLMSAPDU.DED_SET_RESPONSE | \
515
522
  XDLMSAPDU.DED_ACTION_RESPONSE | XDLMSAPDU.GENERAL_DED_CIPHERING | XDLMSAPDU.DED_EVENT_NOTIFICATION_REQUEST:
516
523
  if self.settings.cipher is None:
517
- raise ValueError("Secure connection is not supported.")
518
- if (self.reply.moreData & RequestTypes.FRAME) == 0:
519
- self.reply.data.position = self.reply.data.position - 1
520
- bb = GXByteBuffer(self.reply.data)
521
- self.reply.data.size = self.reply.data.position = index
524
+ return result.Error.from_e(ServiceError("Secure connection is not supported."), "get pdu")
525
+ if (reply.moreData & RequestTypes.FRAME) == 0:
526
+ reply.data.position = reply.data.position - 1
527
+ bb = GXByteBuffer(reply.data)
528
+ reply.data.size = reply.data.position = index
522
529
  p = None
523
530
  if self.settings.cipher.dedicatedKey and (OSI.APPLICATION in self.level):
524
531
  p = AesGcmParameter(0, self.settings.sourceSystemTitle, self.settings.cipher.dedicatedKey, self.settings.cipher.authenticationKey)
525
532
  else:
526
533
  p = AesGcmParameter(0, self.settings.sourceSystemTitle, self.settings.cipher.blockCipherKey, self.settings.cipher.authenticationKey)
527
- self.reply.data.set(GXCiphering.decrypt(self.settings.cipher, p, bb))
528
- self.reply.command = None
529
- self.getPdu()
530
- self.reply.cipherIndex = self.reply.data.size
534
+ reply.data.set(GXCiphering.decrypt(self.settings.cipher, p, bb))
535
+ reply.command = None
536
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
537
+ return res_pdu
538
+ reply.cipherIndex = reply.data.size
531
539
  case XDLMSAPDU.DATA_NOTIFICATION:
532
- GXDLMS.handleDataNotification(self.settings, self.reply)
533
- data = self.reply.data
540
+ GXDLMS.handleDataNotification(self.settings, reply)
541
+ data = reply.data
534
542
  start = data.position - 1
535
543
  invokeId = data.getUInt32()
536
- self.reply.time = None
544
+ reply.time = None
537
545
  len_ = data.getUInt8()
538
546
  tmp = None
539
547
  if len_ != 0:
@@ -546,103 +554,121 @@ class Client:
546
554
  dt = DataType.DATE
547
555
  info = _GXDataInfo()
548
556
  info.type_ = dt
549
- self.reply.time = _GXCommon.getData(self.settings, GXByteBuffer(tmp), info)
550
- GXDLMS.getDataFromBlock(self.reply.data, start)
551
- GXDLMS.getValueFromData(self.settings, self.reply)
557
+ reply.time = _GXCommon.getData(self.settings, GXByteBuffer(tmp), info)
558
+ GXDLMS.getDataFromBlock(reply.data, start)
559
+ GXDLMS.getValueFromData(self.settings, reply)
552
560
  case XDLMSAPDU.EVENT_NOTIFICATION_REQUEST:
553
561
  pass
554
562
  case XDLMSAPDU.INFORMATION_REPORT_REQUEST:
555
563
  pass
556
564
  case XDLMSAPDU.GENERAL_CIPHERING:
557
565
  if self.settings.cipher is None:
558
- raise ValueError("Secure connection is not supported.")
559
- if (self.reply.moreData & RequestTypes.FRAME) == 0:
560
- self.reply.data.position = self.reply.data.position - 1
566
+ return result.Error.from_e(ServiceError("Secure connection is not supported."), "get pdu")
567
+ if (reply.moreData & RequestTypes.FRAME) == 0:
568
+ reply.data.position = reply.data.position - 1
561
569
  p = AesGcmParameter(0, self.settings.sourceSystemTitle, self.settings.cipher.blockCipherKey, self.settings.cipher.authenticationKey)
562
- tmp = GXCiphering.decrypt(self.settings.cipher, p, self.reply.data)
563
- self.reply.data.clear()
564
- self.reply.data.set(tmp)
565
- self.reply.command = None
570
+ tmp = GXCiphering.decrypt(self.settings.cipher, p, reply.data)
571
+ reply.data.clear()
572
+ reply.data.set(tmp)
573
+ reply.command = None
566
574
  if p.security:
567
- self.getPdu()
575
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
576
+ return res_pdu
568
577
  case XDLMSAPDU.GATEWAY_REQUEST:
569
578
  pass
570
579
  case XDLMSAPDU.GATEWAY_RESPONSE:
571
- self.reply.data.getUInt8()
572
- len_ = _GXCommon.getObjectCount(self.reply.data)
580
+ reply.data.getUInt8()
581
+ len_ = _GXCommon.getObjectCount(reply.data)
573
582
  pda = bytearray(len_)
574
- self.reply.data.get(pda)
575
- GXDLMS.getDataFromBlock(self.reply, index)
576
- self.reply.command = None
577
- self.getPdu()
578
- case _: raise ValueError("Invalid PDU Command.")
579
- elif (self.reply.moreData & RequestTypes.FRAME) == 0:
580
- if not self.reply.peek and self.reply.moreData == RequestTypes.NONE:
581
- if self.reply.command == ACSEAPDU.AARE or self.reply.command == ACSEAPDU.AARQ:
582
- self.reply.data.position = 0
583
+ reply.data.get(pda)
584
+ GXDLMS.getDataFromBlock(reply, index)
585
+ reply.command = None
586
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
587
+ return res_pdu
588
+ case _:
589
+ return result.Error.from_e(ValueError("Invalid PDU Command."), "get pdu")
590
+ elif (reply.moreData & RequestTypes.FRAME) == 0:
591
+ if not reply.peek and reply.moreData == RequestTypes.NONE:
592
+ if reply.command == ACSEAPDU.AARE or reply.command == ACSEAPDU.AARQ:
593
+ reply.data.position = 0
583
594
  else:
584
- self.reply.data.position = 1
585
- if self.reply.command == XDLMSAPDU.GENERAL_BLOCK_TRANSFER:
586
- self.reply.data.position = self.reply.cipherIndex + 1
587
- self.handleGbt()
588
- self.reply.cipherIndex = self.reply.data.size
589
- self.reply.command = None
595
+ reply.data.position = 1
596
+ if reply.command == XDLMSAPDU.GENERAL_BLOCK_TRANSFER:
597
+ reply.data.position = reply.cipherIndex + 1
598
+ if isinstance(res_gbt := self.handleGbt(reply), result.Error):
599
+ return res_gbt
600
+ reply.cipherIndex = reply.data.size
601
+ reply.command = None
590
602
  elif self.settings.isServer:
591
- if self.reply.command in (
603
+ if reply.command in (
592
604
  XDLMSAPDU.GLO_READ_REQUEST, XDLMSAPDU.GLO_WRITE_REQUEST, XDLMSAPDU.GLO_GET_REQUEST, XDLMSAPDU.GLO_SET_REQUEST, XDLMSAPDU.GLO_ACTION_REQUEST,
593
605
  XDLMSAPDU.GLO_EVENT_NOTIFICATION_REQUEST, XDLMSAPDU.DED_GET_REQUEST, XDLMSAPDU.DED_SET_REQUEST, XDLMSAPDU.DED_ACTION_REQUEST,
594
606
  XDLMSAPDU.DED_EVENT_NOTIFICATION_REQUEST):
595
- self.reply.command = None
596
- self.reply.data.position = self.reply.getCipherIndex()
597
- self.getPdu()
607
+ reply.command = None
608
+ reply.data.position = reply.getCipherIndex()
609
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
610
+ return res_pdu
598
611
  else:
599
- self.reply.command = None
600
- if self.reply.command in (
601
- XDLMSAPDU.GLO_READ_RESPONSE, XDLMSAPDU.GLO_WRITE_RESPONSE, XDLMSAPDU.GLO_GET_RESPONSE, XDLMSAPDU.GLO_SET_RESPONSE, XDLMSAPDU.GLO_ACTION_RESPONSE,
602
- XDLMSAPDU.DED_GET_RESPONSE, XDLMSAPDU.DED_SET_RESPONSE, XDLMSAPDU.DED_ACTION_RESPONSE, XDLMSAPDU.GENERAL_GLO_CIPHERING, XDLMSAPDU.GENERAL_DED_CIPHERING):
603
- self.reply.data.position = self.reply.cipherIndex
604
- self.getPdu()
605
- if self.reply.command == XDLMSAPDU.READ_RESPONSE and self.reply.totalCount > 1:
606
- if not GXDLMS.handleReadResponse(self.settings, self.reply, 0):
607
- return
612
+ reply.command = None
613
+ if reply.command in (
614
+ XDLMSAPDU.GLO_READ_RESPONSE,
615
+ XDLMSAPDU.GLO_WRITE_RESPONSE,
616
+ XDLMSAPDU.GLO_GET_RESPONSE,
617
+ XDLMSAPDU.GLO_SET_RESPONSE,
618
+ XDLMSAPDU.GLO_ACTION_RESPONSE,
619
+ XDLMSAPDU.DED_GET_RESPONSE,
620
+ XDLMSAPDU.DED_SET_RESPONSE,
621
+ XDLMSAPDU.DED_ACTION_RESPONSE,
622
+ XDLMSAPDU.GENERAL_GLO_CIPHERING,
623
+ XDLMSAPDU.GENERAL_DED_CIPHERING
624
+ ):
625
+ reply.data.position = reply.cipherIndex
626
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
627
+ return res_pdu
628
+ if (
629
+ reply.command == XDLMSAPDU.READ_RESPONSE
630
+ and reply.totalCount > 1
631
+ ):
632
+ if not GXDLMS.handleReadResponse(self.settings, reply, 0):
633
+ return result.OK
608
634
 
609
635
  if (
610
- self.reply.command == XDLMSAPDU.READ_RESPONSE
611
- and self.reply.commandType == ReadResponse.DATA_BLOCK_RESULT
612
- and (self.reply.moreData & RequestTypes.FRAME) != 0
636
+ reply.command == XDLMSAPDU.READ_RESPONSE
637
+ and reply.commandType == ReadResponse.DATA_BLOCK_RESULT
638
+ and (reply.moreData & RequestTypes.FRAME) != 0
613
639
  ):
614
- return
640
+ return result.OK
615
641
  if (
616
- self.reply.data.position != self.reply.data.size
642
+ reply.data.position != reply.data.size
617
643
  and (
618
- self.reply.moreData == RequestTypes.NONE
619
- or self.reply.peek)
620
- and self.reply.command in (
644
+ reply.moreData == RequestTypes.NONE
645
+ or reply.peek)
646
+ and reply.command in (
621
647
  XDLMSAPDU.READ_RESPONSE,
622
648
  XDLMSAPDU.GET_RESPONSE,
623
649
  XDLMSAPDU.ACTION_RESPONSE,
624
650
  XDLMSAPDU.DATA_NOTIFICATION)
625
651
  ):
626
- pass
627
- # GXDLMS.getValueFromData(self.settings, self.reply)
652
+ return result.OK
653
+ # GXDLMS.getValueFromData(self.settings, reply)
628
654
 
629
- def __is_frame(self, notify, read_data: bytearray) -> bool:
655
+ def __is_frame(self, notify, read_data: bytearray, reply_: GXReplyData) -> bool:
630
656
  reply = GXByteBuffer(read_data)
631
657
  is_notify: bool = False
632
658
  match self.com_profile:
633
659
  case c_pf.HDLC():
634
- recv_frame = self.get_frame(read_data)
660
+ recv_frame = self.get_frame(read_data, reply_)
635
661
  if recv_frame is not None:
636
662
  self.log(logL.INFO, F"RX: {recv_frame.content.hex(' ')}")
637
663
  if recv_frame.control == frame.Control.UI_PF:
638
- target = notify # use instead of self.reply in getPdu(target). see in Gurux to do
664
+ target = notify # use instead of reply_ in getPdu(target). see in Gurux to do
639
665
  is_notify = True
640
- self.reply.frameId = recv_frame.control
666
+ reply_.frameId = recv_frame.control
641
667
  else: # TODO: GURUX redundant
642
668
  # self.write_trace(F"RX {self.id}: {get_os_time()} {read_data}", TraceLevel.ERROR)
643
- self.reply.frameId = frame.Control(0)
669
+ reply_.frameId = frame.Control(0)
644
670
  case c_pf.TCPUDPIP(): # getTcpData TODO: check it
645
- target = self.reply
671
+ target = reply_
646
672
  if len(reply) - reply.position < 8:
647
673
  target.complete = False
648
674
  return True
@@ -690,46 +716,44 @@ class Client:
690
716
  break
691
717
  else:
692
718
  reply.position = reply.position - 1
693
- if target is not self.reply:
719
+ if target is not reply_:
694
720
  is_notify = True
695
721
  case c_pf.MBUS(): # not realised see how
696
- GXDLMS.getMBusData(self.settings, reply, self.reply)
722
+ GXDLMS.getMBusData(self.settings, reply, reply_)
697
723
  case _: raise ValueError("Invalid Interface type.")
698
- if not self.reply.complete:
724
+ if not reply_.complete:
699
725
  return False
700
726
 
701
727
  # TODO: relocate notify to read_data_type
702
728
  if notify and not is_notify:
703
729
  #Check command to make sure it's not notify message.
704
- if self.reply.command in (XDLMSAPDU.DATA_NOTIFICATION,
730
+ if reply_.command in (XDLMSAPDU.DATA_NOTIFICATION,
705
731
  XDLMSAPDU.GLO_EVENT_NOTIFICATION_REQUEST,
706
732
  XDLMSAPDU.INFORMATION_REPORT_REQUEST,
707
733
  XDLMSAPDU.EVENT_NOTIFICATION_REQUEST,
708
734
  XDLMSAPDU.DED_INFORMATION_REPORT_REQUEST,
709
735
  XDLMSAPDU.DED_EVENT_NOTIFICATION_REQUEST):
710
736
  is_notify = True
711
- notify.complete = self.reply.complete
712
- notify.command = self.reply.command
713
- self.reply.command = None
714
- self.reply.time = None
715
- notify.self.reply.set(self.reply.data)
716
- # notify.value = self.reply.value
717
- self.reply.data.trim()
737
+ notify.complete = reply_.complete
738
+ notify.command = reply_.command
739
+ reply_.command = None
740
+ reply_.time = None
741
+ notify.reply_.set(reply_.data)
742
+ # notify.value = reply_.value
743
+ reply_.data.trim()
718
744
  if is_notify:
719
745
  return False
720
746
  return True
721
747
 
722
- async def read_data_block(self) -> Any: # todo: 1.make return pdu, 2.make depend from CommunicationProfile
723
- ret = None
748
+ async def read_data_block(self) -> result.SimpleOrError[bytes]: # todo: make depend from CommunicationProfile
724
749
  self.received_frames.clear()
725
- self.reply.clear()
750
+ reply = GXReplyData()
726
751
  while self.send_frames:
727
- # TODO: see how remove self.reply here !!!
728
752
  send_frame = self.send_frames.popleft()
729
753
  notify = GXReplyData()
730
- self.reply.error = 0
754
+ reply.error = 0
731
755
  recv_buf: bytearray = bytearray()
732
- if not self.reply.isStreaming():
756
+ if not reply.isStreaming():
733
757
  await self.media.send(send_frame.content)
734
758
  self.log(logL.INFO, F"TX: {send_frame.content.hex(" ")}")
735
759
  attempt: int = 1
@@ -738,7 +762,7 @@ class Client:
738
762
  await asyncio.wait_for( # todo: don't work exception!!!! Why????
739
763
  fut=self.media.receive(recv_buf),
740
764
  timeout=self.com_profile.parameters.inactivity_time_out) # todo: only for HDLC
741
- if self.__is_frame(notify, recv_buf):
765
+ if self.__is_frame(notify, recv_buf, reply):
742
766
  break
743
767
  elif notify.data.size != 0:
744
768
  if not notify.isMoreData():
@@ -751,37 +775,40 @@ class Client:
751
775
  await self.media.send(send_frame.content)
752
776
  attempt += 1
753
777
  else:
754
- raise exc.Timeout("Failed to receive reply from the device in given time")
778
+ return result.Error.from_e(TimeoutError("Failed to receive reply from the device in given time"), "read data block")
755
779
  recv_buf.clear()
756
- match self.reply.error:
757
- case 0: """errors is absence"""
758
- case 4: raise exc.NoObject
759
- case _: raise GXDLMSException(self.reply.error)
780
+ match reply.error:
781
+ case 0:
782
+ """errors is absence"""
783
+ case 4:
784
+ return result.Error.from_e(exc.NoObject(), "read data block")
785
+ case _:
786
+ return result.Error.from_e(GXDLMSException(reply.error), "read data block")
760
787
  if self.received_frames[-1].control.is_info() or self.received_frames[-1].control == frame.Control.UI_PF:
761
788
  if self.received_frames[-1].is_segmentation:
762
789
  """pass handle frame. wait all information"""
763
790
  else:
764
791
  llc = sub_layer.LLC(frame.Frame.join_info(self.received_frames))
765
792
 
766
- self.reply.data.position = len(self.reply.data)
767
- self.reply.data.set(llc.info)
768
- ret = self.getPdu()
769
-
793
+ reply.data.position = len(reply.data)
794
+ reply.data.set(llc.info)
795
+ if isinstance(res_pdu := self.getPdu(reply), result.Error):
796
+ return res_pdu
770
797
  # TODO: LLC to PDU
771
798
  else:
772
799
  received_frame = self.received_frames.popleft()
773
800
  if send_frame.control == frame.Control.SNRM_P:
774
801
  self.com_profile.negotiation.set_from_UA(received_frame.info)
775
802
  self.log(logL.INFO, F"negotiation setup: {self.com_profile.negotiation}")
776
- if self.reply.isMoreData():
777
- if self.reply.isStreaming():
803
+ if reply.isMoreData():
804
+ if reply.isStreaming():
778
805
  data = None
779
806
  else:
780
807
  # Generates an acknowledgment message, with which the server is informed to send next packets. Frame type. Acknowledgment message as byte array
781
- if self.reply.moreData == RequestTypes.NONE:
782
- raise ValueError("Invalid receiverReady RequestTypes parameter.")
808
+ if reply.moreData == RequestTypes.NONE:
809
+ return result.Error.from_e(ValueError("Invalid receiverReady RequestTypes parameter."), msg="read data block")
783
810
  # Get next frame.
784
- if (self.reply.moreData & RequestTypes.FRAME) != 0:
811
+ if (reply.moreData & RequestTypes.FRAME) != 0:
785
812
  id_ = self.settings.getReceiverReady()
786
813
  # return GXDLMS.getHdlcFrame(settings, id_, None)
787
814
  self.add_frames_to_queue(frame.Control(id_))
@@ -796,13 +823,13 @@ class Client:
796
823
  cmd = XDLMSAPDU.READ_RESPONSE
797
824
  else:
798
825
  cmd = XDLMSAPDU.READ_REQUEST
799
- if self.reply.moreData == RequestTypes.GBT:
826
+ if reply.moreData == RequestTypes.GBT:
800
827
  p = GXDLMSLNParameters(self.settings, 0, XDLMSAPDU.GENERAL_BLOCK_TRANSFER, 0, None, None, 0xff)
801
- p.WindowSize = self.reply.windowSize
802
- p.blockNumberAck = self.reply.blockNumberAck
803
- p.blockIndex = self.reply.blockNumber
828
+ p.WindowSize = reply.windowSize
829
+ p.blockNumberAck = reply.blockNumberAck
830
+ p.blockIndex = reply.blockNumber
804
831
  p.Streaming = False
805
- reply = self.getLnMessages(p) # TODO: test it
832
+ messages = self.getLnMessages(p) # TODO: test it
806
833
  else:
807
834
  # Get next block.
808
835
  bb = GXByteBuffer(4)
@@ -813,12 +840,12 @@ class Client:
813
840
  self.settings.increaseBlockIndex()
814
841
  if self.settings.getUseLogicalNameReferencing():
815
842
  p = GXDLMSLNParameters(self.settings, 0, cmd, pdu.GetResponse.WITH_DATABLOCK, bb, None, 0xff)
816
- reply = self.getLnMessages(p)
843
+ messages = self.getLnMessages(p)
817
844
  else:
818
845
  p = GXDLMSSNParameters(self.settings, cmd, 1, VariableAccessSpecification.BLOCK_NUMBER_ACCESS, bb, None)
819
- reply = self.getSnMessages(p)
820
- data = reply
821
- return ret
846
+ messages = self.getSnMessages(p)
847
+ data = messages
848
+ return result.Simple(reply.data.get_data())
822
849
 
823
850
  def getSnMessages(self, p: GXDLMSSNParameters):
824
851
  reply = GXByteBuffer()
@@ -864,8 +891,8 @@ class Client:
864
891
  if p.settings.is_multiple_block():
865
892
  reply.size = 0
866
893
  if (
867
- not ciphering
868
- and isinstance(self.com_profile, c_pf.HDLC)
894
+ not ciphering
895
+ and isinstance(self.com_profile, c_pf.HDLC)
869
896
  ):
870
897
  if p.settings.isServer:
871
898
  reply.set(_GXCommon.LLC_REPLY_BYTES)
@@ -970,36 +997,31 @@ class Client:
970
997
  def set_params(self, field: str, value: str):
971
998
  self.__dict__[field] = eval(value)
972
999
 
973
- async def close(self) -> None:
1000
+ async def close(self) -> result.StrictOk | result.Error:
974
1001
  """close , media is open"""
1002
+ res = result.StrictOk()
975
1003
  self.log(logL.DEB, "close")
976
1004
  if self.level > OSI.DATA_LINK:
977
- try:
978
- # Release is call only for secured connections. All meters are not supporting Release and it's causing problems.
979
- if (
1005
+ # Release is call only for secured connections. All meters are not supporting Release and it's causing problems.
1006
+ if (
980
1007
  isinstance(self.com_profile, c_pf.TCPUDPIP)
981
1008
  or (
982
- isinstance(self.com_profile, c_pf.HDLC)
983
- and self.settings.cipher.security != Security.NONE
984
- )
985
- ):
986
- self.releaseRequest()
987
- await self.read_data_block()
988
- except Exception:
989
- self.log(logL.WARN, "don't support release ReleaseRequest")
990
- pass
991
- # All meters don't support release.
992
- finally:
993
- self.level = OSI.DATA_LINK
1009
+ isinstance(self.com_profile, c_pf.HDLC)
1010
+ and self.settings.cipher.security != Security.NONE
1011
+ )
1012
+ ):
1013
+ self.releaseRequest()
1014
+ if isinstance(res_rdb := await self.read_data_block(), result.Error):
1015
+ res.append_err(res_rdb.err)
1016
+ self.log(logL.WARN, "don't support release ReleaseRequest")
1017
+ self.level = OSI.DATA_LINK
994
1018
  # hdlc close
995
- try:
996
- res = await self.disconnect_request()
997
- # todo: handle res
998
- except Exception as e:
999
- self.log(logL.ERR, F'Disconnect request ERROR: {e.args[0]}')
1019
+ if isinstance(res_diconnect_req := await self.disconnect_request(), result.Error):
1020
+ res.append_err(res_diconnect_req.err)
1000
1021
  self.level -= OSI.DATA_LINK
1022
+ return res
1001
1023
 
1002
- async def disconnect_request(self):
1024
+ async def disconnect_request(self) -> result.Ok | result.Error:
1003
1025
  """ Sent to server DISC """
1004
1026
  if isinstance(self.com_profile, c_pf.HDLC):
1005
1027
  self.add_frames_to_queue(frame.Control.DISC_P)
@@ -1026,10 +1048,10 @@ class Client:
1026
1048
  return ret
1027
1049
 
1028
1050
  # TODO: remove in future
1029
- def parseApplicationAssociationResponse(self):
1051
+ def parseApplicationAssociationResponse(self, data: bytes):
1030
1052
  """ Parse server's challenge if HLS authentication is used. Received reply from the server. todo: refactoring here """
1031
1053
  ic = 0
1032
- value = cdt.OctetString(self.reply.data.get_data())
1054
+ value = cdt.OctetString(data)
1033
1055
  match self.m_id:
1034
1056
  case mechanism_id.HIGH_GMAC:
1035
1057
  secret = self.settings.sourceSystemTitle
@@ -1135,8 +1157,9 @@ class Client:
1135
1157
  else:
1136
1158
  return self.method2(0xFA00, 12, 8, challenge, cdt.OctetString.TAG) # TODO: rewrite old client.method
1137
1159
 
1138
- def parseAARE(self, buff: GXByteBuffer) -> AcseServiceUser:
1160
+ def parseAARE(self, pdu: bytes) -> AcseServiceUser:
1139
1161
  # Get AARE tag and length
1162
+ buff = GXByteBuffer(pdu)
1140
1163
  tag = buff.getUInt8()
1141
1164
  if self.settings.isServer:
1142
1165
  if tag != (BerType.APPLICATION | BerType.CONSTRUCTED | AARQapdu.PROTOCOL_VERSION):
@@ -1452,11 +1475,11 @@ class Client:
1452
1475
  # raise exc.AssociationResultError(resultComponent, resultDiagnosticValue)
1453
1476
  return resultDiagnosticValue
1454
1477
 
1455
- def parseAareResponse(self, reply) -> AcseServiceUser:
1478
+ def parseAareResponse(self, pdu: bytes) -> AcseServiceUser:
1456
1479
  """ TODO: need refactoring. Parses the AARE response. Parse method will update the following data: DLMSVersion, MaxReceivePDUSize, UseLogicalNameReferencing, LNSettings or SNSettings,
1457
1480
  LNSettings or SNSettings will be updated, depending on the referencing, Logical name or Short name.
1458
1481
  Received data. GXDLMSClient#aarqRequest GXDLMSClient#useLogicalNameReferencing GXDLMSClient#negotiatedConformance GXDLMSClient#proposedConformance """
1459
- if (ret := self.parseAARE(reply)) != AcseServiceUser.AUTHENTICATION_REQUIRED:
1482
+ if (ret := self.parseAARE(pdu)) != AcseServiceUser.AUTHENTICATION_REQUIRED:
1460
1483
  self.level |= OSI.APPLICATION
1461
1484
  if self.settings.dlmsVersion != 6:
1462
1485
  raise ValueError("Invalid DLMS version number.")
@@ -1581,7 +1604,7 @@ class Client:
1581
1604
 
1582
1605
  def getLnMessages(self, p: GXDLMSLNParameters):
1583
1606
  reply = GXByteBuffer()
1584
- messages = list()
1607
+ messages = []
1585
1608
  frame_ = 0
1586
1609
  if (
1587
1610
  p.command == XDLMSAPDU.DATA_NOTIFICATION
@@ -2037,6 +2060,7 @@ class Client:
2037
2060
  else:
2038
2061
  return 'недоступен'
2039
2062
 
2063
+ @deprecated("<use ReadObjAttr>")
2040
2064
  async def read_attribute(self, obj: ic.COSEMInterfaceClasses | str,
2041
2065
  attr_index: int):
2042
2066
  # TODO: redundant, use read_attr?
@@ -2046,44 +2070,25 @@ class Client:
2046
2070
  value=attr_index,
2047
2071
  with_selection=bool(self.negotiated_conformance.selective_access)))
2048
2072
  start_read_time: float = time.perf_counter()
2049
- await self.read_data_block()
2073
+ data = (await self.read_data_block()).unwrap()
2050
2074
  self.last_transfer_time = datetime.timedelta(seconds=time.perf_counter()-start_read_time)
2051
- obj.set_attr(attr_index, self.reply.data.get_data())
2052
-
2053
- async def read_attr(self, attr_desc: ut.CosemAttributeDescriptor) -> bytes:
2054
- """instead <read_attribute> in future. Read and return encoding"""
2055
- self.get_get_request_normal(attr_desc)
2056
- await self.read_data_block()
2057
- return self.reply.data.get_data()
2058
-
2059
- def recv_attr_value(self, attr_desc: ut.CosemAttributeDescriptor) -> cdt.CommonDataTypes:
2060
- """receive attribute value from server"""
2061
- obj = self.objects.get_object(attr_desc)
2062
- index = int(attr_desc.attribute_id)
2063
- obj.set_attr(index=index,
2064
- value=self.read_attr(attr_desc))
2065
- return obj.get_attr(index)
2066
-
2067
- async def write_attr2(self, attr_desc: ut.CosemAttributeDescriptor, value: cdt.CommonDataTypes):
2068
- data = self.get_set_request_normal2(attr_desc, value)
2069
- await self.read_data_block()
2075
+ obj.set_attr(attr_index, data)
2070
2076
 
2071
2077
  @deprecated("use execute_method2")
2072
- async def execute_method(self, meth_desc: ut.CosemMethodDescriptor):
2078
+ async def execute_method(self, meth_desc: ut.CosemMethodDescriptor) -> result.Ok | result.Error:
2073
2079
  data = self.get_action_request_normal_old(meth_desc)
2074
- await self.read_data_block()
2080
+ return await self.read_data_block()
2075
2081
 
2076
- async def execute_method2(self, obj: ic.COSEMInterfaceClasses, i: int, mip=None):
2082
+ async def execute_method2(self, obj: ic.COSEMInterfaceClasses, i: int, mip=None) -> result.Ok | result.Error:
2077
2083
  data = self.get_action_request_normal(
2078
2084
  meth_desc=obj.get_meth_descriptor(i),
2079
2085
  method=obj.get_meth_element(i).DATA_TYPE() if mip is None else mip)
2080
- await self.read_data_block()
2086
+ return await self.read_data_block()
2081
2087
 
2082
2088
  async def is_equal_attribute(self, obj: ic.COSEMInterfaceClasses, attr_index: int | str, with_time: bool | datetime.datetime = False) -> bool:
2083
2089
  self.get_get_request_normal(obj.get_attr_descriptor(attr_index))
2084
- await self.read_data_block()
2085
- if obj.get_attr(attr_index).encoding == self.reply.data.get_data():
2090
+ data = (await self.read_data_block()).unwrap()
2091
+ if obj.get_attr(attr_index).encoding == data:
2086
2092
  return True
2087
2093
  else:
2088
- # self.set_error(Application.VALUE_ERROR, F'{obj} attr-{attr_index}: got {self.reply.data.get_data()} must {obj[attr_index]}')
2089
2094
  return False