@@ -209,6 +209,13 @@ class DlmsConnection:
209209 )
210210 )
211211
212+ # Keep a copy of the context proposed in AARQ so the same context can be reused in
213+ # RLRQ when ciphering is enabled.
214+ proposed_initiate_request : Optional [xdlms .InitiateRequest ] = attr .ib (
215+ default = None ,
216+ init = False ,
217+ )
218+
212219 settings : DlmsConnectionSettings = attr .ib (
213220 default = DlmsConnectionSettings (),
214221 converter = attr .converters .default_if_none (factory = DlmsConnectionSettings ),
@@ -291,6 +298,7 @@ def send(self, event) -> bytes:
291298 )
292299
293300 self .state .process_event (event )
301+ self .register_proposed_context (event )
294302 LOG .debug (f"Preparing to send DLMS Request" , request = event )
295303
296304 if self .use_protection :
@@ -398,6 +406,39 @@ def next_event(self):
398406 def clear_buffer (self ):
399407 self .buffer = bytearray ()
400408
409+ @staticmethod
410+ def copy_initiate_request (
411+ initiate_request : xdlms .InitiateRequest ,
412+ ) -> xdlms .InitiateRequest :
413+ return xdlms .InitiateRequest (
414+ proposed_conformance = Conformance (
415+ ** attr .asdict (initiate_request .proposed_conformance )
416+ ),
417+ proposed_quality_of_service = initiate_request .proposed_quality_of_service ,
418+ client_max_receive_pdu_size = initiate_request .client_max_receive_pdu_size ,
419+ proposed_dlms_version_number = initiate_request .proposed_dlms_version_number ,
420+ response_allowed = initiate_request .response_allowed ,
421+ dedicated_key = initiate_request .dedicated_key ,
422+ )
423+
424+ def register_proposed_context (self , event : Any ) -> None :
425+ """
426+ Keep track of the context proposed in AARQ so it can be reused in RLRQ.
427+ """
428+ if not isinstance (event , acse .ApplicationAssociationRequest ):
429+ return
430+
431+ if not event .user_information :
432+ self .proposed_initiate_request = None
433+ return
434+
435+ if not isinstance (event .user_information .content , xdlms .InitiateRequest ):
436+ return
437+
438+ self .proposed_initiate_request = self .copy_initiate_request (
439+ event .user_information .content
440+ )
441+
401442 @property
402443 def use_protection (self ) -> bool :
403444 """
@@ -587,19 +628,33 @@ def get_aarq(self) -> acse.ApplicationAssociationRequest:
587628 user_information = acse .UserInformation (content = initiate_request ),
588629 )
589630
590- def get_rlrq (self ) -> acse . ReleaseRequest :
631+ def get_rlrq_initiate_request (self ) -> xdlms . InitiateRequest :
591632 """
592- Returns a ReleaseRequestApdu to release the current association if one should be used .
633+ Return the original context proposed in AARQ when available .
593634 """
594635
595- initiate_request = xdlms .InitiateRequest (
636+ if self .proposed_initiate_request is not None :
637+ return self .copy_initiate_request (self .proposed_initiate_request )
638+
639+ return xdlms .InitiateRequest (
596640 proposed_conformance = self .conformance ,
597641 client_max_receive_pdu_size = self .max_pdu_size ,
598642 )
599643
644+ def get_rlrq (self ) -> acse .ReleaseRequest :
645+ """
646+ Returns a ReleaseRequestApdu to release the current association if one should be used.
647+ """
648+
649+ user_information = None
650+
651+ if self .use_protection :
652+ initiate_request = self .get_rlrq_initiate_request ()
653+ user_information = acse .UserInformation (content = initiate_request )
654+
600655 return acse .ReleaseRequest (
601656 reason = enums .ReleaseRequestReason .NORMAL ,
602- user_information = acse . UserInformation ( content = initiate_request ) ,
657+ user_information = user_information ,
603658 )
604659
605660 def update_negotiated_parameters (
0 commit comments