better-rtplot 0.4.5__tar.gz → 0.4.6__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 (36) hide show
  1. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/PKG-INFO +1 -1
  2. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/pyproject.toml +1 -1
  3. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/client.py +18 -0
  4. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/server_browser.py +41 -2
  5. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/LICENSE +0 -0
  6. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/README.md +0 -0
  7. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/interactive_test.py +0 -0
  8. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/saved_plots/.gitignore +0 -0
  9. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/server.py +0 -0
  10. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/server_browser_gui.py +0 -0
  11. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/index.html +0 -0
  12. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/auto-render.min.js +0 -0
  13. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  14. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  15. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  16. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  17. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  18. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  19. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  20. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  21. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  22. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  23. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  24. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  25. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  26. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  27. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  28. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  29. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  30. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  31. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  32. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  33. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/katex.min.css +0 -0
  34. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/katex/katex.min.js +0 -0
  35. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/uPlot.iife.min.js +0 -0
  36. {better_rtplot-0.4.5 → better_rtplot-0.4.6}/rtplot/static/uPlot.min.css +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-rtplot
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary:
5
5
  License: GPL V3.0
6
6
  Author: jmontp
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "better-rtplot"
3
- version = "0.4.5"
3
+ version = "0.4.6"
4
4
  description = ""
5
5
  authors = ["jmontp <jmontp@umich.edu>"]
6
6
  license = "GPL V3.0"
@@ -25,6 +25,13 @@ control_socket.setsockopt(zmq.RCVHWM, 1000)
25
25
  _control_slider_values = {}
26
26
  _control_button_events = []
27
27
 
28
+ # Cached payload of the most recent initialize_plots() call. The server
29
+ # uses this to recover from its own crashes: when its tab loses the
30
+ # config, it pushes a "resend_config" control event over the return
31
+ # channel, and poll_controls() re-PUBs this dict so the plot reattaches
32
+ # without the user restarting their script. None until first call.
33
+ plot_desc_dict = None
34
+
28
35
  #Global variable to keep track of last connected address
29
36
  # default is fixed publisher mode, therefore you don't connect
30
37
  # to an address
@@ -506,6 +513,13 @@ def poll_controls():
506
513
  (cleared after this call).
507
514
 
508
515
  Call this from your tight loop before computing the next sample.
516
+
517
+ Also transparently handles ``resend_config`` requests from the server:
518
+ if the server crashed and reconnected (or the user clicked the
519
+ Reconnect button on a tab), it asks us to re-publish the cached
520
+ initialize_plots() payload so the plot rewires without the user
521
+ restarting their script. The request is consumed silently — the
522
+ returned ControlState only ever contains slider/button events.
509
523
  """
510
524
  global _control_button_events
511
525
  while True:
@@ -520,6 +534,10 @@ def poll_controls():
520
534
  _control_button_events.append(event.get("id"))
521
535
  elif evtype == "slider":
522
536
  _control_slider_values[event.get("id")] = float(event.get("value", 0.0))
537
+ elif evtype == "resend_config":
538
+ if plot_desc_dict is not None:
539
+ socket.send_string(SENDING_PLOT_UPDATE)
540
+ socket.send_json(plot_desc_dict)
523
541
 
524
542
  buttons = _control_button_events
525
543
  _control_button_events = []
@@ -921,6 +921,10 @@ async def create_connect_tab(
921
921
  tabs[t.id] = t
922
922
  _open_tab_sockets(t)
923
923
  t.receiver_task = asyncio.create_task(zmq_receiver(t))
924
+ # If the client at the other end is already running and only called
925
+ # initialize_plots() once at its startup, this nudges it to resend
926
+ # the cached config so the new tab populates without a script restart.
927
+ asyncio.create_task(_request_config_resend(t))
924
928
  if persist:
925
929
  save_persisted_tabs()
926
930
  await broadcast_tab_list()
@@ -956,13 +960,47 @@ async def rename_tab(tab_id: str, new_name: str):
956
960
  await broadcast_tab(tab_id)
957
961
 
958
962
 
963
+ async def _request_config_resend(tab: "Tab"):
964
+ """Nudge a still-running client to re-send its initialize_plots() payload.
965
+
966
+ The common scenario: the server crashed (or was restarted) mid-session
967
+ and lost the plot config it received the first time the client called
968
+ ``initialize_plots()``. The client is still happily streaming data,
969
+ but the new server has nothing to plot it on. This sends a
970
+ ``{"type": "resend_config"}`` control event over the tab's PUSH
971
+ socket; the client's poll_controls() loop intercepts it and re-sends
972
+ the cached config so the plot picks back up without the user having
973
+ to restart their script.
974
+
975
+ Retries on a brief schedule because the client's PULL socket may need
976
+ a moment to auto-reconnect to the freshly bound PUSH socket — a
977
+ one-shot DONTWAIT send to a peer-less PUSH gets silently dropped.
978
+ Stops early once we see ``tab.initialized`` flip to True (the client
979
+ has answered) so we don't spam.
980
+ """
981
+ for i, delay in enumerate((0.4, 1.0, 2.0)):
982
+ await asyncio.sleep(delay)
983
+ if tab.ctrl_sock is None:
984
+ return
985
+ if i > 0 and tab.initialized:
986
+ return
987
+ try:
988
+ await tab.ctrl_sock.send_json(
989
+ {"type": "resend_config"}, flags=zmq.DONTWAIT
990
+ )
991
+ except (zmq.Again, zmq.ZMQError):
992
+ pass
993
+
994
+
959
995
  async def reconnect_tab(tab_id: str):
960
996
  """Close and reopen ``tab_id``'s sockets, restart its receiver task.
961
997
 
962
998
  The tab's buffer and plot config are preserved — if the sender on
963
999
  the other side is still running the same stream we avoid a visible
964
- flash to empty. A new config from the sender will replace the state
965
- via the normal RECEIVED_PLOT_UPDATE path.
1000
+ flash to empty. After the sockets come back up we ask the (likely
1001
+ still-running) client to re-send its plot config so the connection
1002
+ recovers cleanly even if the server itself restarted in the
1003
+ meantime.
966
1004
  """
967
1005
  t = tabs.get(tab_id)
968
1006
  if t is None:
@@ -976,6 +1014,7 @@ async def reconnect_tab(tab_id: str):
976
1014
  t.data_rate_hz = 0.0
977
1015
  t._last_rx_ts = 0.0
978
1016
  t.receiver_task = asyncio.create_task(zmq_receiver(t))
1017
+ asyncio.create_task(_request_config_resend(t))
979
1018
  await broadcast_tab(tab_id)
980
1019
 
981
1020
 
File without changes
File without changes