motorcortex-python 0.25.0__tar.gz → 0.25.4__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.
Files changed (28) hide show
  1. {motorcortex_python-0.25.0/motorcortex_python.egg-info → motorcortex_python-0.25.4}/PKG-INFO +12 -3
  2. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/__init__.py +41 -8
  3. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/init_threads.py +2 -2
  4. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/request.py +115 -45
  5. motorcortex_python-0.25.4/motorcortex/version.py +1 -0
  6. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4/motorcortex_python.egg-info}/PKG-INFO +12 -3
  7. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex_python.egg-info/SOURCES.txt +1 -0
  8. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex_python.egg-info/requires.txt +1 -1
  9. motorcortex_python-0.25.4/pyproject.toml +3 -0
  10. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/setup.py +3 -2
  11. motorcortex_python-0.25.0/motorcortex/version.py +0 -1
  12. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/LICENSE +0 -0
  13. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/MANIFEST.in +0 -0
  14. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/README.md +0 -0
  15. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/message_types.py +0 -0
  16. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/motorcortex_hash.json +0 -0
  17. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/motorcortex_pb2.py +0 -0
  18. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/nng_url.py +0 -0
  19. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/parameter_tree.py +0 -0
  20. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/reply.py +0 -0
  21. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/setup_logger.py +0 -0
  22. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/state_callback_handler.py +0 -0
  23. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/subscribe.py +0 -0
  24. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/subscription.py +0 -0
  25. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex/timespec.py +0 -0
  26. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex_python.egg-info/dependency_links.txt +0 -0
  27. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/motorcortex_python.egg-info/top_level.txt +0 -0
  28. {motorcortex_python-0.25.0 → motorcortex_python-0.25.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: motorcortex-python
3
- Version: 0.25.0
3
+ Version: 0.25.4
4
4
  Summary: Python bindings for Motorcortex Engine
5
5
  Home-page: https://www.motorcortex.io
6
6
  Author: Alexey Zakharov
@@ -8,8 +8,17 @@ Author-email: alexey.zakharov@vectioneer.com
8
8
  License: MIT
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: pynng==0.8.*
11
+ Requires-Dist: pynng>=0.9.0
12
12
  Requires-Dist: protobuf>=3.20
13
+ Dynamic: author
14
+ Dynamic: author-email
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: license
19
+ Dynamic: license-file
20
+ Dynamic: requires-dist
21
+ Dynamic: summary
13
22
 
14
23
  # motorcortex-python
15
24
 
@@ -84,11 +84,21 @@ def connect(
84
84
  Establishes connections to Motorcortex request and subscribe endpoints, performs login, and loads the parameter tree.
85
85
 
86
86
  Args:
87
- url (str): Connection URL in the format 'address:req_port:sub_port'.
88
- motorcortex_types (module): Motorcortex message types module.
87
+ url (str): Connection URL in the format 'address:req_port:sub_port' or 'wss://address'.
88
+ motorcortex_types (MessageTypes): Motorcortex message types instance.
89
89
  param_tree (ParameterTree): ParameterTree instance to load parameters into.
90
90
  reconnect (bool, optional): Whether to enable automatic reconnection. Defaults to True.
91
- **kwargs: Additional keyword arguments, including 'login' and 'password' for authentication.
91
+ **kwargs: Additional keyword arguments:
92
+ login (str): Username for authentication.
93
+ password (str): Password for authentication.
94
+ certificate (str, optional): Path to a TLS certificate file for secure connections.
95
+ timeout_ms (int, optional): Connection timeout in milliseconds. Defaults to 1000.
96
+ recv_timeout_ms (int, optional): Receive timeout in milliseconds. Defaults to 500.
97
+ token_update_interval_ms (int, optional): Session token refresh interval in milliseconds. Defaults to 30000.
98
+ state_update (Callable, optional): Custom callback for connection state changes.
99
+ If provided, disables the built-in reconnect logic.
100
+ req_number_of_threads (int, optional): Thread pool size for request connection. Defaults to 2.
101
+ sub_number_of_threads (int, optional): Thread pool size for subscribe connection. Defaults to 2.
92
102
 
93
103
  Returns:
94
104
  tuple: (req, sub)
@@ -100,20 +110,40 @@ def connect(
100
110
 
101
111
  Examples:
102
112
  >>> from motorcortex import connect, MessageTypes, ParameterTree
103
- >>> url = "127.0.0.1:5555:5556"
104
113
  >>> types = MessageTypes()
105
114
  >>> tree = ParameterTree()
106
- >>> req, sub = connect(url, types, tree, certificate="mcx.cert.crt", timeout_ms=1000, login="admin", password="iddqd")
107
- >>> print(tree) # Parameter tree loaded from server
115
+ >>> req, sub = connect("wss://192.168.2.100", types, tree,
116
+ ... certificate="mcx.cert.crt", timeout_ms=1000,
117
+ ... login="admin", password="admin",
118
+ ... token_update_interval_ms=15000)
108
119
  """
109
120
 
121
+ token_interval_sec = kwargs.get("token_update_interval_ms", 30000) / 1000.0
122
+
110
123
  initial_connect_done = [False] # Now reconnections will trigger callback login
111
124
 
112
125
  if reconnect and not kwargs.get("state_update"):
113
126
 
114
127
  def stateUpdate(req, sub, state):
115
128
  if state == ConnectionState.CONNECTION_OK and initial_connect_done[0]:
116
- req.login(kwargs.get("login"), kwargs.get("password")).get()
129
+ # Try to restore session using token, fall back to login
130
+ restored = False
131
+ if req.token:
132
+ try:
133
+ restore_reply = req.restoreSession(req.token)
134
+ restore_msg = restore_reply.get(timeout_ms=5000)
135
+ motorcortex_msg = motorcortex_types.motorcortex()
136
+ if restore_msg and restore_msg.status == motorcortex_msg.OK:
137
+ restored = True
138
+ logger.debug("[SESSION] Session restored using token")
139
+ except Exception as e:
140
+ logger.debug(f"[SESSION] Token restore failed: {e}")
141
+
142
+ if not restored:
143
+ logger.debug("[SESSION] Falling back to login")
144
+ req.login(kwargs.get("login"), kwargs.get("password")).get()
145
+
146
+ req._startTokenRefresh(token_interval_sec)
117
147
  sub.resubscribe()
118
148
 
119
149
  kwargs.update(state_update=stateUpdate)
@@ -143,6 +173,9 @@ def connect(
143
173
  tree = param_tree_reply.get()
144
174
  param_tree.load(tree)
145
175
 
176
+ # Start session token refresh
177
+ req._startTokenRefresh(token_interval_sec)
178
+
146
179
  initial_connect_done[0] = True # Now reconnections will trigger callback login
147
180
 
148
181
  return req, sub
@@ -192,4 +225,4 @@ def statusToStr(motorcortex_msg: object, code: int) -> str:
192
225
 
193
226
  status += str(' (%s)' % hex(code))
194
227
 
195
- return status
228
+ return status
@@ -16,5 +16,5 @@ def init_nng_threads(task=2, expire=1, poller=1, resolver=1):
16
16
  lib.nng_init_set_parameter(lib.NNG_INIT_NUM_EXPIRE_THREADS, expire)
17
17
  lib.nng_init_set_parameter(lib.NNG_INIT_NUM_POLLER_THREADS, poller)
18
18
  lib.nng_init_set_parameter(lib.NNG_INIT_NUM_RESOLVER_THREADS, resolver)
19
- except:
20
- logger.error(f"[INIT-THREADS] Interface is not avaliable")
19
+ except AttributeError:
20
+ logger.error("[INIT-THREADS] Cannot adjust thread count: interface unavailable")
@@ -17,7 +17,7 @@ import hashlib
17
17
  import json
18
18
  import tempfile
19
19
  import os
20
- from threading import Event
20
+ from threading import Event, Timer
21
21
  from concurrent.futures import ThreadPoolExecutor, Future
22
22
  from typing import Any, Callable, List, Optional, Union
23
23
  from pynng import Req0, TLSConfig
@@ -33,7 +33,7 @@ from motorcortex.nng_url import NngUrl
33
33
 
34
34
  class ConnectionState(Enum):
35
35
  """Enumeration of connection states.
36
-
36
+
37
37
  - CONNECTING: Connection is being established.
38
38
  - CONNECTION_OK: Connection is successfully established.
39
39
  - CONNECTION_LOST: Connection was lost.
@@ -152,7 +152,10 @@ class Request:
152
152
  self.__pool: ThreadPoolExecutor = ThreadPoolExecutor(max_workers=number_of_threads,
153
153
  thread_name_prefix="mcx_req")
154
154
  self.__callback_handler: StateCallbackHandler = StateCallbackHandler()
155
- logger.debug("Request object initialized with state: DISCONNECTED")
155
+ self.__token: Optional[str] = None
156
+ self.__token_timer: Optional[Timer] = None
157
+ self.__token_update_interval_sec: float = 30.0
158
+ logger.debug("[REQUEST] Request object initialized with state: DISCONNECTED")
156
159
 
157
160
  def url(self) -> Optional[str]:
158
161
  """Return the current connection URL."""
@@ -176,70 +179,70 @@ class Request:
176
179
  Returns:
177
180
  Reply: A promise that resolves when the connection is established.
178
181
  """
179
- logger.debug(f"[CONNECT] Starting connection to {url}")
180
- logger.debug(f"[CONNECT] Current state: {self.__connection_state.name}")
182
+ logger.debug(f"[REQUEST-CONNECT] Starting connection to {url}")
183
+ logger.debug(f"[REQUEST-CONNECT] Current state: {self.__connection_state.name}")
181
184
 
182
185
  self.__connection_state = ConnectionState.CONNECTING
183
186
  conn_timeout_ms, recv_timeout_ms, certificate, state_update = self.parse(**kwargs)
184
187
 
185
- logger.debug(f"[CONNECT] Parameters - timeout: {conn_timeout_ms} ms, recv_timeout: {recv_timeout_ms} ms, "
188
+ logger.debug(f"[REQUEST-CONNECT] Parameters - timeout: {conn_timeout_ms} ms, recv_timeout: {recv_timeout_ms} ms, "
186
189
  f"has_cert: {certificate is not None}, has_state_update: {state_update is not None}")
187
190
 
188
191
  if state_update:
189
- logger.debug("[CONNECT] Starting state callback handler")
192
+ logger.debug("[REQUEST-CONNECT] Starting state callback handler")
190
193
  self.__callback_handler.start(state_update)
191
194
 
192
195
  self.__url = url
193
196
  tls_config = None
194
197
  if certificate:
195
198
  parsed = NngUrl(url)
196
- logger.debug(f"[CONNECT] Using TLS with certificate: {certificate}")
199
+ logger.debug(f"[REQUEST-CONNECT] Using TLS with certificate: {certificate}")
197
200
  tls_config = TLSConfig(TLSConfig.MODE_CLIENT, ca_files=certificate, server_name=parsed.hostname)
198
201
 
199
- logger.debug(f"[CONNECT] Creating Req0 socket with recv_timeout={recv_timeout_ms}ms")
202
+ logger.debug(f"[REQUEST-CONNECT] Creating Req0 socket with recv_timeout={recv_timeout_ms}ms")
200
203
  self.__socket = Req0(recv_timeout=recv_timeout_ms, tls_config=tls_config)
201
204
  self.__connected_event = Event() # Create a fresh event for each connection
202
205
  self.__connected = False # Reset state
203
206
 
204
- logger.debug("[CONNECT] Socket created, registering callbacks")
207
+ logger.debug("[REQUEST-CONNECT] Socket created, registering callbacks")
205
208
 
206
209
  def pre_connect_cb(_pipe):
207
- logger.debug(f"[CALLBACK] PRE_CONNECT fired - Connection established")
210
+ logger.debug(f"[REQUEST-CALLBACK] PRE_CONNECT fired - Connection established")
208
211
  old_state = self.__connection_state.name
209
212
  self.__connected = True
210
213
  self.__connection_state = ConnectionState.CONNECTION_OK
211
- logger.debug(f"[CALLBACK] State transition: {old_state} -> {self.__connection_state.name}")
214
+ logger.debug(f"[REQUEST-CALLBACK] State transition: {old_state} -> {self.__connection_state.name}")
212
215
  self.__callback_handler.notify(self, self.connectionState())
213
216
  self.__connected_event.set()
214
- logger.debug("[CALLBACK] Connection event set, connected=True")
217
+ logger.debug("[REQUEST-CALLBACK] Connection event set, connected=True")
215
218
 
216
219
  def post_remove_cb(_pipe):
217
- logger.debug(f"[CALLBACK] POST_REMOVE fired - Connection lost/failed")
220
+ logger.debug(f"[REQUEST-CALLBACK] POST_REMOVE fired - Connection lost/failed")
218
221
  old_state = self.__connection_state.name
219
222
 
220
223
  if self.__connection_state == ConnectionState.DISCONNECTING:
221
224
  self.__connection_state = ConnectionState.DISCONNECTED
222
- logger.debug(f"[CALLBACK] Clean disconnect: {old_state} -> DISCONNECTED")
225
+ logger.debug(f"[REQUEST-CALLBACK] Clean disconnect: {old_state} -> DISCONNECTED")
223
226
  elif self.__connection_state == ConnectionState.CONNECTING:
224
227
  self.__connection_state = ConnectionState.CONNECTION_FAILED
225
- logger.debug(f"[CALLBACK] Connection failed: {old_state} -> CONNECTION_FAILED")
228
+ logger.debug(f"[REQUEST-CALLBACK] Connection failed: {old_state} -> CONNECTION_FAILED")
226
229
  elif self.__connection_state == ConnectionState.CONNECTION_OK:
227
230
  self.__connection_state = ConnectionState.CONNECTION_LOST
228
- logger.debug(f"[CALLBACK] Connection lost: {old_state} -> CONNECTION_LOST")
231
+ logger.debug(f"[REQUEST-CALLBACK] Connection lost: {old_state} -> CONNECTION_LOST")
229
232
 
230
233
  self.__connected = False
231
234
  self.__callback_handler.notify(self, self.connectionState())
232
235
  self.__connected_event.set()
233
- logger.debug("[CALLBACK] Connection event set, connected=False")
236
+ logger.debug("[REQUEST-CALLBACK] Connection event set, connected=False")
234
237
 
235
238
  self.__socket.add_pre_pipe_connect_cb(pre_connect_cb)
236
239
  self.__socket.add_post_pipe_remove_cb(post_remove_cb)
237
- logger.debug("[CONNECT] Callbacks registered successfully")
240
+ logger.debug("[REQUEST-CONNECT] Callbacks registered successfully")
238
241
 
239
- logger.debug(f"[CONNECT] Starting dial to {url} (non-blocking)")
242
+ logger.debug(f"[REQUEST-CONNECT] Starting dial to {url} (non-blocking)")
240
243
  self.__socket.dial(url, block=False)
241
244
 
242
- logger.debug(f"[CONNECT] Submitting waitForConnection to thread pool with timeout={conn_timeout_ms / 1000.0}s")
245
+ logger.debug(f"[REQUEST-CONNECT] Submitting waitForConnection to thread pool with timeout={conn_timeout_ms / 1000.0}s")
243
246
  return Reply(self.__pool.submit(self.waitForConnection, self.__connected_event,
244
247
  conn_timeout_ms / 1000.0, lambda: self.__connected))
245
248
 
@@ -247,29 +250,30 @@ class Request:
247
250
  """
248
251
  Close the request connection and clean up resources.
249
252
  """
250
- logger.debug("[CLOSE] Closing connection")
251
- logger.debug(f"[CLOSE] Current state: {self.__connection_state.name}, connected: {self.__connected}")
253
+ logger.debug("[REQUEST-CLOSE] Closing connection")
254
+ logger.debug(f"[REQUEST-CLOSE] Current state: {self.__connection_state.name}, connected: {self.__connected}")
252
255
 
253
256
  self.__connection_state = ConnectionState.DISCONNECTING
257
+ self._stopTokenRefresh()
254
258
  if self.__connected_event:
255
259
  self.__connected = False
256
260
  self.__connected_event.set()
257
- logger.debug("[CLOSE] Connection event set for shutdown")
261
+ logger.debug("[REQUEST-CLOSE] Connection event set for shutdown")
258
262
  else:
259
- logger.debug("[CLOSE] No connection event to set")
263
+ logger.debug("[REQUEST-CLOSE] No connection event to set")
260
264
 
261
265
  if self.__socket:
262
- logger.debug("[CLOSE] Closing socket")
266
+ logger.debug("[REQUEST-CLOSE] Closing socket")
263
267
  self.__socket.close()
264
268
  else:
265
- logger.debug("[CLOSE] No socket to close")
269
+ logger.debug("[REQUEST-CLOSE] No socket to close")
266
270
 
267
- logger.debug("[CLOSE] Stopping callback handler")
271
+ logger.debug("[REQUEST-CLOSE] Stopping callback handler")
268
272
  self.__callback_handler.stop()
269
273
 
270
- logger.debug("[CLOSE] Shutting down thread pool (blocking)")
274
+ logger.debug("[REQUEST-CLOSE] Shutting down thread pool (blocking)")
271
275
  self.__pool.shutdown(wait=True)
272
- logger.debug("[CLOSE] Connection closed successfully")
276
+ logger.debug("[REQUEST-CLOSE] Connection closed successfully")
273
277
 
274
278
  def send(self, encoded_msg: Any, do_not_decode_reply: bool = False) -> Optional[Reply]:
275
279
  """
@@ -285,7 +289,7 @@ class Request:
285
289
  if self.__socket is not None:
286
290
  return Reply(self.__pool.submit(self.__send, self.__socket, encoded_msg,
287
291
  None if do_not_decode_reply else self.__protobuf_types))
288
- logger.debug("[SEND] Attempted to send on null socket - connection not established?")
292
+ logger.debug("[REQUEST-SEND] Attempted to send on null socket - connection not established?")
289
293
  return None
290
294
 
291
295
  def login(self, login: str, password: str) -> Reply:
@@ -527,6 +531,72 @@ class Request:
527
531
  # encoding and sending data
528
532
  return self.send(self.__protobuf_types.encode(remove_group_msg))
529
533
 
534
+ @property
535
+ def token(self) -> Optional[str]:
536
+ """Return the current session token."""
537
+ return self.__token
538
+
539
+ def getSessionToken(self) -> Reply:
540
+ """Request a session token from the server."""
541
+ msg = self.__protobuf_types.createType('motorcortex.GetSessionTokenMsg')
542
+ return self.send(self.__protobuf_types.encode(msg))
543
+
544
+ def restoreSession(self, token: str) -> Reply:
545
+ """Restore a session using a saved token."""
546
+ msg = self.__protobuf_types.createType('motorcortex.RestoreSessionMsg')
547
+ msg.token = token
548
+ return self.send(self.__protobuf_types.encode(msg))
549
+
550
+ def _startTokenRefresh(self, interval_sec: float = 30.0) -> None:
551
+ """Start periodic session token refresh.
552
+
553
+ Args:
554
+ interval_sec: Interval between token refreshes in seconds.
555
+ """
556
+ self.__token_update_interval_sec = interval_sec
557
+ self._stopTokenRefresh()
558
+ # Fetch token immediately, then schedule periodic refresh
559
+ self.__fetchToken()
560
+ self.__scheduleTokenRefresh()
561
+
562
+ def _stopTokenRefresh(self) -> None:
563
+ """Stop periodic session token refresh."""
564
+ if self.__token_timer:
565
+ self.__token_timer.cancel()
566
+ self.__token_timer = None
567
+
568
+ def __scheduleTokenRefresh(self) -> None:
569
+ """Schedule the next token refresh."""
570
+ self._stopTokenRefresh()
571
+ self.__token_timer = Timer(self.__token_update_interval_sec, self.__onTokenTimer)
572
+ self.__token_timer.daemon = True
573
+ self.__token_timer.start()
574
+ logger.debug(f"[REQUEST-KEEPALIVE] Next token refresh in {self.__token_update_interval_sec}s")
575
+
576
+ def __onTokenTimer(self) -> None:
577
+ """Timer callback: fetch token and reschedule."""
578
+ self.__fetchToken()
579
+ # Always reschedule if not disconnecting/disconnected
580
+ if self.__connection_state not in (ConnectionState.DISCONNECTING, ConnectionState.DISCONNECTED):
581
+ self.__scheduleTokenRefresh()
582
+
583
+ def __fetchToken(self) -> None:
584
+ """Fetch a session token from the server."""
585
+ if self.__connection_state != ConnectionState.CONNECTION_OK:
586
+ logger.debug(f"[REQUEST-KEEPALIVE] Skipping token fetch, state: {self.__connection_state.name}")
587
+ return
588
+ try:
589
+ reply = self.getSessionToken()
590
+ if reply:
591
+ msg = reply.get(timeout_ms=5000)
592
+ if msg and hasattr(msg, 'token'):
593
+ self.__token = msg.token
594
+ logger.debug("[REQUEST-KEEPALIVE] Session token updated")
595
+ else:
596
+ logger.debug("[REQUEST-KEEPALIVE] Server returned no token")
597
+ except Exception as e:
598
+ logger.debug(f"[REQUEST-KEEPALIVE] Token refresh failed: {e}")
599
+
530
600
  @staticmethod
531
601
  def __buildSetParameterMsg(
532
602
  path: str,
@@ -791,33 +861,33 @@ class Request:
791
861
  Returns:
792
862
  True if connection is established, raises on failure or timeout.
793
863
  """
794
- logger.debug(f"[WAIT] waitForConnection started with timeout={timeout_sec}s")
864
+ logger.debug(f"[REQUEST-WAIT] waitForConnection started with timeout={timeout_sec}s")
795
865
 
796
866
  # Wait for the condition to be set or timeout
797
867
  if timeout_sec <= 0:
798
- logger.debug("[WAIT] Waiting indefinitely for connection event")
868
+ logger.debug("[REQUEST-WAIT] Waiting indefinitely for connection event")
799
869
  result = event.wait()
800
870
  else:
801
- logger.debug(f"[WAIT] Waiting up to {timeout_sec}s for connection event")
871
+ logger.debug(f"[REQUEST-WAIT] Waiting up to {timeout_sec}s for connection event")
802
872
  result = event.wait(timeout_sec)
803
873
 
804
874
  if not result:
805
- logger.error(f"[WAIT] Connection timeout after {timeout_sec}s - no event received")
875
+ logger.error(f"[REQUEST-WAIT] Connection timeout after {timeout_sec}s - no event received")
806
876
  raise TimeoutError(f"Connection timeout after {timeout_sec}s")
807
877
 
808
- logger.debug("[WAIT] Event received - checking connection status")
878
+ logger.debug("[REQUEST-WAIT] Event received - checking connection status")
809
879
 
810
880
  # Check if we actually connected (event could be set by close() or failure)
811
881
  if is_connected_fn:
812
882
  connected = is_connected_fn()
813
- logger.debug(f"[WAIT] Connection status check: connected={connected}")
883
+ logger.debug(f"[REQUEST-WAIT] Connection status check: connected={connected}")
814
884
  if not connected:
815
- logger.error("[WAIT] Event was set but connection failed or was closed")
885
+ logger.error("[REQUEST-WAIT] Event was set but connection failed or was closed")
816
886
  raise ConnectionError("Connection failed or was closed before completion")
817
887
  else:
818
- logger.debug("[WAIT] No connection status check function provided")
888
+ logger.debug("[REQUEST-WAIT] No connection status check function provided")
819
889
 
820
- logger.debug("[WAIT] Connection successfully established")
890
+ logger.debug("[REQUEST-WAIT] Connection successfully established")
821
891
  return True
822
892
 
823
893
  @staticmethod
@@ -841,10 +911,10 @@ class Request:
841
911
  path = os.sep.join([tempfile.gettempdir(), "mcx-python-pt-" + str(tree_hash.hash)])
842
912
  tree = Request.loadParameterTreeFile(path, protobuf_types)
843
913
  if tree:
844
- logger.debug('Found parameter tree in the cache')
914
+ logger.debug('[REQUEST] Found parameter tree in the cache')
845
915
  return tree
846
916
  else:
847
- logger.debug('Failed to find parameter tree in the cache')
917
+ logger.debug('[REQUEST] Failed to find parameter tree in the cache')
848
918
 
849
919
  # getting and instantiating data type from the loaded dict
850
920
  param_tree_msg = protobuf_types.createType('motorcortex.GetParameterTreeMsg')
@@ -868,7 +938,7 @@ class Request:
868
938
  Returns:
869
939
  The saved ParameterTree instance.
870
940
  """
871
- logger.debug('Saved parameter tree to the cache')
941
+ logger.debug('[REQUEST] Saved parameter tree to the cache')
872
942
  json_data = {}
873
943
  base64_data = base64.b64encode(parameter_tree.SerializeToString())
874
944
  json_data['md5'] = hashlib.md5(base64_data).hexdigest()
@@ -894,7 +964,7 @@ class Request:
894
964
  Returns:
895
965
  The loaded ParameterTree instance, or None if not found/invalid.
896
966
  """
897
- logger.debug('Loaded parameter tree from the cache')
967
+ logger.debug('[REQUEST] Loaded parameter tree from the cache')
898
968
  param_tree_hash_msg = None
899
969
  if os.path.exists(path):
900
970
  with open(path, "r") as outfile:
@@ -907,4 +977,4 @@ class Request:
907
977
  tree_raw = base64.b64decode(json_data['data'])
908
978
  param_tree_hash_msg.ParseFromString(tree_raw)
909
979
 
910
- return param_tree_hash_msg
980
+ return param_tree_hash_msg
@@ -0,0 +1 @@
1
+ __version__ = '0.25.4'
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: motorcortex-python
3
- Version: 0.25.0
3
+ Version: 0.25.4
4
4
  Summary: Python bindings for Motorcortex Engine
5
5
  Home-page: https://www.motorcortex.io
6
6
  Author: Alexey Zakharov
@@ -8,8 +8,17 @@ Author-email: alexey.zakharov@vectioneer.com
8
8
  License: MIT
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: pynng==0.8.*
11
+ Requires-Dist: pynng>=0.9.0
12
12
  Requires-Dist: protobuf>=3.20
13
+ Dynamic: author
14
+ Dynamic: author-email
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: license
19
+ Dynamic: license-file
20
+ Dynamic: requires-dist
21
+ Dynamic: summary
13
22
 
14
23
  # motorcortex-python
15
24
 
@@ -1,6 +1,7 @@
1
1
  LICENSE
2
2
  MANIFEST.in
3
3
  README.md
4
+ pyproject.toml
4
5
  setup.py
5
6
  motorcortex/__init__.py
6
7
  motorcortex/init_threads.py
@@ -1,2 +1,2 @@
1
- pynng==0.8.*
1
+ pynng>=0.9.0
2
2
  protobuf>=3.20
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61,<70", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -33,7 +33,8 @@ setup(name='motorcortex-python',
33
33
  url='https://www.motorcortex.io',
34
34
  license='MIT',
35
35
  packages=['motorcortex'],
36
- install_requires=['pynng==0.8.*',
37
- 'protobuf>=3.20'],
36
+ install_requires=[
37
+ 'pynng>=0.9.0',
38
+ 'protobuf>=3.20'],
38
39
  include_package_data=True,
39
40
  )
@@ -1 +0,0 @@
1
- __version__ = '0.25.0'