motorcortex-python 1.0.3__tar.gz → 1.2.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.
Files changed (39) hide show
  1. {motorcortex_python-1.0.3/motorcortex_python.egg-info → motorcortex_python-1.2.0}/PKG-INFO +19 -1
  2. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/README.md +18 -0
  3. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/__init__.py +1 -1
  4. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/_request_utils.py +57 -0
  5. motorcortex_python-1.2.0/motorcortex/_sp_stream.py +331 -0
  6. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/motorcortex_hash.json +17 -1
  7. motorcortex_python-1.2.0/motorcortex/motorcortex_pb2.py +113 -0
  8. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/motorcortex_pb2.pyi +161 -1
  9. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/request.py +39 -0
  10. motorcortex_python-1.2.0/motorcortex/subscribe.py +576 -0
  11. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/subscription.py +82 -6
  12. motorcortex_python-1.2.0/motorcortex/version.py +1 -0
  13. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0/motorcortex_python.egg-info}/PKG-INFO +19 -1
  14. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex_python.egg-info/SOURCES.txt +1 -0
  15. motorcortex_python-1.0.3/motorcortex/motorcortex_pb2.py +0 -105
  16. motorcortex_python-1.0.3/motorcortex/subscribe.py +0 -400
  17. motorcortex_python-1.0.3/motorcortex/version.py +0 -1
  18. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/LICENSE +0 -0
  19. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/MANIFEST.in +0 -0
  20. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/_connection_state.py +0 -0
  21. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/_request_builders.py +0 -0
  22. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/_subscribe_dispatch.py +0 -0
  23. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/exceptions.py +0 -0
  24. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/init_threads.py +0 -0
  25. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/message_types.py +0 -0
  26. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/nng_url.py +0 -0
  27. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/parameter_tree.py +0 -0
  28. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/py.typed +0 -0
  29. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/reply.py +0 -0
  30. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/session.py +0 -0
  31. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/setup_logger.py +0 -0
  32. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/state_callback_handler.py +0 -0
  33. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex/timespec.py +0 -0
  34. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex_python.egg-info/dependency_links.txt +0 -0
  35. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex_python.egg-info/requires.txt +0 -0
  36. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/motorcortex_python.egg-info/top_level.txt +0 -0
  37. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/pyproject.toml +0 -0
  38. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/setup.cfg +0 -0
  39. {motorcortex_python-1.0.3 → motorcortex_python-1.2.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: motorcortex-python
3
- Version: 1.0.3
3
+ Version: 1.2.0
4
4
  Summary: Python bindings for Motorcortex Engine
5
5
  Home-page: https://www.motorcortex.io
6
6
  Author: Alexey Zakharov
@@ -90,6 +90,24 @@ finally:
90
90
 
91
91
  The URL grammar accepts IPv4, hostnames, and IPv6 literals both with and without explicit ports — e.g. `wss://host`, `wss://host:5568:5567`, `wss://[::1]`, `wss://[fe80::1]:5568:5567`.
92
92
 
93
+ ### Unsubscribing and closing
94
+
95
+ - **`sub.unsubscribe(subscription)`** is the precise control: it removes
96
+ one group from the server and stops its updates. Use it whenever your
97
+ application is done with a specific group but keeps running.
98
+ - **`sub.close()`** ends the subscribe connection for good and
99
+ best-effort-removes any groups you didn't unsubscribe (so a churned
100
+ `Subscribe` doesn't leave stale groups loading the server). `close()`
101
+ is terminal — a closed `Subscribe` cannot be reconnected; create a new
102
+ one (or a new `Session`) instead. Calling `connect()` on a closed
103
+ `Subscribe` resolves to `False`, it never raises.
104
+
105
+ If the subscribe connection drops, the library silently redials in the
106
+ background and fires your `state_update` callback with
107
+ `CONNECTION_LOST` / `CONNECTION_OK`. After a reconnect, call
108
+ `sub.resubscribe()` to restore your groups — `motorcortex.connect()`'s
109
+ default reconnect handler already does this for you.
110
+
93
111
  ## Documentation
94
112
 
95
113
  - **[API reference — `docs/_index.md`](docs/_index.md)** — flat, method-by-method reference for every public class and function. Regenerated from the docstrings via `pydoc-markdown`.
@@ -65,6 +65,24 @@ finally:
65
65
 
66
66
  The URL grammar accepts IPv4, hostnames, and IPv6 literals both with and without explicit ports — e.g. `wss://host`, `wss://host:5568:5567`, `wss://[::1]`, `wss://[fe80::1]:5568:5567`.
67
67
 
68
+ ### Unsubscribing and closing
69
+
70
+ - **`sub.unsubscribe(subscription)`** is the precise control: it removes
71
+ one group from the server and stops its updates. Use it whenever your
72
+ application is done with a specific group but keeps running.
73
+ - **`sub.close()`** ends the subscribe connection for good and
74
+ best-effort-removes any groups you didn't unsubscribe (so a churned
75
+ `Subscribe` doesn't leave stale groups loading the server). `close()`
76
+ is terminal — a closed `Subscribe` cannot be reconnected; create a new
77
+ one (or a new `Session`) instead. Calling `connect()` on a closed
78
+ `Subscribe` resolves to `False`, it never raises.
79
+
80
+ If the subscribe connection drops, the library silently redials in the
81
+ background and fires your `state_update` callback with
82
+ `CONNECTION_LOST` / `CONNECTION_OK`. After a reconnect, call
83
+ `sub.resubscribe()` to restore your groups — `motorcortex.connect()`'s
84
+ default reconnect handler already does this for you.
85
+
68
86
  ## Documentation
69
87
 
70
88
  - **[API reference — `docs/_index.md`](docs/_index.md)** — flat, method-by-method reference for every public class and function. Regenerated from the docstrings via `pydoc-markdown`.
@@ -286,7 +286,7 @@ def connect(
286
286
  state_update (Callable, optional): Custom callback for connection state changes.
287
287
  If provided, disables the built-in reconnect logic.
288
288
  req_number_of_threads (int, optional): Thread pool size for request connection. Defaults to 2.
289
- sub_number_of_threads (int, optional): Thread pool size for subscribe connection. Defaults to 2.
289
+ sub_number_of_threads (int, optional): Thread pool size for subscribe connection. Defaults to 2, clamped up to the minimum of 4 that ``Subscribe`` requires (connect worker, recv loop, reconnect loop, subscription updaters).
290
290
 
291
291
  Returns:
292
292
  tuple: (req, sub)
@@ -19,6 +19,7 @@ import hashlib
19
19
  import json
20
20
  import os
21
21
  import tempfile
22
+ import zlib
22
23
  from threading import Event
23
24
  from typing import Any, Callable, Optional, Tuple
24
25
 
@@ -135,6 +136,26 @@ def fetch_parameter_tree(
135
136
  Returns:
136
137
  A ``ParameterTreeMsg`` — either the cached one or the freshly
137
138
  fetched one (also persisted to the cache).
139
+
140
+ On a cache miss, a compressed tree is tried first
141
+ (``motorcortex-core >= 2.36.0``): a single zlib/deflate blob,
142
+ typically 7-13x smaller than the plain reply. The server deflates
143
+ the *full wire reply* — a 4-byte little-endian type-hash prefix
144
+ followed by the ``ParameterTreeMsg`` body, same as any other RPC
145
+ reply — so the inflated blob goes through the canonical
146
+ ``MessageTypes.decode()``, which strips the prefix and resolves the
147
+ type by hash. Any failure — timeout on an old server that silently
148
+ drops the unrecognised request hash, a non-OK status from a
149
+ 2.36.1-2.38 core, an unknown or unexpected type hash, or an
150
+ inflate/parse error — falls through to the plain
151
+ ``GetParameterTreeMsg`` request. Both paths decode to the same
152
+ ``ParameterTreeMsg`` type, so callers (and the cache write below)
153
+ can't tell the difference. Tried for every URL scheme (ipc, tcp,
154
+ ws, tls+tcp, wss) — the request is just bytes over
155
+ :func:`send_and_recv`. ``send_and_recv`` in this repo has no
156
+ per-call timeout knob, so on an old server the compressed attempt
157
+ costs at most one ``recv_timeout`` (the socket's own) before
158
+ falling back.
138
159
  """
139
160
  path = parameter_tree_cache_path(url, tree_hash)
140
161
  cached = load_parameter_tree_file(path, protobuf_types)
@@ -143,6 +164,42 @@ def fetch_parameter_tree(
143
164
  return cached
144
165
  logger.debug("[REQUEST] Failed to find parameter tree in the cache")
145
166
 
167
+ try:
168
+ compressed_request = protobuf_types.createType(
169
+ "motorcortex.GetParameterTreeCompressedMsg")
170
+ compressed_reply = send_and_recv(
171
+ socket, protobuf_types.encode(compressed_request), protobuf_types)
172
+ if compressed_reply is not None \
173
+ and getattr(compressed_reply, "status", 1) == 0 \
174
+ and getattr(compressed_reply, "compressed", b""):
175
+ # The server deflates the *full wire reply* here, not a bare
176
+ # ParameterTreeMsg: inflating yields the same hash-prefixed
177
+ # framing every other RPC reply uses (matches the message's
178
+ # own docstring: "the exact bytes a GetParameterTreeMsg reply
179
+ # carries"). Run it through the canonical decode() — which
180
+ # strips the type-hash prefix and resolves the type by hash —
181
+ # instead of ParseFromString on the raw blob; parsing the blob
182
+ # directly is exactly the bug this branch used to have: the
183
+ # prefix corrupted every parse and silently forced the plain
184
+ # fallback. An unknown hash (KeyError in decode) or a decode
185
+ # to some other type falls through to the plain request rather
186
+ # than caching garbage as a ParameterTreeMsg.
187
+ tree_msg = protobuf_types.decode(
188
+ zlib.decompress(compressed_reply.compressed))
189
+ decoded_name = getattr(
190
+ getattr(tree_msg, "DESCRIPTOR", None), "full_name", None)
191
+ if decoded_name != "motorcortex.ParameterTreeMsg":
192
+ raise ValueError(
193
+ "compressed tree decoded to unexpected type "
194
+ f"{decoded_name or type(tree_msg).__name__}")
195
+ logger.debug("[REQUEST] parameter tree fetched compressed")
196
+ return save_parameter_tree_file(path, tree_msg)
197
+ except Exception as e:
198
+ logger.debug(
199
+ "[REQUEST] compressed tree unavailable (%s: %s) — falling back to plain",
200
+ type(e).__name__, e,
201
+ )
202
+
146
203
  request_msg = protobuf_types.createType("motorcortex.GetParameterTreeMsg")
147
204
  handle = send_and_recv(socket, protobuf_types.encode(request_msg), protobuf_types)
148
205
  return save_parameter_tree_file(path, handle)
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/python3
2
+ #
3
+ # Developer: Alexey Zakharov (alexey.zakharov@vectioneer.com)
4
+ # All rights reserved. Copyright (c) 2016-2026 VECTIONEER.
5
+ #
6
+ """Stream-based SP (nanomsg) pub/sub client transport.
7
+
8
+ Replaces the pynng ``Sub0`` socket for the subscribe data channel so the
9
+ client can send the routing hello as the first upstream message — a raw
10
+ ``nng_stream`` has no protocol layer, so we speak the SP wire format
11
+ ourselves. Uses only pynng's bundled cffi bindings (``pynng._nng``): the
12
+ full public ``nng_stream_* / nng_aio_*`` API ships in every pynng wheel.
13
+
14
+ Wire format per scheme (mirrors motorcortex-cpp src/sp_stream.cpp, which
15
+ is the proven reference — see its comments for the full rationale):
16
+
17
+ - ws/wss: subprotocol ``pub.sp.nanomsg.org`` on the HTTP upgrade;
18
+ message mode — one send/recv is one SP message, no length
19
+ prefix, no byte handshake.
20
+ - tcp/tls: 8-byte handshake ``\\x00SP\\x00`` + u16-BE proto + u16-BE 0
21
+ (we announce 33/sub, require peer 32/pub), then each message
22
+ is a u64-BE length prefix + body.
23
+ - ipc: same handshake, but a 9-byte per-message header: type byte
24
+ ``0x01`` + u64-BE length. The type byte is load-bearing.
25
+
26
+ nng 1.11.0 caveat (learned in cpp): never ``nng_aio_cancel`` an aio on a
27
+ raw TLS stream — it crashes ``nng_stream_close`` intermittently. This
28
+ implementation never cancels: every aio is owned by exactly one blocking
29
+ call which waits for it to finish (success, error, or timeout) before
30
+ freeing it. Closing the stream from another thread makes a pending recv
31
+ finish with an error, which is the supported wakeup path.
32
+
33
+ Stream lifetime vs. concurrent close(): a bare local capture of
34
+ ``self._stream`` before an nng call is not enough — close() both nulls
35
+ the attribute *and* frees the underlying stream, so a racing capture can
36
+ still end up handing a freed pointer to nng between close()'s two steps.
37
+ Every blocking nng call therefore goes through ``_issue()``, which
38
+ captures the stream, increments ``_io_inflight``, AND actually calls
39
+ ``nng_stream_send``/``nng_stream_recv`` — all under ``_io_cond`` in one
40
+ atomic step (not just the capture: an earlier version issued the nng
41
+ call *after* releasing the lock, which left a window where
42
+ ``nng_stream_close`` could run first and the op would then be issued
43
+ against an already-closed stream — nng accepts that silently and never
44
+ completes it, hanging ``nng_aio_wait`` forever when the aio has no
45
+ timeout of its own, i.e. exactly ``recv_msg()``'s default). close() sets
46
+ ``_closing``/nulls ``self._stream`` under the same lock, calls
47
+ ``nng_stream_close`` (which wakes any pending aio — pending meaning
48
+ already issued, which ``_issue()`` now guarantees), and only frees the
49
+ stream once ``_io_inflight`` drops back to zero. That ordering — close,
50
+ then wait for in-flight callers to finish with the local pointer they
51
+ already captured, then free — is what makes the free race-free without
52
+ ever touching ``nng_aio_cancel``.
53
+ """
54
+
55
+ import struct
56
+ import threading
57
+ from typing import Optional
58
+
59
+ from pynng._nng import ffi, lib # type: ignore[import-untyped]
60
+ from pynng.exceptions import check_err # type: ignore[import-untyped]
61
+
62
+ from motorcortex.setup_logger import logger
63
+
64
+ _SP_PROTO_SUB = 33
65
+ _SP_PROTO_PUB = 32
66
+ _HANDSHAKE_LEN = 8
67
+
68
+ #: One recv buffer per stream in ws message mode. nng's ws transport caps
69
+ #: read-ahead at one frame and the server pipe buffer is 1 MiB, so a
70
+ #: larger message cannot arrive whole (it would be truncated server-side
71
+ #: first). Mirrors WS_MAX_MSG in cpp sp_stream.h.
72
+ WS_MAX_MSG = 1 << 20
73
+
74
+ #: Refuse absurd length prefixes on byte-stream schemes (64 MiB).
75
+ _MAX_BODY = 1 << 26
76
+
77
+
78
+ class SpStreamError(Exception):
79
+ """Transport-level failure on the SP stream."""
80
+
81
+
82
+ class SpProtocolError(SpStreamError):
83
+ """Peer completed the byte handshake but is not an SP pub (32)."""
84
+
85
+
86
+ def _tls_pointer(tls_config):
87
+ """Raw ``nng_tls_config *`` out of a pynng TLSConfig.
88
+
89
+ ``TLSConfig._tls_config`` is private but stable across pynng 0.9.x;
90
+ isolate the access here so a pynng bump touches one line.
91
+ """
92
+ return tls_config._tls_config
93
+
94
+
95
+ def _alloc_aio(timeout_ms: int):
96
+ ap = ffi.new("nng_aio **")
97
+ check_err(lib.nng_aio_alloc(ap, ffi.NULL, ffi.NULL))
98
+ aio = ap[0]
99
+ lib.nng_aio_set_timeout(aio, timeout_ms if timeout_ms > 0 else -1)
100
+ return aio
101
+
102
+
103
+ class SpStream:
104
+ def __init__(self) -> None:
105
+ self._dialer = None
106
+ self._stream = None
107
+ self._ws_mode = False
108
+ self._ipc_mode = False
109
+ self._recv_buf = None # allocated lazily in Task 2
110
+ # Guards the stream-lifetime TOCTOU between a live nng call and a
111
+ # concurrent close(); see the module docstring.
112
+ self._io_cond = threading.Condition()
113
+ self._io_inflight = 0
114
+ self._closing = False
115
+
116
+ @property
117
+ def connected(self) -> bool:
118
+ return self._stream is not None
119
+
120
+ def connect(self, url: str, tls_config=None, timeout_ms: int = 5000) -> None:
121
+ """Dial + (non-ws) SP handshake. Raises on any failure; the
122
+ stream is fully closed again before an exception propagates."""
123
+ self.close()
124
+ # Re-arm under _io_cond, only after close() fully completed, so
125
+ # the flag flip is atomic against a concurrent close()'s own
126
+ # locked ``_closing = True`` transition (a close() racing the
127
+ # reopen after this point is caught by Subscribe's ``_closed``
128
+ # re-checks, which close any stream dialed past it).
129
+ with self._io_cond:
130
+ self._closing = False
131
+ self._ws_mode = url.startswith("ws://") or url.startswith("wss://")
132
+ self._ipc_mode = url.startswith("ipc://")
133
+
134
+ dp = ffi.new("nng_stream_dialer **")
135
+ check_err(lib.nng_stream_dialer_alloc(dp, url.encode()))
136
+ self._dialer = dp[0]
137
+ try:
138
+ if self._ws_mode:
139
+ # mcxpub0 keeps stock pub0's SP wire name, so every
140
+ # existing client's subprotocol offer is accepted.
141
+ check_err(lib.nng_stream_dialer_set_string(
142
+ self._dialer, b"ws:protocol", b"pub.sp.nanomsg.org"))
143
+ if tls_config is not None:
144
+ check_err(lib.nng_stream_dialer_set_ptr(
145
+ self._dialer, b"tls-config", _tls_pointer(tls_config)))
146
+
147
+ aio = _alloc_aio(timeout_ms)
148
+ lib.nng_stream_dialer_dial(self._dialer, aio)
149
+ lib.nng_aio_wait(aio)
150
+ rv = lib.nng_aio_result(aio)
151
+ if rv == 0:
152
+ self._stream = ffi.cast(
153
+ "nng_stream *", lib.nng_aio_get_output(aio, 0))
154
+ lib.nng_aio_free(aio)
155
+ check_err(rv)
156
+
157
+ if not self._ws_mode:
158
+ self._handshake(timeout_ms)
159
+ except Exception:
160
+ self.close()
161
+ raise
162
+
163
+ def _handshake(self, timeout_ms: int) -> None:
164
+ ours = b"\x00SP\x00" + struct.pack(">H", _SP_PROTO_SUB) + b"\x00\x00"
165
+ self._io(True, ours, timeout_ms)
166
+ peer = self._io(False, _HANDSHAKE_LEN, timeout_ms)
167
+ expected = b"\x00SP\x00" + struct.pack(">H", _SP_PROTO_PUB) + b"\x00\x00"
168
+ if peer[:6] != expected[:6]:
169
+ raise SpProtocolError(
170
+ f"peer is not an SP pub socket (got {peer!r})")
171
+
172
+ def send_msg(self, data: bytes, timeout_ms: int = 2000) -> None:
173
+ if self._stream is None:
174
+ raise SpStreamError("send on a closed SpStream")
175
+ if self._ws_mode:
176
+ self._io(True, data, timeout_ms) # message mode: 1 send = 1 msg
177
+ return
178
+ prefix = struct.pack(">Q", len(data))
179
+ if self._ipc_mode:
180
+ prefix = b"\x01" + prefix # load-bearing type byte
181
+ self._io(True, prefix, timeout_ms)
182
+ self._io(True, data, timeout_ms)
183
+
184
+ def _issue(self, op, buf, offset: int, length: int, timeout_ms: int):
185
+ """Allocate an aio, set its iov, and hand it to ``op`` (a raw
186
+ ``nng_stream_send``/``nng_stream_recv`` — the *call*, not just
187
+ the registration) — all atomically under ``_io_cond``.
188
+
189
+ This has to be one critical section, not "capture the stream,
190
+ release the lock, then call ``op``" (the original shape): a
191
+ concurrent ``close()`` also serializes on ``_io_cond`` for its
192
+ own ``_closing = True`` transition, so whichever of the two
193
+ critical sections runs first fully determines a safe outcome —
194
+ either the op is issued while the stream is still known-open
195
+ (and ``close()``'s later ``nng_stream_close()`` correctly wakes
196
+ it, per nng's contract for *already-registered* ops), or
197
+ ``close()`` has already flipped ``_closing`` and this call bails
198
+ out before ever touching nng. The two-step version left a gap
199
+ between "session granted" / "op actually issued" that
200
+ ``close()`` could land in: ``nng_stream_close()`` running before
201
+ ``nng_stream_recv()``/``nng_stream_send()`` for that same aio
202
+ leaves it waiting forever — with a finite ``timeout_ms`` the
203
+ aio's own timer eventually rescues it, but ``recv_msg()``'s
204
+ default infinite timeout (the receive loop's steady state) has
205
+ no such backstop, so this used to hang the process instead of
206
+ surfacing the intended "stream closed" `None`.
207
+
208
+ Raises ``SpStreamError`` (stream already closing/closed) or lets
209
+ an ``nng_aio_set_iov`` failure propagate — in both cases nothing
210
+ was issued and ``_io_inflight`` was never incremented, so the
211
+ caller has nothing to undo. On success returns the aio with
212
+ ``_io_inflight`` already bumped; the caller owns freeing it and
213
+ decrementing the counter once the op settles.
214
+ """
215
+ with self._io_cond:
216
+ if self._closing or self._stream is None:
217
+ raise SpStreamError("stream closed mid-transfer")
218
+ stream = self._stream
219
+ aio = _alloc_aio(timeout_ms)
220
+ try:
221
+ iov = ffi.new("nng_iov *")
222
+ iov.iov_buf = buf + offset
223
+ iov.iov_len = length
224
+ check_err(lib.nng_aio_set_iov(aio, 1, iov))
225
+ except Exception:
226
+ lib.nng_aio_free(aio)
227
+ raise
228
+ self._io_inflight += 1
229
+ op(stream, aio)
230
+ return aio
231
+
232
+ def _settle(self, aio):
233
+ """Wait for an ``_issue()``-returned aio outside the lock (the
234
+ blocking part must never hold ``_io_cond`` — that would stall
235
+ ``close()``'s own attempt to grab it), then release the
236
+ in-flight slot. Returns ``(rv, got)``."""
237
+ try:
238
+ lib.nng_aio_wait(aio)
239
+ rv = lib.nng_aio_result(aio)
240
+ got = lib.nng_aio_count(aio) if rv == 0 else 0
241
+ finally:
242
+ lib.nng_aio_free(aio)
243
+ with self._io_cond:
244
+ self._io_inflight -= 1
245
+ self._io_cond.notify_all()
246
+ return rv, got
247
+
248
+ def _io(self, is_send: bool, data_or_len, timeout_ms: int):
249
+ """Blocking send of ``bytes`` / recv of exactly N bytes.
250
+ One aio per call; always waited on, never cancelled. Every nng
251
+ call is issued via ``_issue()`` so it can't race a concurrent
252
+ close() into hanging (see its docstring)."""
253
+ if is_send:
254
+ buf = ffi.new("uint8_t[]", bytes(data_or_len))
255
+ need = len(data_or_len)
256
+ else:
257
+ need = int(data_or_len)
258
+ buf = ffi.new("uint8_t[]", need)
259
+ op = lib.nng_stream_send if is_send else lib.nng_stream_recv
260
+ done = 0
261
+ while done < need:
262
+ aio = self._issue(op, buf, done, need - done, timeout_ms)
263
+ rv, got = self._settle(aio)
264
+ if rv != 0:
265
+ check_err(rv)
266
+ if got == 0:
267
+ raise SpStreamError("stream closed mid-transfer")
268
+ done += got
269
+ return None if is_send else bytes(ffi.buffer(buf, need))
270
+
271
+ def recv_msg(self, timeout_ms: int = -1) -> Optional[bytes]:
272
+ """Block for one whole SP message.
273
+
274
+ Returns ``None`` when the stream is closed/dead (remote close,
275
+ local close() from another thread, desync) — the receive loop's
276
+ exit signal. The ws invariant (nng caps ws read-ahead at one
277
+ frame) means one recv into a WS_MAX_MSG buffer is exactly one
278
+ message; byte-stream schemes reassemble via the length prefix.
279
+ """
280
+ if self._stream is None:
281
+ return None
282
+ try:
283
+ if self._ws_mode:
284
+ return self._recv_ws(timeout_ms)
285
+ hdr_len = 9 if self._ipc_mode else 8
286
+ hdr = self._io(False, hdr_len, timeout_ms)
287
+ if self._ipc_mode:
288
+ if hdr[0] != 0x01:
289
+ logger.error("[SPSTREAM] ipc type-byte desync (0x%02x)", hdr[0])
290
+ return None
291
+ hdr = hdr[1:]
292
+ (length,) = struct.unpack(">Q", hdr)
293
+ if length == 0 or length > _MAX_BODY:
294
+ logger.error("[SPSTREAM] absurd frame length %d — desync", length)
295
+ return None
296
+ return self._io(False, length, timeout_ms)
297
+ except Exception as e:
298
+ logger.debug("[SPSTREAM] recv_msg terminating: %s", e)
299
+ return None
300
+
301
+ def _recv_ws(self, timeout_ms: int) -> Optional[bytes]:
302
+ if self._recv_buf is None:
303
+ self._recv_buf = ffi.new("uint8_t[]", WS_MAX_MSG)
304
+ try:
305
+ aio = self._issue(lib.nng_stream_recv, self._recv_buf, 0, WS_MAX_MSG, timeout_ms)
306
+ except Exception:
307
+ return None
308
+ rv, got = self._settle(aio)
309
+ if rv != 0 or got == 0:
310
+ return None
311
+ return bytes(ffi.buffer(self._recv_buf, got))
312
+
313
+ def close(self) -> None:
314
+ with self._io_cond:
315
+ self._closing = True
316
+ stream, self._stream = self._stream, None
317
+ dialer, self._dialer = self._dialer, None
318
+ if stream is not None:
319
+ lib.nng_stream_close(stream) # wakes any pending recv/send
320
+ # Wait for every in-flight _issue()/_settle() caller to
321
+ # finish with the (now-closed, error-returning) stream
322
+ # pointer it already captured before we free it — this is
323
+ # what makes the free race-free; see module docstring and
324
+ # _issue()'s docstring for why issuing the op is folded into
325
+ # the same critical section as this ``_closing`` flip.
326
+ with self._io_cond:
327
+ while self._io_inflight > 0:
328
+ self._io_cond.wait()
329
+ lib.nng_stream_free(stream)
330
+ if dialer is not None:
331
+ lib.nng_stream_dialer_free(dialer)
@@ -43,6 +43,14 @@
43
43
  "type": "motorcortex.SessionTokenMsg",
44
44
  "hash": "0x7c3e91ab"
45
45
  },
46
+ {
47
+ "type": "motorcortex.GetSubCookieMsg",
48
+ "hash": "0x5505c0b0"
49
+ },
50
+ {
51
+ "type": "motorcortex.SubCookieMsg",
52
+ "hash": "0x90ff084b"
53
+ },
46
54
  {
47
55
  "type": "motorcortex.RestoreSessionMsg",
48
56
  "hash": "0x8aef888a"
@@ -67,6 +75,14 @@
67
75
  "type": "motorcortex.ParameterTreeHashMsg",
68
76
  "hash": "0xc8cef26b"
69
77
  },
78
+ {
79
+ "type": "motorcortex.GetParameterTreeCompressedMsg",
80
+ "hash": "0x2347001b"
81
+ },
82
+ {
83
+ "type": "motorcortex.CompressedParameterTreeMsg",
84
+ "hash": "0x769fb6e8"
85
+ },
70
86
  {
71
87
  "type": "motorcortex.CreateGroupMsg",
72
88
  "hash": "0x9eab1b83"
@@ -149,7 +165,7 @@
149
165
  },
150
166
  {
151
167
  "type": "motorcortex.StatusCode",
152
- "hash": "0xd785e9fa"
168
+ "hash": "0x6a1f3a02"
153
169
  },
154
170
  {
155
171
  "type": "motorcortex.ErrorLevel",
@@ -0,0 +1,113 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: motorcortex.proto
4
+ """Generated protocol buffer code."""
5
+ from google.protobuf.internal import builder as _builder
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import descriptor_pool as _descriptor_pool
8
+ from google.protobuf import symbol_database as _symbol_database
9
+ # @@protoc_insertion_point(imports)
10
+
11
+ _sym_db = _symbol_database.Default()
12
+
13
+
14
+
15
+
16
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11motorcortex.proto\x12\x0bmotorcortex\"i\n\x0fParameterOffset\x12\x36\n\x04type\x18\x01 \x02(\x0e\x32\x17.motorcortex.OffsetType:\x0fOFFSET_ELEMENTS\x12\x0e\n\x06offset\x18\x02 \x02(\r\x12\x0e\n\x06length\x18\x03 \x02(\r\"f\n\x05\x45rror\x12\x11\n\ttimestamp\x18\x01 \x02(\x06\x12\x14\n\x0c\x65rror_number\x18\x02 \x02(\x07\x12\x13\n\x0b\x65rror_level\x18\x03 \x02(\x07\x12\x11\n\tsubsystem\x18\x04 \x02(\x07\x12\x0c\n\x04info\x18\x05 \x02(\x07\"a\n\tErrorList\x12\"\n\x06\x65rrors\x18\x01 \x03(\x0b\x32\x12.motorcortex.Error\x12\x18\n\x10number_of_errors\x18\x02 \x02(\x07\x12\x16\n\x0eupdate_counter\x18\x03 \x02(\x07\"\x8a\x02\n\rParameterInfo\x12\n\n\x02id\x18\x01 \x02(\r\x12\x11\n\tdata_type\x18\x02 \x02(\r\x12\x11\n\tdata_size\x18\x03 \x02(\r\x12\x1a\n\x12number_of_elements\x18\x04 \x02(\r\x12\r\n\x05\x66lags\x18\x05 \x02(\r\x12\x13\n\x0bpermissions\x18\x06 \x02(\r\x12.\n\nparam_type\x18\x07 \x02(\x0e\x32\x1a.motorcortex.ParameterType\x12(\n\x08group_id\x18\x08 \x02(\x0e\x32\x16.motorcortex.UserGroup\x12\x1f\n\x04unit\x18\t \x02(\x0e\x32\x11.motorcortex.Unit\x12\x0c\n\x04path\x18\n \x02(\t\"\x94\x01\n\x12GroupParameterInfo\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06offset\x18\x02 \x02(\r\x12\x0c\n\x04size\x18\x03 \x02(\r\x12(\n\x04info\x18\x04 \x02(\x0b\x32\x1a.motorcortex.ParameterInfo\x12\'\n\x06status\x18\x05 \x02(\x0e\x32\x17.motorcortex.StatusCode\"1\n\x06Header\x12\x14\n\x0c\x66rameCounter\x18\x01 \x02(\x07\x12\x11\n\ttimestamp\x18\x02 \x02(\x06\"Z\n\x08GroupMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12)\n\x06params\x18\x02 \x03(\x0b\x32\x19.motorcortex.ParameterMsg\"Y\n\tStatusMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\'\n\x06status\x18\x02 \x02(\x0e\x32\x17.motorcortex.StatusCode\"P\n\x08LoginMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\r\n\x05login\x18\x02 \x02(\t\x12\x10\n\x08password\x18\x03 \x02(\t\"9\n\x12GetSessionTokenMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\"n\n\x0fSessionTokenMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\r\n\x05token\x18\x02 \x02(\t\x12\'\n\x06status\x18\x03 \x02(\x0e\x32\x17.motorcortex.StatusCode\"6\n\x0fGetSubCookieMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\"l\n\x0cSubCookieMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x0e\n\x06\x63ookie\x18\x02 \x02(\t\x12\'\n\x06status\x18\x03 \x02(\x0e\x32\x17.motorcortex.StatusCode\"G\n\x11RestoreSessionMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\r\n\x05token\x18\x02 \x02(\t\"0\n\tLogoutMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\":\n\x13GetParameterTreeMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\"\x9a\x01\n\x10ParameterTreeMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12*\n\x06params\x18\x02 \x03(\x0b\x32\x1a.motorcortex.ParameterInfo\x12\x0c\n\x04hash\x18\x03 \x02(\r\x12\'\n\x06status\x18\x04 \x02(\x0e\x32\x17.motorcortex.StatusCode\">\n\x17GetParameterTreeHashMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\"r\n\x14ParameterTreeHashMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x0c\n\x04hash\x18\x02 \x02(\r\x12\'\n\x06status\x18\x03 \x02(\x0e\x32\x17.motorcortex.StatusCode\"D\n\x1dGetParameterTreeCompressedMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\"\xa7\x01\n\x1a\x43ompressedParameterTreeMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x12\n\ncompressed\x18\x02 \x02(\x0c\x12\x19\n\x11uncompressed_size\x18\x03 \x02(\r\x12\x0c\n\x04hash\x18\x04 \x02(\r\x12\'\n\x06status\x18\x05 \x02(\x0e\x32\x17.motorcortex.StatusCode\"h\n\x0e\x43reateGroupMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x13\n\x0b\x66rq_divider\x18\x02 \x02(\r\x12\r\n\x05\x61lias\x18\x03 \x02(\t\x12\r\n\x05paths\x18\x04 \x03(\t\"\xaa\x01\n\x0eGroupStatusMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\n\n\x02id\x18\x02 \x02(\r\x12\r\n\x05\x61lias\x18\x03 \x02(\t\x12/\n\x06params\x18\x04 \x03(\x0b\x32\x1f.motorcortex.GroupParameterInfo\x12\'\n\x06status\x18\x05 \x02(\x0e\x32\x17.motorcortex.StatusCode\"D\n\x0eRemoveGroupMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\r\n\x05\x61lias\x18\x02 \x02(\t\"D\n\x0fGetParameterMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x0c\n\x04path\x18\x02 \x02(\t\"\x95\x01\n\x0cParameterMsg\x12\r\n\x05value\x18\x01 \x02(\x0c\x12#\n\x06header\x18\x02 \x01(\x0b\x32\x13.motorcortex.Header\x12(\n\x04info\x18\x03 \x01(\x0b\x32\x1a.motorcortex.ParameterInfo\x12\'\n\x06status\x18\x04 \x02(\x0e\x32\x17.motorcortex.StatusCode\"h\n\x13GetParameterListMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12,\n\x06params\x18\x02 \x03(\x0b\x32\x1c.motorcortex.GetParameterMsg\"\x8b\x01\n\x10ParameterListMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12)\n\x06params\x18\x02 \x03(\x0b\x32\x19.motorcortex.ParameterMsg\x12\'\n\x06status\x18\x03 \x02(\x0e\x32\x17.motorcortex.StatusCode\"\x81\x01\n\x0fSetParameterMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12,\n\x06offset\x18\x02 \x01(\x0b\x32\x1c.motorcortex.ParameterOffset\x12\x0c\n\x04path\x18\x03 \x02(\t\x12\r\n\x05value\x18\x04 \x02(\x0c\"h\n\x13SetParameterListMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12,\n\x06params\x18\x02 \x03(\x0b\x32\x1c.motorcortex.SetParameterMsg\"\x99\x01\n\x15OverwriteParameterMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12,\n\x06offset\x18\x02 \x01(\x0b\x32\x1c.motorcortex.ParameterOffset\x12\x10\n\x08\x61\x63tivate\x18\x03 \x02(\x08\x12\x0c\n\x04path\x18\x04 \x02(\t\x12\r\n\x05value\x18\x05 \x02(\x0c\"H\n\x13ReleaseParameterMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x0c\n\x04path\x18\x02 \x02(\t\"O\n\x07SaveMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x0c\n\x04path\x18\x02 \x02(\t\x12\x11\n\tfile_name\x18\x03 \x02(\t\"O\n\x07LoadMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\x0c\n\x04path\x18\x02 \x02(\t\x12\x11\n\tfile_name\x18\x03 \x02(\t\"C\n\rConsoleCmdMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12\r\n\x05value\x18\x02 \x02(\t\"b\n\x11\x43onsoleCmdListMsg\x12#\n\x06header\x18\x01 \x01(\x0b\x32\x13.motorcortex.Header\x12(\n\x04\x63mds\x18\x02 \x03(\x0b\x32\x1a.motorcortex.ConsoleCmdMsg*\xab\x02\n\x04Unit\x12\x12\n\x0eunit_undefined\x10\x00\x12\n\n\x06Length\x10\x0f\x12\x06\n\x02mm\x10\x01\x12\x05\n\x01m\x10\x02\x12\n\n\x05\x41ngle\x10\xf1\x01\x12\x07\n\x03rad\x10\x31\x12\x07\n\x03\x64\x65g\x10\x41\x12\t\n\x04Time\x10\xf2\x01\x12\x0b\n\x07nanosec\x10\x12\x12\x0c\n\x08microsec\x10\"\x12\x0c\n\x08millisec\x10\x32\x12\x07\n\x03sec\x10\x42\x12\x0b\n\x06Weight\x10\xf3\x01\x12\x08\n\x04gram\x10\x13\x12\x06\n\x02kg\x10#\x12\r\n\x08Velocity\x10\xf4\x01\x12\t\n\x05m_sec\x10\x14\x12\x0b\n\x07rad_sec\x10$\x12\x11\n\x0c\x41\x63\x63\x65leration\x10\xf5\x01\x12\n\n\x06m_sec2\x10\x15\x12\x0c\n\x08rad_sec2\x10%\x12\n\n\x05\x46orce\x10\xf6\x01\x12\x05\n\x01N\x10\x16\x12\x06\n\x02Nm\x10&\x12\x0b\n\x07percent\x10\x17*_\n\tUserGroup\x12\x18\n\x14user_group_undefined\x10\x00\x12\n\n\x06SYSTEM\x10\x01\x12\x11\n\rADMINISTRATOR\x10\x03\x12\x0c\n\x08OPERATOR\x10\x07\x12\x0b\n\x07MONITOR\x10\x0f*\xc4\x01\n\nPermission\x12\x18\n\x14permission_undefined\x10\x00\x12\x0e\n\tUSER_READ\x10\x80\x02\x12\x0f\n\nUSER_WRITE\x10\x80\x01\x12\x10\n\x0cUSER_EXECUTE\x10@\x12\x0e\n\nGROUP_READ\x10 \x12\x0f\n\x0bGROUP_WRITE\x10\x10\x12\x11\n\rGROUP_EXECUTE\x10\x08\x12\x0f\n\x0bOTHERS_READ\x10\x04\x12\x10\n\x0cOTHERS_WRITE\x10\x02\x12\x12\n\x0eOTHERS_EXECUTE\x10\x01*\xd4\x01\n\x08\x44\x61taType\x12\x17\n\x13\x64\x61ta_type_undefined\x10\x00\x12\x08\n\x04INT8\x10\x01\x12\t\n\x05UINT8\x10\x02\x12\t\n\x05INT16\x10\x03\x12\n\n\x06UINT16\x10\x04\x12\t\n\x05INT32\x10\x05\x12\n\n\x06UINT32\x10\x06\x12\t\n\x05INT64\x10\x07\x12\n\n\x06UINT64\x10\x08\x12\x08\n\x04\x42OOL\x10\t\x12\n\n\x05\x46LOAT\x10\x81\x02\x12\x0b\n\x06\x44OUBLE\x10\x82\x02\x12\t\n\x04\x43HAR\x10\x81\x04\x12\x0b\n\x06STRING\x10\x82\x04\x12\n\n\x05\x42YTES\x10\x99\t\x12\x0e\n\tUSER_TYPE\x10\x80\n*\x84\x01\n\rParameterType\x12\x18\n\x14param_type_undefined\x10\x00\x12\t\n\x05INPUT\x10\x01\x12\n\n\x06OUTPUT\x10\x10\x12\x0e\n\tPARAMETER\x10\x80\x02\x12\x17\n\x12PARAMETER_VOLATILE\x10\x81\x02\x12\x19\n\x14PARAMETER_PERSISTENT\x10\x82\x02*\xd2\x02\n\nStatusCode\x12\x06\n\x02OK\x10\x00\x12\x12\n\x0eREAD_ONLY_MODE\x10\x01\x12\x0c\n\x06\x46\x41ILED\x10\x80\xfe\x03\x12\x15\n\x10\x46\x41ILED_TO_DECODE\x10\x80 \x12\x15\n\x10SUB_LIST_IS_FULL\x10\x80\"\x12\x19\n\x14WRONG_PARAMETER_PATH\x10\x80$\x12 \n\x1b\x46\x41ILED_TO_SET_REQUESTED_FRQ\x10\x80&\x12\x18\n\x13\x46\x41ILED_TO_OPEN_FILE\x10\x80(\x12\x17\n\x12GROUP_LIST_IS_FULL\x10\x80*\x12\x1f\n\x1aTIMEOUT_WAITING_FOR_UPDATE\x10\x80,\x12\x15\n\x10SAVE_IN_PROGRESS\x10\x80.\x12\x13\n\x0eWRONG_PASSWORD\x10\x80\x42\x12\x17\n\x12USER_NOT_LOGGED_IN\x10\x80\x44\x12\x16\n\x11PERMISSION_DENIED\x10\x80\x46*v\n\nErrorLevel\x12\x19\n\x15\x65rror_level_undefined\x10\x00\x12\x08\n\x04INFO\x10\x01\x12\x0b\n\x07WARNING\x10\x02\x12\x14\n\x10\x46ORCED_DISENGAGE\x10\x03\x12\x0c\n\x08SHUTDOWN\x10\x04\x12\x12\n\x0e\x45MERGENCY_STOP\x10\x05*_\n\nOffsetType\x12\x19\n\x15offset_type_undefined\x10\x00\x12\x13\n\x0fOFFSET_ELEMENTS\x10\x01\x12\x10\n\x0cOFFSET_BYTES\x10\x02\x12\x0f\n\x0bOFFSET_BITS\x10\x03*<\n\rParameterFlag\x12\x12\n\x0eLINK_IS_ACTIVE\x10\x01\x12\x17\n\x13OVERWRITE_IS_ACTIVE\x10\x02')
17
+
18
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
19
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'motorcortex_pb2', globals())
20
+ if _descriptor._USE_C_DESCRIPTORS == False:
21
+
22
+ DESCRIPTOR._options = None
23
+ _UNIT._serialized_start=3796
24
+ _UNIT._serialized_end=4095
25
+ _USERGROUP._serialized_start=4097
26
+ _USERGROUP._serialized_end=4192
27
+ _PERMISSION._serialized_start=4195
28
+ _PERMISSION._serialized_end=4391
29
+ _DATATYPE._serialized_start=4394
30
+ _DATATYPE._serialized_end=4606
31
+ _PARAMETERTYPE._serialized_start=4609
32
+ _PARAMETERTYPE._serialized_end=4741
33
+ _STATUSCODE._serialized_start=4744
34
+ _STATUSCODE._serialized_end=5082
35
+ _ERRORLEVEL._serialized_start=5084
36
+ _ERRORLEVEL._serialized_end=5202
37
+ _OFFSETTYPE._serialized_start=5204
38
+ _OFFSETTYPE._serialized_end=5299
39
+ _PARAMETERFLAG._serialized_start=5301
40
+ _PARAMETERFLAG._serialized_end=5361
41
+ _PARAMETEROFFSET._serialized_start=34
42
+ _PARAMETEROFFSET._serialized_end=139
43
+ _ERROR._serialized_start=141
44
+ _ERROR._serialized_end=243
45
+ _ERRORLIST._serialized_start=245
46
+ _ERRORLIST._serialized_end=342
47
+ _PARAMETERINFO._serialized_start=345
48
+ _PARAMETERINFO._serialized_end=611
49
+ _GROUPPARAMETERINFO._serialized_start=614
50
+ _GROUPPARAMETERINFO._serialized_end=762
51
+ _HEADER._serialized_start=764
52
+ _HEADER._serialized_end=813
53
+ _GROUPMSG._serialized_start=815
54
+ _GROUPMSG._serialized_end=905
55
+ _STATUSMSG._serialized_start=907
56
+ _STATUSMSG._serialized_end=996
57
+ _LOGINMSG._serialized_start=998
58
+ _LOGINMSG._serialized_end=1078
59
+ _GETSESSIONTOKENMSG._serialized_start=1080
60
+ _GETSESSIONTOKENMSG._serialized_end=1137
61
+ _SESSIONTOKENMSG._serialized_start=1139
62
+ _SESSIONTOKENMSG._serialized_end=1249
63
+ _GETSUBCOOKIEMSG._serialized_start=1251
64
+ _GETSUBCOOKIEMSG._serialized_end=1305
65
+ _SUBCOOKIEMSG._serialized_start=1307
66
+ _SUBCOOKIEMSG._serialized_end=1415
67
+ _RESTORESESSIONMSG._serialized_start=1417
68
+ _RESTORESESSIONMSG._serialized_end=1488
69
+ _LOGOUTMSG._serialized_start=1490
70
+ _LOGOUTMSG._serialized_end=1538
71
+ _GETPARAMETERTREEMSG._serialized_start=1540
72
+ _GETPARAMETERTREEMSG._serialized_end=1598
73
+ _PARAMETERTREEMSG._serialized_start=1601
74
+ _PARAMETERTREEMSG._serialized_end=1755
75
+ _GETPARAMETERTREEHASHMSG._serialized_start=1757
76
+ _GETPARAMETERTREEHASHMSG._serialized_end=1819
77
+ _PARAMETERTREEHASHMSG._serialized_start=1821
78
+ _PARAMETERTREEHASHMSG._serialized_end=1935
79
+ _GETPARAMETERTREECOMPRESSEDMSG._serialized_start=1937
80
+ _GETPARAMETERTREECOMPRESSEDMSG._serialized_end=2005
81
+ _COMPRESSEDPARAMETERTREEMSG._serialized_start=2008
82
+ _COMPRESSEDPARAMETERTREEMSG._serialized_end=2175
83
+ _CREATEGROUPMSG._serialized_start=2177
84
+ _CREATEGROUPMSG._serialized_end=2281
85
+ _GROUPSTATUSMSG._serialized_start=2284
86
+ _GROUPSTATUSMSG._serialized_end=2454
87
+ _REMOVEGROUPMSG._serialized_start=2456
88
+ _REMOVEGROUPMSG._serialized_end=2524
89
+ _GETPARAMETERMSG._serialized_start=2526
90
+ _GETPARAMETERMSG._serialized_end=2594
91
+ _PARAMETERMSG._serialized_start=2597
92
+ _PARAMETERMSG._serialized_end=2746
93
+ _GETPARAMETERLISTMSG._serialized_start=2748
94
+ _GETPARAMETERLISTMSG._serialized_end=2852
95
+ _PARAMETERLISTMSG._serialized_start=2855
96
+ _PARAMETERLISTMSG._serialized_end=2994
97
+ _SETPARAMETERMSG._serialized_start=2997
98
+ _SETPARAMETERMSG._serialized_end=3126
99
+ _SETPARAMETERLISTMSG._serialized_start=3128
100
+ _SETPARAMETERLISTMSG._serialized_end=3232
101
+ _OVERWRITEPARAMETERMSG._serialized_start=3235
102
+ _OVERWRITEPARAMETERMSG._serialized_end=3388
103
+ _RELEASEPARAMETERMSG._serialized_start=3390
104
+ _RELEASEPARAMETERMSG._serialized_end=3462
105
+ _SAVEMSG._serialized_start=3464
106
+ _SAVEMSG._serialized_end=3543
107
+ _LOADMSG._serialized_start=3545
108
+ _LOADMSG._serialized_end=3624
109
+ _CONSOLECMDMSG._serialized_start=3626
110
+ _CONSOLECMDMSG._serialized_end=3693
111
+ _CONSOLECMDLISTMSG._serialized_start=3695
112
+ _CONSOLECMDLISTMSG._serialized_end=3793
113
+ # @@protoc_insertion_point(module_scope)