s2-python 0.2.2__tar.gz → 0.3.0__tar.gz
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.
- {s2_python-0.2.2 → s2_python-0.3.0}/PKG-INFO +1 -1
- {s2_python-0.2.2 → s2_python-0.3.0}/setup.cfg +1 -1
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/PKG-INFO +1 -1
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/s2_connection.py +81 -38
- {s2_python-0.2.2 → s2_python-0.3.0}/README.rst +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/pyproject.toml +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/setup.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/SOURCES.txt +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/dependency_links.txt +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/entry_points.txt +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/not-zip-safe +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/requires.txt +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2_python.egg-info/top_level.txt +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/__init__.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/__init__.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/duration.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/handshake.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/handshake_response.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/instruction_status_update.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/number_range.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/power_forecast.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/power_forecast_element.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/power_forecast_value.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/power_measurement.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/power_range.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/power_value.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/reception_status.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/resource_manager_details.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/revoke_object.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/role.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/select_control_type.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/session_request.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/support.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/timer.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/common/transition.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/__init__.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_actuator_description.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_actuator_status.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_fill_level_target_profile.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_fill_level_target_profile_element.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_instruction.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_leakage_behaviour.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_leakage_behaviour_element.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_operation_mode.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_operation_mode_element.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_storage_description.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_storage_status.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_system_description.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_timer_status.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_usage_forecast.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_usage_forecast_element.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/rm.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/generated/__init__.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/generated/gen_s2.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/py.typed +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/reception_status_awaiter.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/s2_control_type.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/s2_parser.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/s2_validation_error.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/utils.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/validate_values_mixin.py +0 -0
- {s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/version.py +0 -0
@@ -8,7 +8,7 @@ license_files = LICENSE.txt
|
|
8
8
|
long_description = file: README.rst
|
9
9
|
long_description_content_type = text/x-rst; charset=UTF-8
|
10
10
|
url = https://github.com/flexiblepower/s2-ws-json-python
|
11
|
-
version = 0.
|
11
|
+
version = 0.3.0
|
12
12
|
platforms = Linux
|
13
13
|
classifiers =
|
14
14
|
Development Status :: 4 - Beta
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
3
|
import logging
|
4
|
+
import time
|
4
5
|
import threading
|
5
6
|
import uuid
|
6
7
|
from dataclasses import dataclass
|
@@ -180,6 +181,7 @@ class MessageHandlers:
|
|
180
181
|
|
181
182
|
class S2Connection: # pylint: disable=too-many-instance-attributes
|
182
183
|
url: str
|
184
|
+
reconnect: bool
|
183
185
|
reception_status_awaiter: ReceptionStatusAwaiter
|
184
186
|
ws: Optional[WSConnection]
|
185
187
|
s2_parser: S2Parser
|
@@ -195,16 +197,20 @@ class S2Connection: # pylint: disable=too-many-instance-attributes
|
|
195
197
|
|
196
198
|
_eventloop: asyncio.AbstractEventLoop
|
197
199
|
_stop_event: asyncio.Event
|
200
|
+
_restart_connection_event: asyncio.Event
|
198
201
|
|
199
|
-
def __init__(
|
202
|
+
def __init__( # pylint: disable=too-many-arguments
|
200
203
|
self,
|
201
204
|
url: str,
|
202
205
|
role: EnergyManagementRole,
|
203
206
|
control_types: List[S2ControlType],
|
204
207
|
asset_details: AssetDetails,
|
208
|
+
reconnect: bool = False,
|
205
209
|
) -> None:
|
206
210
|
self.url = url
|
211
|
+
self.reconnect = reconnect
|
207
212
|
self.reception_status_awaiter = ReceptionStatusAwaiter()
|
213
|
+
self.ws = None
|
208
214
|
self.s2_parser = S2Parser()
|
209
215
|
|
210
216
|
self._handlers = MessageHandlers()
|
@@ -221,14 +227,13 @@ class S2Connection: # pylint: disable=too-many-instance-attributes
|
|
221
227
|
self._handlers.register_handler(HandshakeResponse, self.handle_handshake_response_as_rm)
|
222
228
|
|
223
229
|
def start_as_rm(self) -> None:
|
224
|
-
self.
|
225
|
-
self._thread.start()
|
226
|
-
logger.debug("Started eventloop thread!")
|
230
|
+
self._run_eventloop(self._run_as_rm())
|
227
231
|
|
228
|
-
def _run_eventloop(self) -> None:
|
232
|
+
def _run_eventloop(self, main_task: Awaitable[None]) -> None:
|
233
|
+
self._thread = threading.current_thread()
|
229
234
|
logger.debug("Starting eventloop")
|
230
235
|
try:
|
231
|
-
self._eventloop.run_until_complete(
|
236
|
+
self._eventloop.run_until_complete(main_task)
|
232
237
|
except asyncio.CancelledError:
|
233
238
|
pass
|
234
239
|
logger.debug("S2 connection thread has stopped.")
|
@@ -256,45 +261,69 @@ class S2Connection: # pylint: disable=too-many-instance-attributes
|
|
256
261
|
|
257
262
|
async def _run_as_rm(self) -> None:
|
258
263
|
logger.debug("Connecting as S2 resource manager.")
|
259
|
-
|
264
|
+
|
260
265
|
self._stop_event = asyncio.Event()
|
261
|
-
await self.connect_ws()
|
262
266
|
|
263
|
-
|
264
|
-
background_tasks.append(self._eventloop.create_task(self._receive_messages()))
|
265
|
-
background_tasks.append(self._eventloop.create_task(self._handle_received_messages()))
|
267
|
+
first_run = True
|
266
268
|
|
267
|
-
|
268
|
-
|
269
|
+
while (first_run or self.reconnect) and not self._stop_event.is_set():
|
270
|
+
first_run = False
|
271
|
+
self._restart_connection_event = asyncio.Event()
|
272
|
+
await self._connect_and_run()
|
273
|
+
time.sleep(1)
|
269
274
|
|
270
|
-
|
275
|
+
logger.debug("Finished S2 connection eventloop.")
|
271
276
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
await task
|
277
|
-
except asyncio.CancelledError:
|
278
|
-
pass
|
279
|
-
except websockets.ConnectionClosedError:
|
280
|
-
logger.info("The other party closed the websocket connection.c")
|
277
|
+
async def _connect_and_run(self) -> None:
|
278
|
+
self._received_messages = asyncio.Queue()
|
279
|
+
await self._connect_ws()
|
280
|
+
if self.ws:
|
281
281
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
282
|
+
async def wait_till_stop() -> None:
|
283
|
+
await self._stop_event.wait()
|
284
|
+
|
285
|
+
async def wait_till_connection_restart() -> None:
|
286
|
+
await self._restart_connection_event.wait()
|
287
|
+
|
288
|
+
background_tasks = [
|
289
|
+
self._eventloop.create_task(self._receive_messages()),
|
290
|
+
self._eventloop.create_task(wait_till_stop()),
|
291
|
+
self._eventloop.create_task(self._connect_as_rm()),
|
292
|
+
self._eventloop.create_task(wait_till_connection_restart()),
|
293
|
+
]
|
294
|
+
|
295
|
+
(done, pending) = await asyncio.wait(
|
296
|
+
background_tasks, return_when=asyncio.FIRST_COMPLETED
|
297
|
+
)
|
298
|
+
if self._current_control_type:
|
299
|
+
self._current_control_type.deactivate(self)
|
300
|
+
self._current_control_type = None
|
301
|
+
|
302
|
+
for task in done:
|
303
|
+
try:
|
304
|
+
await task
|
305
|
+
except asyncio.CancelledError:
|
306
|
+
pass
|
307
|
+
except (websockets.ConnectionClosedError, websockets.ConnectionClosedOK):
|
308
|
+
logger.info("The other party closed the websocket connection.")
|
309
|
+
|
310
|
+
for task in pending:
|
311
|
+
try:
|
312
|
+
task.cancel()
|
313
|
+
await task
|
314
|
+
except asyncio.CancelledError:
|
315
|
+
pass
|
288
316
|
|
289
|
-
if self.ws:
|
290
317
|
await self.ws.close()
|
291
318
|
await self.ws.wait_closed()
|
292
|
-
logger.debug("Finished S2 connection eventloop.")
|
293
319
|
|
294
|
-
async def
|
295
|
-
|
320
|
+
async def _connect_ws(self) -> None:
|
321
|
+
try:
|
322
|
+
self.ws = await ws_connect(uri=self.url)
|
323
|
+
except (EOFError, OSError) as e:
|
324
|
+
logger.info("Could not connect due to: %s", str(e))
|
296
325
|
|
297
|
-
async def
|
326
|
+
async def _connect_as_rm(self) -> None:
|
298
327
|
await self.send_msg_and_await_reception_status_async(
|
299
328
|
Handshake(
|
300
329
|
message_id=uuid.uuid4(), role=self.role, supported_protocol_versions=[S2_VERSION]
|
@@ -302,6 +331,8 @@ class S2Connection: # pylint: disable=too-many-instance-attributes
|
|
302
331
|
)
|
303
332
|
logger.debug("Send handshake to CEM. Expecting Handshake and HandshakeResponse from CEM.")
|
304
333
|
|
334
|
+
await self._handle_received_messages()
|
335
|
+
|
305
336
|
async def handle_handshake(
|
306
337
|
self, _: "S2Connection", message: S2Message, send_okay: Awaitable[None]
|
307
338
|
) -> None:
|
@@ -427,7 +458,11 @@ class S2Connection: # pylint: disable=too-many-instance-attributes
|
|
427
458
|
|
428
459
|
json_msg = s2_msg.to_json()
|
429
460
|
logger.debug("Sending message %s", json_msg)
|
430
|
-
|
461
|
+
try:
|
462
|
+
await self.ws.send(json_msg)
|
463
|
+
except websockets.ConnectionClosedError as e:
|
464
|
+
logger.error("Unable to send message %s due to %s", s2_msg, str(e))
|
465
|
+
self._restart_connection_event.set()
|
431
466
|
|
432
467
|
async def respond_with_reception_status(
|
433
468
|
self, subject_message_id: str, status: ReceptionStatusValues, diagnostic_label: str
|
@@ -458,9 +493,17 @@ class S2Connection: # pylint: disable=too-many-instance-attributes
|
|
458
493
|
s2_msg.message_id, # type: ignore[attr-defined]
|
459
494
|
timeout_reception_status,
|
460
495
|
)
|
461
|
-
|
462
|
-
|
463
|
-
|
496
|
+
try:
|
497
|
+
reception_status = await self.reception_status_awaiter.wait_for_reception_status(
|
498
|
+
s2_msg.message_id, timeout_reception_status # type: ignore[attr-defined]
|
499
|
+
)
|
500
|
+
except TimeoutError:
|
501
|
+
logger.error(
|
502
|
+
"Did not receive a reception status on time for %s",
|
503
|
+
s2_msg.message_id, # type: ignore[attr-defined]
|
504
|
+
)
|
505
|
+
self._stop_event.set()
|
506
|
+
raise
|
464
507
|
|
465
508
|
if reception_status.status != ReceptionStatusValues.OK and raise_on_error:
|
466
509
|
raise RuntimeError(f"ReceptionStatus was not OK but rather {reception_status.status}")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{s2_python-0.2.2 → s2_python-0.3.0}/src/s2python/frbc/frbc_fill_level_target_profile_element.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|