Skip to content

Commit 136aef5

Browse files
committed
Add invoke-id tracking in DlmsClient requests
1 parent eeac7e9 commit 136aef5

2 files changed

Lines changed: 53 additions & 19 deletions

File tree

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Calendar Versioning](https://calver.org/)
1212
### Added
1313

1414
### Changed
15+
* `DlmsClient` now tracks and increments invoke-id values and sets explicit
16+
invoke-id-and-priority in GET, GET.WITH_LIST, SET, and ACTION requests.
1517

1618
### Deprecated
1719

dlms_cosem/client.py

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import contextlib
22
from typing import *
3+
from typing import Any, Generator
34

45
import attr
56
import structlog
@@ -16,7 +17,7 @@
1617

1718

1819
class DataResultError(Exception):
19-
""" Error retrieveing data"""
20+
"""Error retrieveing data"""
2021

2122

2223
class ActionError(Exception):
@@ -41,6 +42,9 @@ class DlmsClient:
4142
client_initial_invocation_counter: int = attr.ib(default=0)
4243
meter_initial_invocation_counter: int = attr.ib(default=0)
4344
timeout: int = attr.ib(default=10)
45+
invoke_id: int = attr.ib(default=0)
46+
invoke_id_confirmed: bool = attr.ib(default=True)
47+
invoke_id_high_priority: bool = attr.ib(default=True)
4448
connection_settings: Optional[DlmsConnectionSettings] = attr.ib(default=None)
4549

4650
dlms_connection: DlmsConnection = attr.ib(
@@ -63,21 +67,36 @@ class DlmsClient:
6367
)
6468

6569
@contextlib.contextmanager
66-
def session(self) -> "DlmsClient":
70+
def session(self) -> Generator["DlmsClient", Any, None]:
6771
self.connect()
6872
self.associate()
6973
yield self
7074
self.release_association()
7175
self.disconnect()
7276

77+
def next_invoke_id(self) -> int:
78+
current = self.invoke_id
79+
self.invoke_id = (current + 1) % 16
80+
return current
81+
82+
def next_invoke_id_and_priority(self) -> xdlms.InvokeIdAndPriority:
83+
return xdlms.InvokeIdAndPriority(
84+
invoke_id=self.next_invoke_id(),
85+
confirmed=self.invoke_id_confirmed,
86+
high_priority=self.invoke_id_high_priority,
87+
)
88+
7389
def get(
74-
self,
75-
cosem_attribute: cosem.CosemAttribute,
76-
access_descriptor: Optional[RangeDescriptor] = None,
90+
self,
91+
cosem_attribute: cosem.CosemAttribute,
92+
access_descriptor: Optional[RangeDescriptor] = None,
7793
) -> bytes:
94+
invoke = self.next_invoke_id_and_priority()
7895
self.send(
7996
xdlms.GetRequestNormal(
80-
cosem_attribute=cosem_attribute, access_selection=access_descriptor
97+
cosem_attribute=cosem_attribute,
98+
access_selection=access_descriptor,
99+
invoke_id_and_priority=invoke,
81100
)
82101
)
83102
all_data_received = False
@@ -115,13 +134,15 @@ def get(
115134
return bytes(data)
116135

117136
def get_many(
118-
self, cosem_attributes_with_selection: List[cosem.CosemAttributeWithSelection]
137+
self, cosem_attributes_with_selection: List[cosem.CosemAttributeWithSelection]
119138
):
120139
"""
121140
Make a GET.WITH_LIST call. Get many items in one request.
122141
"""
142+
invoke = self.next_invoke_id_and_priority()
123143
out = xdlms.GetRequestWithList(
124-
cosem_attributes_with_selection=cosem_attributes_with_selection
144+
cosem_attributes_with_selection=cosem_attributes_with_selection,
145+
invoke_id_and_priority=invoke,
125146
)
126147
self.send(out)
127148
response = self.next_event()
@@ -134,11 +155,25 @@ def get_many(
134155
return response
135156

136157
def set(self, cosem_attribute: cosem.CosemAttribute, data: bytes):
137-
self.send(xdlms.SetRequestNormal(cosem_attribute=cosem_attribute, data=data))
158+
invoke = self.next_invoke_id_and_priority()
159+
self.send(
160+
xdlms.SetRequestNormal(
161+
cosem_attribute=cosem_attribute,
162+
data=data,
163+
invoke_id_and_priority=invoke,
164+
)
165+
)
138166
return self.next_event()
139167

140168
def action(self, method: cosem.CosemMethod, data: bytes):
141-
self.send(xdlms.ActionRequestNormal(cosem_method=method, data=data))
169+
invoke = self.next_invoke_id_and_priority()
170+
self.send(
171+
xdlms.ActionRequestNormal(
172+
cosem_method=method,
173+
data=data,
174+
invoke_id_and_priority=invoke,
175+
)
176+
)
142177
response = self.next_event()
143178

144179
if isinstance(response, xdlms.ActionResponseNormalWithError):
@@ -153,10 +188,9 @@ def action(self, method: cosem.CosemMethod, data: bytes):
153188
return
154189

155190
def associate(
156-
self,
157-
association_request: Optional[acse.ApplicationAssociationRequest] = None,
191+
self,
192+
association_request: Optional[acse.ApplicationAssociationRequest] = None,
158193
) -> acse.ApplicationAssociationResponse:
159-
160194
# the aarq can be overridden or the standard one from the connection is used.
161195
aarq = association_request or self.dlms_connection.get_aarq()
162196

@@ -174,7 +208,7 @@ def associate(
174208
extra_error = None
175209
if response.user_information:
176210
if isinstance(
177-
response.user_information.content, ConfirmedServiceError
211+
response.user_information.content, ConfirmedServiceError
178212
):
179213
extra_error = response.user_information.content.error
180214
raise exceptions.DlmsClientException(
@@ -187,7 +221,6 @@ def associate(
187221
)
188222

189223
if self.should_send_hls_reply():
190-
191224
# TODO: wrap hls logic in method
192225
try:
193226
hls_response = self.send_hls_reply()
@@ -203,7 +236,7 @@ def associate(
203236
raise HLSError("Did not receive any HLS response data")
204237

205238
if not self.dlms_connection.authentication.hls_meter_data_is_valid(
206-
hls_data, self.dlms_connection
239+
hls_data, self.dlms_connection
207240
):
208241
raise HLSError(
209242
f"Meter did not respond with correct challenge calculation"
@@ -213,8 +246,8 @@ def associate(
213246

214247
def should_send_hls_reply(self) -> bool:
215248
return (
216-
self.dlms_connection.state.current_state
217-
== state.SHOULD_SEND_HLS_SEVER_CHALLENGE_RESULT
249+
self.dlms_connection.state.current_state
250+
== state.SHOULD_SEND_HLS_SEVER_CHALLENGE_RESULT
218251
)
219252

220253
def send_hls_reply(self) -> Optional[bytes]:
@@ -232,7 +265,6 @@ def send_hls_reply(self) -> Optional[bytes]:
232265
)
233266

234267
def release_association(self) -> Optional[acse.ReleaseResponse]:
235-
236268
rlrq = self.dlms_connection.get_rlrq()
237269
try:
238270
self.send(rlrq)

0 commit comments

Comments
 (0)