spectra-plot 0.2.1__tar.gz → 0.2.2__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.
- {spectra_plot-0.2.1/spectra_plot.egg-info → spectra_plot-0.2.2}/PKG-INFO +1 -1
- spectra_plot-0.2.2/VERSION +1 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/__init__.py +5 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_cli.py +8 -1
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_codec.py +156 -86
- spectra_plot-0.2.2/spectra/_codec_fb.py +506 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_easy.py +12 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/__init__.py +0 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/__init__.py +0 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/AckStatePayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/CmdAssignFiguresPayload.py +102 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/CmdCloseWindowPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/CmdRemoveFigurePayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/CmdSetActivePayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/DiffOp.py +206 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/DiffOpType.py +26 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/EvtFigureDestroyedPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/EvtInputPayload.py +141 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/EvtTopicListChangedPayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/EvtWindowClosedPayload.py +76 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/HelloPayload.py +102 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/InputType.py +10 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqAddSeriesPayload.py +89 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqAppendDataPayload.py +102 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqCloseFigurePayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqCloseWindowPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqCreateAxesPayload.py +102 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqCreateFigurePayload.py +76 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqCreateWindowPayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqDeclareTopicPayload.py +89 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqDestroyFigurePayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqDetachFigurePayload.py +115 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqListTopicsPayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqPublishTopicSamplesPayload.py +89 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqReconnectPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqRemoveSeriesPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqSetDataPayload.py +115 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqShowPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqSubscribeTopicPayload.py +89 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqUnsubscribeTopicPayload.py +76 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqUpdateBatchPayload.py +74 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/ReqUpdatePropertyPayload.py +167 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespAxesCreatedPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespErrPayload.py +76 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespFigureCreatedPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespFigureListPayload.py +89 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespOkPayload.py +50 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespSeriesAddedPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespSubscribeTopicPayload.py +63 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/RespTopicListPayload.py +87 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/SnapshotAxisState.py +180 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/SnapshotFigureState.py +215 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/SnapshotKnobState.py +147 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/SnapshotSeriesState.py +232 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/StateDiffPayload.py +100 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/StateSnapshotPayload.py +137 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/TopicInfoEntry.py +141 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/WelcomePayload.py +102 -0
- spectra_plot-0.2.2/spectra/_fb_generated/spectra/ipc/fb/__init__.py +0 -0
- spectra_plot-0.2.2/spectra/_launcher.py +352 -0
- spectra_plot-0.2.2/spectra/_log.py +102 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_protocol.py +23 -1
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_transport.py +31 -1
- spectra_plot-0.2.2/spectra/topic.py +324 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2/spectra_plot.egg-info}/PKG-INFO +1 -1
- spectra_plot-0.2.2/spectra_plot.egg-info/SOURCES.txt +97 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_codec.py +18 -80
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_easy_embed.py +17 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_embed.py +33 -13
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_phase2.py +53 -143
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_phase3.py +40 -95
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_phase4.py +31 -102
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_phase5.py +4 -2
- spectra_plot-0.2.2/tests/test_windows_compat.py +216 -0
- spectra_plot-0.2.1/VERSION +0 -1
- spectra_plot-0.2.1/spectra/_launcher.py +0 -205
- spectra_plot-0.2.1/spectra/_log.py +0 -62
- spectra_plot-0.2.1/spectra_plot.egg-info/SOURCES.txt +0 -42
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/MANIFEST.in +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/pyproject.toml +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/setup.cfg +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_animation.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_axes.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_blob.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_download.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_embed.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_errors.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_figure.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_persistence.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_series.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/_session.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/backends/__init__.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/backends/_qt_compat.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/backends/backend_qtagg.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra/embed.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra_plot.egg-info/dependency_links.txt +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra_plot.egg-info/entry_points.txt +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra_plot.egg-info/requires.txt +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/spectra_plot.egg-info/top_level.txt +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_cross_codec.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_download.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_easy.py +0 -0
- {spectra_plot-0.2.1 → spectra_plot-0.2.2}/tests/test_qt_backend.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.2
|
|
@@ -38,6 +38,8 @@ from ._errors import (
|
|
|
38
38
|
BackendError,
|
|
39
39
|
)
|
|
40
40
|
from ._animation import ipc_sleep, FramePacer, BackendAnimator
|
|
41
|
+
from ._log import set_log_level, get_log_level
|
|
42
|
+
from .topic import Publisher
|
|
41
43
|
|
|
42
44
|
# ─── Easy API (one-liners, everything in background) ─────────────────────────
|
|
43
45
|
from ._easy import (
|
|
@@ -144,4 +146,7 @@ __all__ = [
|
|
|
144
146
|
"ipc_sleep",
|
|
145
147
|
"FramePacer",
|
|
146
148
|
"BackendAnimator",
|
|
149
|
+
"set_log_level",
|
|
150
|
+
"get_log_level",
|
|
151
|
+
"Publisher",
|
|
147
152
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Command-line entry points for spectra."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import platform as _platform
|
|
4
5
|
import sys
|
|
5
6
|
|
|
6
7
|
|
|
@@ -58,4 +59,10 @@ def backend_main():
|
|
|
58
59
|
)
|
|
59
60
|
sys.exit(1)
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
if _platform.system() == "Windows":
|
|
63
|
+
# os.execv on Windows spawns a new process and exits the current one
|
|
64
|
+
# which can cause issues with console handling. Use subprocess instead.
|
|
65
|
+
import subprocess
|
|
66
|
+
sys.exit(subprocess.call([binary] + sys.argv[1:]))
|
|
67
|
+
else:
|
|
68
|
+
os.execv(binary, [binary] + sys.argv[1:])
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"""TLV (Tag-Length-Value) codec mirroring src/ipc/codec.hpp.
|
|
2
2
|
|
|
3
3
|
Wire format per field: [tag: u8] [len: u32 LE] [data: len bytes]
|
|
4
|
+
|
|
5
|
+
FlatBuffers payloads are prefixed with 0x01; legacy TLV payloads start with
|
|
6
|
+
a raw tag byte (never 0x00 or 0x01 in practice, but 0x00 is reserved).
|
|
7
|
+
Decode functions auto-detect the format and delegate to _codec_fb when needed.
|
|
4
8
|
"""
|
|
5
9
|
|
|
6
10
|
import struct
|
|
7
11
|
from typing import List, Optional, Tuple
|
|
8
12
|
|
|
9
13
|
from . import _protocol as P
|
|
14
|
+
from . import _codec_fb as fb_codec
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
# ─── Encoder ──────────────────────────────────────────────────────────────────
|
|
@@ -188,16 +193,11 @@ def decode_header(data: bytes) -> Optional[dict]:
|
|
|
188
193
|
# ─── Convenience: encode specific payloads ────────────────────────────────────
|
|
189
194
|
|
|
190
195
|
def encode_hello(client_type: str = "python", build: str = "") -> bytes:
|
|
191
|
-
|
|
192
|
-
enc.put_u16(P.TAG_PROTOCOL_MAJOR, P.PROTOCOL_MAJOR)
|
|
193
|
-
enc.put_u16(P.TAG_PROTOCOL_MINOR, P.PROTOCOL_MINOR)
|
|
194
|
-
enc.put_string(P.TAG_AGENT_BUILD, build)
|
|
195
|
-
enc.put_u32(P.TAG_CAPABILITIES, 0)
|
|
196
|
-
enc.put_string(P.TAG_CLIENT_TYPE, client_type)
|
|
197
|
-
return enc.take()
|
|
198
|
-
|
|
196
|
+
return fb_codec.encode_fb_hello(client_type=client_type, build=build)
|
|
199
197
|
|
|
200
198
|
def decode_welcome(data: bytes) -> dict:
|
|
199
|
+
if fb_codec._is_fb(data):
|
|
200
|
+
return fb_codec.decode_fb_welcome(data)
|
|
201
201
|
result = {"session_id": 0, "window_id": 0, "process_id": 0, "heartbeat_ms": 5000, "mode": ""}
|
|
202
202
|
dec = PayloadDecoder(data)
|
|
203
203
|
while dec.next():
|
|
@@ -216,42 +216,16 @@ def decode_welcome(data: bytes) -> dict:
|
|
|
216
216
|
|
|
217
217
|
|
|
218
218
|
def encode_req_create_figure(title: str = "", width: int = 1280, height: int = 720) -> bytes:
|
|
219
|
-
|
|
220
|
-
enc.put_string(P.TAG_TITLE, title)
|
|
221
|
-
enc.put_u32(P.TAG_WIDTH, width)
|
|
222
|
-
enc.put_u32(P.TAG_HEIGHT, height)
|
|
223
|
-
return enc.take()
|
|
224
|
-
|
|
219
|
+
return fb_codec.encode_fb_req_create_figure(title=title, width=width, height=height)
|
|
225
220
|
|
|
226
221
|
def encode_req_create_axes(figure_id: int, rows: int, cols: int, index: int, is_3d: bool = False) -> bytes:
|
|
227
|
-
|
|
228
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
229
|
-
enc.put_u32(P.TAG_GRID_ROWS, rows)
|
|
230
|
-
enc.put_u32(P.TAG_GRID_COLS, cols)
|
|
231
|
-
enc.put_u32(P.TAG_GRID_INDEX, index)
|
|
232
|
-
if is_3d:
|
|
233
|
-
enc.put_bool(P.TAG_IS_3D, True)
|
|
234
|
-
return enc.take()
|
|
235
|
-
|
|
222
|
+
return fb_codec.encode_fb_req_create_axes(figure_id=figure_id, rows=rows, cols=cols, index=index, is_3d=is_3d)
|
|
236
223
|
|
|
237
224
|
def encode_req_add_series(figure_id: int, axes_index: int, series_type: str, label: str = "") -> bytes:
|
|
238
|
-
|
|
239
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
240
|
-
enc.put_u32(P.TAG_AXES_INDEX, axes_index)
|
|
241
|
-
enc.put_string(P.TAG_SERIES_TYPE, series_type)
|
|
242
|
-
enc.put_string(P.TAG_SERIES_LABEL, label)
|
|
243
|
-
return enc.take()
|
|
244
|
-
|
|
225
|
+
return fb_codec.encode_fb_req_add_series(figure_id=figure_id, axes_index=axes_index, series_type=series_type, label=label)
|
|
245
226
|
|
|
246
227
|
def encode_req_set_data(figure_id: int, series_index: int, data: List[float], dtype: int = 0) -> bytes:
|
|
247
|
-
|
|
248
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
249
|
-
enc.put_u32(P.TAG_SERIES_INDEX, series_index)
|
|
250
|
-
enc.put_u16(P.TAG_DTYPE, dtype)
|
|
251
|
-
if data:
|
|
252
|
-
enc.put_float_array(P.TAG_BLOB_INLINE, data)
|
|
253
|
-
return enc.take()
|
|
254
|
-
|
|
228
|
+
return fb_codec.encode_fb_req_set_data(figure_id=figure_id, series_index=series_index, data=data, dtype=dtype)
|
|
255
229
|
|
|
256
230
|
def encode_req_set_data_raw(figure_id: int, series_index: int, raw_bytes: bytes, count: int, dtype: int = 0) -> bytes:
|
|
257
231
|
"""Encode REQ_SET_DATA with pre-packed float array bytes for zero-copy from numpy."""
|
|
@@ -294,14 +268,7 @@ def encode_req_set_data_chunked(
|
|
|
294
268
|
|
|
295
269
|
|
|
296
270
|
def encode_req_append_data(figure_id: int, series_index: int, data: List[float]) -> bytes:
|
|
297
|
-
|
|
298
|
-
enc = PayloadEncoder()
|
|
299
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
300
|
-
enc.put_u32(P.TAG_SERIES_INDEX, series_index)
|
|
301
|
-
if data:
|
|
302
|
-
enc.put_float_array(P.TAG_BLOB_INLINE, data)
|
|
303
|
-
return enc.take()
|
|
304
|
-
|
|
271
|
+
return fb_codec.encode_fb_req_append_data(figure_id=figure_id, series_index=series_index, data=data)
|
|
305
272
|
|
|
306
273
|
def encode_req_append_data_raw(figure_id: int, series_index: int, raw_bytes: bytes, count: int) -> bytes:
|
|
307
274
|
"""Encode REQ_APPEND_DATA with pre-packed float array bytes for zero-copy from numpy."""
|
|
@@ -325,48 +292,19 @@ def encode_req_update_property(
|
|
|
325
292
|
bool_val: bool = False,
|
|
326
293
|
str_val: str = "",
|
|
327
294
|
) -> bytes:
|
|
328
|
-
|
|
329
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
330
|
-
enc.put_u32(P.TAG_AXES_INDEX, axes_index)
|
|
331
|
-
enc.put_u32(P.TAG_SERIES_INDEX, series_index)
|
|
332
|
-
enc.put_string(P.TAG_PROPERTY_NAME, prop)
|
|
333
|
-
enc.put_double(P.TAG_F1, f1)
|
|
334
|
-
enc.put_double(P.TAG_F2, f2)
|
|
335
|
-
enc.put_double(P.TAG_F3, f3)
|
|
336
|
-
enc.put_double(P.TAG_F4, f4)
|
|
337
|
-
enc.put_bool(P.TAG_BOOL_VAL, bool_val)
|
|
338
|
-
if str_val:
|
|
339
|
-
enc.put_string(P.TAG_STR_VAL, str_val)
|
|
340
|
-
return enc.take()
|
|
341
|
-
|
|
295
|
+
return fb_codec.encode_fb_req_update_property(figure_id=figure_id, axes_index=axes_index, series_index=series_index, prop=prop, f1=f1, f2=f2, f3=f3, f4=f4, bool_val=bool_val, str_val=str_val)
|
|
342
296
|
|
|
343
297
|
def encode_req_show(figure_id: int, window_id: int = 0) -> bytes:
|
|
344
|
-
|
|
345
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
346
|
-
if window_id != 0:
|
|
347
|
-
enc.put_u64(P.TAG_WINDOW_ID, window_id)
|
|
348
|
-
return enc.take()
|
|
349
|
-
|
|
298
|
+
return fb_codec.encode_fb_req_show(figure_id=figure_id, window_id=window_id)
|
|
350
299
|
|
|
351
300
|
def encode_req_destroy_figure(figure_id: int) -> bytes:
|
|
352
|
-
|
|
353
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
354
|
-
return enc.take()
|
|
355
|
-
|
|
301
|
+
return fb_codec.encode_fb_req_destroy_figure(figure_id=figure_id)
|
|
356
302
|
|
|
357
303
|
def encode_req_remove_series(figure_id: int, series_index: int) -> bytes:
|
|
358
|
-
|
|
359
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
360
|
-
enc.put_u32(P.TAG_SERIES_INDEX, series_index)
|
|
361
|
-
return enc.take()
|
|
362
|
-
|
|
304
|
+
return fb_codec.encode_fb_req_remove_series(figure_id=figure_id, series_index=series_index)
|
|
363
305
|
|
|
364
306
|
def encode_req_close_figure(figure_id: int) -> bytes:
|
|
365
|
-
|
|
366
|
-
enc = PayloadEncoder()
|
|
367
|
-
enc.put_u64(P.TAG_FIGURE_ID, figure_id)
|
|
368
|
-
return enc.take()
|
|
369
|
-
|
|
307
|
+
return fb_codec.encode_fb_req_close_figure(figure_id=figure_id)
|
|
370
308
|
|
|
371
309
|
def encode_req_update_batch(updates: list) -> bytes:
|
|
372
310
|
"""Encode REQ_UPDATE_BATCH — multiple property updates in one message.
|
|
@@ -382,12 +320,7 @@ def encode_req_update_batch(updates: list) -> bytes:
|
|
|
382
320
|
|
|
383
321
|
|
|
384
322
|
def encode_req_reconnect(session_id: int, session_token: str = "") -> bytes:
|
|
385
|
-
|
|
386
|
-
enc.put_u64(P.TAG_SESSION_ID, session_id)
|
|
387
|
-
if session_token:
|
|
388
|
-
enc.put_string(P.TAG_SESSION_TOKEN, session_token)
|
|
389
|
-
return enc.take()
|
|
390
|
-
|
|
323
|
+
return fb_codec.encode_fb_req_reconnect(session_id=session_id, session_token=session_token)
|
|
391
324
|
|
|
392
325
|
def encode_req_list_figures() -> bytes:
|
|
393
326
|
return b""
|
|
@@ -397,6 +330,96 @@ def encode_req_disconnect() -> bytes:
|
|
|
397
330
|
return b""
|
|
398
331
|
|
|
399
332
|
|
|
333
|
+
# ─── Request payload decoders (round-trip validation / testing) ───────────────
|
|
334
|
+
|
|
335
|
+
def decode_req_append_data(data: bytes) -> dict:
|
|
336
|
+
"""Decode REQ_APPEND_DATA payload (FlatBuffers). Returns dict with figure_id, series_index, data."""
|
|
337
|
+
if fb_codec._is_fb(data):
|
|
338
|
+
return fb_codec.decode_fb_req_append_data(data)
|
|
339
|
+
# TLV fallback (legacy)
|
|
340
|
+
result: dict = {"figure_id": 0, "series_index": 0, "data": []}
|
|
341
|
+
dec = PayloadDecoder(data)
|
|
342
|
+
while dec.next():
|
|
343
|
+
if dec.tag == P.TAG_FIGURE_ID:
|
|
344
|
+
result["figure_id"] = dec.as_u64()
|
|
345
|
+
elif dec.tag == P.TAG_SERIES_INDEX:
|
|
346
|
+
result["series_index"] = dec.as_u32()
|
|
347
|
+
elif dec.tag == P.TAG_BLOB_INLINE:
|
|
348
|
+
result["data"] = dec.as_float_array()
|
|
349
|
+
return result
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def decode_req_remove_series(data: bytes) -> dict:
|
|
353
|
+
"""Decode REQ_REMOVE_SERIES payload. Returns dict with figure_id, series_index."""
|
|
354
|
+
if fb_codec._is_fb(data):
|
|
355
|
+
return fb_codec.decode_fb_req_remove_series(data)
|
|
356
|
+
result: dict = {"figure_id": 0, "series_index": 0}
|
|
357
|
+
dec = PayloadDecoder(data)
|
|
358
|
+
while dec.next():
|
|
359
|
+
if dec.tag == P.TAG_FIGURE_ID:
|
|
360
|
+
result["figure_id"] = dec.as_u64()
|
|
361
|
+
elif dec.tag == P.TAG_SERIES_INDEX:
|
|
362
|
+
result["series_index"] = dec.as_u32()
|
|
363
|
+
return result
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def decode_req_close_figure(data: bytes) -> dict:
|
|
367
|
+
"""Decode REQ_CLOSE_FIGURE payload. Returns dict with figure_id."""
|
|
368
|
+
if fb_codec._is_fb(data):
|
|
369
|
+
return fb_codec.decode_fb_req_close_figure(data)
|
|
370
|
+
result: dict = {"figure_id": 0}
|
|
371
|
+
dec = PayloadDecoder(data)
|
|
372
|
+
while dec.next():
|
|
373
|
+
if dec.tag == P.TAG_FIGURE_ID:
|
|
374
|
+
result["figure_id"] = dec.as_u64()
|
|
375
|
+
return result
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def decode_req_reconnect(data: bytes) -> dict:
|
|
379
|
+
"""Decode REQ_RECONNECT payload. Returns dict with session_id, session_token."""
|
|
380
|
+
if fb_codec._is_fb(data):
|
|
381
|
+
return fb_codec.decode_fb_req_reconnect(data)
|
|
382
|
+
result: dict = {"session_id": 0, "session_token": ""}
|
|
383
|
+
dec = PayloadDecoder(data)
|
|
384
|
+
while dec.next():
|
|
385
|
+
if dec.tag == P.TAG_SESSION_ID:
|
|
386
|
+
result["session_id"] = dec.as_u64()
|
|
387
|
+
elif dec.tag == P.TAG_SESSION_TOKEN:
|
|
388
|
+
result["session_token"] = dec.as_string()
|
|
389
|
+
return result
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def decode_req_update_property(data: bytes) -> dict:
|
|
393
|
+
"""Decode REQ_UPDATE_PROPERTY payload. Returns dict with all fields."""
|
|
394
|
+
if fb_codec._is_fb(data):
|
|
395
|
+
return fb_codec.decode_fb_req_update_property(data)
|
|
396
|
+
result: dict = {"figure_id": 0, "axes_index": 0, "series_index": 0,
|
|
397
|
+
"prop": "", "f1": 0.0, "f2": 0.0, "f3": 0.0, "f4": 0.0,
|
|
398
|
+
"bool_val": False, "str_val": ""}
|
|
399
|
+
dec = PayloadDecoder(data)
|
|
400
|
+
while dec.next():
|
|
401
|
+
if dec.tag == P.TAG_FIGURE_ID:
|
|
402
|
+
result["figure_id"] = dec.as_u64()
|
|
403
|
+
elif dec.tag == P.TAG_AXES_INDEX:
|
|
404
|
+
result["axes_index"] = dec.as_u32()
|
|
405
|
+
elif dec.tag == P.TAG_SERIES_INDEX:
|
|
406
|
+
result["series_index"] = dec.as_u32()
|
|
407
|
+
elif dec.tag == P.TAG_PROPERTY_NAME:
|
|
408
|
+
result["prop"] = dec.as_string()
|
|
409
|
+
elif dec.tag == P.TAG_F1:
|
|
410
|
+
result["f1"] = dec.as_float()
|
|
411
|
+
elif dec.tag == P.TAG_F2:
|
|
412
|
+
result["f2"] = dec.as_float()
|
|
413
|
+
elif dec.tag == P.TAG_F3:
|
|
414
|
+
result["f3"] = dec.as_float()
|
|
415
|
+
elif dec.tag == P.TAG_F4:
|
|
416
|
+
result["f4"] = dec.as_float()
|
|
417
|
+
elif dec.tag == P.TAG_BOOL_VAL:
|
|
418
|
+
result["bool_val"] = dec.as_bool()
|
|
419
|
+
elif dec.tag == P.TAG_STR_VAL:
|
|
420
|
+
result["str_val"] = dec.as_string()
|
|
421
|
+
return result
|
|
422
|
+
|
|
400
423
|
def encode_req_anim_start(figure_id: int, fps: float = 60.0, duration: float = 0.0) -> bytes:
|
|
401
424
|
"""Encode REQ_ANIM_START — start backend-driven animation.
|
|
402
425
|
|
|
@@ -447,6 +470,8 @@ def decode_blob_release(data: bytes) -> str:
|
|
|
447
470
|
|
|
448
471
|
def decode_resp_err(data: bytes) -> Tuple[int, int, str]:
|
|
449
472
|
"""Returns (request_id, code, message)."""
|
|
473
|
+
if fb_codec._is_fb(data):
|
|
474
|
+
return fb_codec.decode_fb_resp_err(data)
|
|
450
475
|
request_id = 0
|
|
451
476
|
code = 0
|
|
452
477
|
message = ""
|
|
@@ -464,6 +489,8 @@ def decode_resp_err(data: bytes) -> Tuple[int, int, str]:
|
|
|
464
489
|
|
|
465
490
|
def decode_resp_figure_created(data: bytes) -> Tuple[int, int]:
|
|
466
491
|
"""Returns (request_id, figure_id)."""
|
|
492
|
+
if fb_codec._is_fb(data):
|
|
493
|
+
return fb_codec.decode_fb_resp_figure_created(data)
|
|
467
494
|
request_id = 0
|
|
468
495
|
figure_id = 0
|
|
469
496
|
dec = PayloadDecoder(data)
|
|
@@ -478,6 +505,8 @@ def decode_resp_figure_created(data: bytes) -> Tuple[int, int]:
|
|
|
478
505
|
|
|
479
506
|
def decode_resp_axes_created(data: bytes) -> Tuple[int, int]:
|
|
480
507
|
"""Returns (request_id, axes_index)."""
|
|
508
|
+
if fb_codec._is_fb(data):
|
|
509
|
+
return fb_codec.decode_fb_resp_axes_created(data)
|
|
481
510
|
request_id = 0
|
|
482
511
|
axes_index = 0
|
|
483
512
|
dec = PayloadDecoder(data)
|
|
@@ -492,6 +521,8 @@ def decode_resp_axes_created(data: bytes) -> Tuple[int, int]:
|
|
|
492
521
|
|
|
493
522
|
def decode_resp_series_added(data: bytes) -> Tuple[int, int]:
|
|
494
523
|
"""Returns (request_id, series_index)."""
|
|
524
|
+
if fb_codec._is_fb(data):
|
|
525
|
+
return fb_codec.decode_fb_resp_series_added(data)
|
|
495
526
|
request_id = 0
|
|
496
527
|
series_index = 0
|
|
497
528
|
dec = PayloadDecoder(data)
|
|
@@ -506,6 +537,8 @@ def decode_resp_series_added(data: bytes) -> Tuple[int, int]:
|
|
|
506
537
|
|
|
507
538
|
def decode_resp_figure_list(data: bytes) -> Tuple[int, List[int]]:
|
|
508
539
|
"""Returns (request_id, [figure_ids])."""
|
|
540
|
+
if fb_codec._is_fb(data):
|
|
541
|
+
return fb_codec.decode_fb_resp_figure_list(data)
|
|
509
542
|
request_id = 0
|
|
510
543
|
figure_ids: List[int] = []
|
|
511
544
|
dec = PayloadDecoder(data)
|
|
@@ -520,6 +553,8 @@ def decode_resp_figure_list(data: bytes) -> Tuple[int, List[int]]:
|
|
|
520
553
|
|
|
521
554
|
def decode_resp_ok(data: bytes) -> int:
|
|
522
555
|
"""Returns request_id."""
|
|
556
|
+
if fb_codec._is_fb(data):
|
|
557
|
+
return fb_codec.decode_fb_resp_ok(data)
|
|
523
558
|
request_id = 0
|
|
524
559
|
dec = PayloadDecoder(data)
|
|
525
560
|
while dec.next():
|
|
@@ -530,6 +565,8 @@ def decode_resp_ok(data: bytes) -> int:
|
|
|
530
565
|
|
|
531
566
|
def decode_evt_window_closed(data: bytes) -> Tuple[int, int, str]:
|
|
532
567
|
"""Returns (figure_id, window_id, reason)."""
|
|
568
|
+
if fb_codec._is_fb(data):
|
|
569
|
+
return fb_codec.decode_fb_evt_window_closed(data)
|
|
533
570
|
figure_id = 0
|
|
534
571
|
window_id = 0
|
|
535
572
|
reason = ""
|
|
@@ -543,3 +580,36 @@ def decode_evt_window_closed(data: bytes) -> Tuple[int, int, str]:
|
|
|
543
580
|
elif t == P.TAG_REASON:
|
|
544
581
|
reason = dec.as_string()
|
|
545
582
|
return figure_id, window_id, reason
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
# ─── Topics ───────────────────────────────────────────────────────────────────
|
|
586
|
+
|
|
587
|
+
def encode_req_declare_topic(name: str, kind: int = 0, unit: str = "",
|
|
588
|
+
ring_capacity: int = 4096) -> bytes:
|
|
589
|
+
return fb_codec.encode_fb_req_declare_topic(name, kind, unit, ring_capacity)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def encode_req_publish_topic_samples(name: str, samples: List[float]) -> bytes:
|
|
593
|
+
return fb_codec.encode_fb_req_publish_topic_samples(name, samples)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def encode_req_subscribe_topic(name: str, figure_id: int, axes_index: int,
|
|
597
|
+
series_index: int = 0xFFFFFFFF) -> bytes:
|
|
598
|
+
return fb_codec.encode_fb_req_subscribe_topic(name, figure_id, axes_index, series_index)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def encode_req_unsubscribe_topic(name: str, figure_id: int, axes_index: int,
|
|
602
|
+
series_index: int) -> bytes:
|
|
603
|
+
return fb_codec.encode_fb_req_unsubscribe_topic(name, figure_id, axes_index, series_index)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def encode_req_list_topics() -> bytes:
|
|
607
|
+
return fb_codec.encode_fb_req_list_topics()
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def decode_resp_topic_list(data: bytes):
|
|
611
|
+
return fb_codec.decode_fb_resp_topic_list(data)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def decode_resp_subscribe_topic(data: bytes):
|
|
615
|
+
return fb_codec.decode_fb_resp_subscribe_topic(data)
|