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