ndscope 0.22.0__tar.gz → 0.22.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.
- {ndscope-0.22.0 → ndscope-0.22.2}/CHANGELOG.md +32 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/PKG-INFO +4 -2
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/__main__.py +47 -26
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/__version__.py +3 -3
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/cache.py +62 -105
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/channel_select.py +12 -6
- ndscope-0.22.2/ndscope/configuration/__init__.py +0 -0
- ndscope-0.22.2/ndscope/configuration/configuration.py +283 -0
- ndscope-0.22.2/ndscope/data_source_determination.py +60 -0
- ndscope-0.22.2/ndscope/markers.py +360 -0
- ndscope-0.22.2/ndscope/messages/__init__.py +0 -0
- ndscope-0.22.2/ndscope/messages/dtt_messages.py +41 -0
- ndscope-0.22.2/ndscope/messages/has_issues.py +106 -0
- ndscope-0.22.2/ndscope/messages/message_windows/__init__.py +0 -0
- ndscope-0.22.2/ndscope/messages/message_windows/log_list.py +46 -0
- ndscope-0.22.2/ndscope/messages/message_windows/message_window.py +42 -0
- ndscope-0.22.2/ndscope/messages/messages.py +207 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/plot.py +18 -11
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/plotMenu.py +39 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/scope.py +277 -89
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/scope.ui +92 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_util.py +22 -13
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/util.py +0 -23
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/PKG-INFO +4 -2
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/SOURCES.txt +11 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/requires.txt +3 -1
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/scm_file_list.json +11 -0
- ndscope-0.22.2/ndscope.egg-info/scm_version.json +8 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/pyproject.toml +3 -1
- ndscope-0.22.0/ndscope.egg-info/scm_version.json +0 -8
- {ndscope-0.22.0 → ndscope-0.22.2}/COPYING +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/COPYING-GPL-3 +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/MANIFEST.in +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/NEWS.md +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/README.md +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/RELEASE.md +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/bad.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/bad2.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/bad_legend.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/bad_ndscope.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/bad_times.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/big.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/biglive.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/calib.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/calib.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/calib_arrakis.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/calib_arrakis.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/dev.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/dts1 +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/install_wheel +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/live.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/live.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/manual_tests/manual_test.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/manual_tests/manual_test_live.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/manual_tests/ndscope-test.txt +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/manual_tests/old_manual_test.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/manual_tests/test_results_2025_10_23.txt +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/maturin.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/__init__.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/_qt.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/axis.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/axisCtrlTemplate.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/base_plot.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/channel_config.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/channel_config_dialog.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/channel_list.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/channel_select_dialog.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/const.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/crosshair.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/cursors.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/data.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/dialog.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/dialog_nds_auth.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/dialog_nds_ontape.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/dtt/__init__.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/dtt/channel_trend_id.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/dtt/dtt.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/exceptions.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/export.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/kerberos.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/legend.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/math_config.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/math_config.ui +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/nds.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/primary_view.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/result.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/ruff.toml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/scatterPlotMenu.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/scatter_plot.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/template.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/__init__.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test0.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test1-1.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test1-2.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test1-3.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test1.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test2.stp +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test2.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test3.txt +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test3.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test4.xml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/templates/test4.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_cache.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_cli.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_data_flow.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_layout.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_plot.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/test/test_window.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope/trigger.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/dependency_links.txt +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/entry_points.txt +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/ndscope.egg-info/top_level.txt +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/newdaq.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/past.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/past.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/past2.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/pidcheck.py +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/setup.cfg +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/teststand_arrakis.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/teststand_arrakis.yaml +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/trend.sh +0 -0
- {ndscope-0.22.0 → ndscope-0.22.2}/trend.yaml +0 -0
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
3
|
+
## NEXT
|
|
4
|
+
|
|
5
|
+
- New "latched" user message system
|
|
6
|
+
- Improved error messages
|
|
7
|
+
- Gaps are more visible
|
|
8
|
+
- Fixed bug where Y axis label could overlap tick marks
|
|
9
|
+
- Fixed bug where --single-shot data exports, e.g. hdf5, would never complete
|
|
10
|
+
- Added system to place markers on plots, then jump between markers.
|
|
11
|
+
- Fix bug where arrakis data could be timestamped wrong
|
|
12
|
+
- Add --arrakis (choose default Arrakis data source) and --arrakis-server (specify an arrakis server) options.
|
|
13
|
+
|
|
14
|
+
## 0.22.0 - 2026-08-24
|
|
15
|
+
|
|
16
|
+
- Add buttons to enable/disable Y cursors on all plots
|
|
17
|
+
- Y axis autoscale preserved during time-axis-only actions
|
|
18
|
+
- Use shift <mouse gesture> to constrain a mouse action to the time axis
|
|
19
|
+
- Use ctrl <mouse gesture> to constrain a mouse action to the Y axis
|
|
20
|
+
- Fixed downsampling so that zooming in on fast channels will show real resolution
|
|
21
|
+
- Fixed exception when cancelling a Kerberos login
|
|
22
|
+
- Allow full precision when entering scale and offsets for a channel
|
|
23
|
+
- Set curved width now used when adding new plots
|
|
24
|
+
- Use --replay-id and other new options to access Arrakis replay feature
|
|
25
|
+
- LIGO_DATA_URL environment variable dropped.
|
|
26
|
+
- NDSSERVER environment variable takes precedence over ARRAKIS_SERVER
|
|
27
|
+
|
|
28
|
+
## 0.21.0 - 2026-03-12
|
|
29
|
+
|
|
30
|
+
- Updated color scheme to pick colors with more contrast
|
|
31
|
+
- Add scatter plot
|
|
32
|
+
- Improved HDF5 export
|
|
33
|
+
|
|
2
34
|
## [0.20.7] - 2026-01-29
|
|
3
35
|
(changes since 0.19)
|
|
4
36
|
- Interface
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ndscope
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.2
|
|
4
4
|
Summary: Next-generation NDS time series plotting
|
|
5
5
|
Author-email: Jameson Graef Rollins <jameson.rollins@ligo.org>, Patrick Thomas <patrick.thomas@ligo.org>, Erik von Reis <erik.vonreis@ligo.org>
|
|
6
6
|
Maintainer-email: Jameson Graef Rollins <jameson.rollins@ligo.org>, Patrick Thomas <patrick.thomas@ligo.org>, Erik von Reis <erik.vonreis@ligo.org>
|
|
@@ -18,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: COPYING
|
|
19
19
|
License-File: COPYING-GPL-3
|
|
20
20
|
Requires-Dist: cairosvg
|
|
21
|
-
Requires-Dist: dttlib>=0.11.
|
|
21
|
+
Requires-Dist: dttlib>=0.11.3
|
|
22
22
|
Requires-Dist: gpstime
|
|
23
23
|
Requires-Dist: h5py
|
|
24
24
|
Requires-Dist: matplotlib
|
|
@@ -32,6 +32,8 @@ Requires-Dist: rapidfuzz
|
|
|
32
32
|
Requires-Dist: scipy
|
|
33
33
|
Requires-Dist: setproctitle
|
|
34
34
|
Requires-Dist: setuptools_scm
|
|
35
|
+
Requires-Dist: tomlkit
|
|
36
|
+
Requires-Dist: platformdirs
|
|
35
37
|
Dynamic: license-file
|
|
36
38
|
|
|
37
39
|
# Next-generation NDS oscilloscope
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
from __future__ import division, unicode_literals
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
import signal
|
|
6
|
-
import logging
|
|
2
|
+
|
|
7
3
|
import argparse
|
|
8
4
|
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import signal
|
|
8
|
+
import sys
|
|
9
|
+
from argparse import Namespace
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
9
12
|
import pyqtgraph as pg
|
|
13
|
+
from dttlib import PipInstant, Replay
|
|
14
|
+
|
|
10
15
|
from .cache import DataCache
|
|
11
|
-
from
|
|
16
|
+
from .configuration.configuration import load_config, get_config
|
|
17
|
+
from .data_source_determination import (
|
|
18
|
+
add_data_source_args,
|
|
19
|
+
parse_data_source_args,
|
|
20
|
+
get_first_data_source,
|
|
21
|
+
)
|
|
12
22
|
|
|
13
23
|
try:
|
|
14
24
|
import yaml
|
|
@@ -22,16 +32,13 @@ except ImportError:
|
|
|
22
32
|
pass
|
|
23
33
|
|
|
24
34
|
|
|
25
|
-
from gpstime import
|
|
35
|
+
from gpstime import GPSTimeException, gpstime
|
|
26
36
|
|
|
27
|
-
from . import __version__
|
|
28
|
-
from . import const
|
|
37
|
+
from . import __version__, const
|
|
29
38
|
from . import template as _template
|
|
30
|
-
from .util import resolve_data_source
|
|
31
39
|
from ._qt import create_app
|
|
32
|
-
from .scope import NDScope
|
|
33
40
|
from .exceptions import UnknownChannelError
|
|
34
|
-
|
|
41
|
+
from .scope import NDScope
|
|
35
42
|
|
|
36
43
|
logging.addLevelName(5, "DATA")
|
|
37
44
|
logging.basicConfig(
|
|
@@ -41,7 +48,6 @@ logging.basicConfig(
|
|
|
41
48
|
|
|
42
49
|
logger = logging.getLogger("MAIN")
|
|
43
50
|
|
|
44
|
-
|
|
45
51
|
##################################################
|
|
46
52
|
|
|
47
53
|
|
|
@@ -146,16 +152,19 @@ specifying the file as argument:
|
|
|
146
152
|
|
|
147
153
|
ndscope foobar.hdf5
|
|
148
154
|
|
|
149
|
-
The data source url can be provided in the ARRAKIS_SERVER environment
|
|
150
|
-
variable. The URL should in the form grpc://server:port for arrakis servers
|
|
151
|
-
or nds://server:port for nds servers. If NDSSERVER is defined, it will
|
|
152
|
-
override ARRAKIS_SERVER, then NDSSERVER=server:port will be used
|
|
155
|
+
The data source url can be provided in the ARRAKIS_SERVER environment
|
|
156
|
+
variable. The URL should in the form grpc://server:port for arrakis servers
|
|
157
|
+
or nds://server:port for nds servers. If NDSSERVER is defined, it will
|
|
158
|
+
override ARRAKIS_SERVER, then NDSSERVER=server:port will be used
|
|
153
159
|
(NDS servers only).
|
|
154
160
|
|
|
155
|
-
|
|
161
|
+
Use the --arrakis option to use the ARRAKIS_SERVER environment
|
|
162
|
+
variable instead of the NDSSERVER variable.
|
|
163
|
+
|
|
164
|
+
The data source can be set with the --arrakis-server or --url option for either
|
|
156
165
|
Arrakis servers or nds servers
|
|
157
166
|
|
|
158
|
-
ndscope --
|
|
167
|
+
ndscope --arrakis-server=grpc://ngdd.ligo-la.caltech.edu:31206 L1:FOO
|
|
159
168
|
|
|
160
169
|
ndscope --url=nds://nds.ligo-la.caltech.edu:31200 L1:FOO
|
|
161
170
|
|
|
@@ -212,8 +221,7 @@ parser.add_argument(
|
|
|
212
221
|
dest="time_window",
|
|
213
222
|
help="time window scalar or tuple, in seconds",
|
|
214
223
|
)
|
|
215
|
-
|
|
216
|
-
tgroup.add_argument(
|
|
224
|
+
parser.add_argument(
|
|
217
225
|
"--trend",
|
|
218
226
|
choices=["auto", "raw", "sec", "min"],
|
|
219
227
|
help="specify trend type, 'auto' auto-selects best trend for time span [default]",
|
|
@@ -317,10 +325,9 @@ parser.add_argument(
|
|
|
317
325
|
help="specify initial window size",
|
|
318
326
|
)
|
|
319
327
|
parser.add_argument("--expand", action="store_true", help="expand control bar")
|
|
320
|
-
|
|
321
|
-
parser
|
|
322
|
-
|
|
323
|
-
)
|
|
328
|
+
|
|
329
|
+
add_data_source_args(parser)
|
|
330
|
+
|
|
324
331
|
parser.add_argument(
|
|
325
332
|
"--gen-template",
|
|
326
333
|
action="store_true",
|
|
@@ -378,6 +385,10 @@ def main():
|
|
|
378
385
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
379
386
|
setproctitle(" ".join([PROG] + sys.argv[1:]))
|
|
380
387
|
|
|
388
|
+
# these are configuration values determined by arguments
|
|
389
|
+
# to be passed to the load_config function
|
|
390
|
+
args_conf = {}
|
|
391
|
+
|
|
381
392
|
args = parser.parse_args()
|
|
382
393
|
|
|
383
394
|
if args.usage:
|
|
@@ -489,6 +500,15 @@ def main():
|
|
|
489
500
|
template["t0"] = t0
|
|
490
501
|
template["time-window"] = window
|
|
491
502
|
|
|
503
|
+
##########
|
|
504
|
+
# Parse data sources
|
|
505
|
+
args_conf.update(parse_data_source_args(args))
|
|
506
|
+
|
|
507
|
+
##########
|
|
508
|
+
# args fully parsed. Load configuration
|
|
509
|
+
|
|
510
|
+
load_config(args_conf)
|
|
511
|
+
|
|
492
512
|
##########
|
|
493
513
|
# launch app
|
|
494
514
|
|
|
@@ -496,7 +516,8 @@ def main():
|
|
|
496
516
|
print(yaml.safe_dump(template, default_style=False))
|
|
497
517
|
sys.exit()
|
|
498
518
|
|
|
499
|
-
|
|
519
|
+
# only the first data source for now
|
|
520
|
+
url_text = get_first_data_source()
|
|
500
521
|
logger.debug(f"url = {url_text}")
|
|
501
522
|
|
|
502
523
|
# create cache here so we can start running channel searches
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.22.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 22,
|
|
21
|
+
__version__ = version = '0.22.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 22, 2)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g86e3ad200'
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
import logging
|
|
3
|
-
import collections
|
|
4
|
-
import traceback
|
|
5
3
|
import dttlib
|
|
6
4
|
from threading import Lock
|
|
7
5
|
import asyncio
|
|
8
6
|
|
|
9
7
|
from qtpy import QtCore
|
|
10
|
-
from qtpy.QtCore import Signal, Slot, QTimer
|
|
8
|
+
from qtpy.QtCore import Signal, Slot, QTimer
|
|
11
9
|
|
|
12
10
|
from rapidfuzz import process, fuzz
|
|
13
11
|
|
|
14
|
-
from .dtt.channel_trend_id import ChannelTrendId
|
|
12
|
+
from .dtt.channel_trend_id import ChannelTrendId
|
|
15
13
|
|
|
16
14
|
from . import nds
|
|
17
15
|
from .data import DataBuffer, DataBufferDict
|
|
18
16
|
from .exceptions import UnknownChannelError
|
|
19
17
|
from .dtt import DTT, AnalysisResult
|
|
18
|
+
from .messages.dtt_messages import handle_dtt_messages
|
|
19
|
+
from .messages.messages import set_error, clear_message
|
|
20
20
|
|
|
21
21
|
from typing import TYPE_CHECKING, Set, Union, Optional, Tuple, Dict, List
|
|
22
22
|
|
|
@@ -47,6 +47,9 @@ class AtomicViewId(object):
|
|
|
47
47
|
return old_val
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
primed_cache_logger = logging.getLogger("PRIMEDCACHE")
|
|
51
|
+
|
|
52
|
+
|
|
50
53
|
class PrimedCache(object):
|
|
51
54
|
"""Use with PrimedCache(DataCache):
|
|
52
55
|
to ensure a DataCache is primed at the end of a block.
|
|
@@ -69,18 +72,30 @@ class CacheReadyState(Enum):
|
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
class CacheReady(QtCore.QObject):
|
|
72
|
-
"""
|
|
75
|
+
"""
|
|
76
|
+
A statemachine that attempts to meter data flow into the Qt main thread
|
|
77
|
+
"""
|
|
73
78
|
|
|
74
79
|
def __init__(self, delay_ms: int, cache: "DataCache"):
|
|
75
80
|
"""
|
|
76
|
-
param delay_sec: delay to introduce between re-priming and actually
|
|
81
|
+
param delay_sec: delay to introduce between re-priming and actually
|
|
82
|
+
firing the cache to send data
|
|
77
83
|
"""
|
|
78
84
|
self.delay_ms = delay_ms
|
|
79
85
|
self.cache = cache
|
|
80
|
-
self.
|
|
86
|
+
self._state = CacheReadyState.Primed
|
|
81
87
|
self.state_lock = Lock()
|
|
82
88
|
self.commands: List[str] = []
|
|
83
89
|
|
|
90
|
+
@property
|
|
91
|
+
def state(self):
|
|
92
|
+
return self._state
|
|
93
|
+
|
|
94
|
+
@state.setter
|
|
95
|
+
def state(self, new_state: CacheReadyState):
|
|
96
|
+
primed_cache_logger.debug("Transitioning from %s to %s", self.state, new_state)
|
|
97
|
+
self._state = new_state
|
|
98
|
+
|
|
84
99
|
def _add_command(self, command: str):
|
|
85
100
|
if len(self.commands) == 0:
|
|
86
101
|
self.commands = [command]
|
|
@@ -145,6 +160,7 @@ class CacheReady(QtCore.QObject):
|
|
|
145
160
|
"""
|
|
146
161
|
if len(self.commands) > 0:
|
|
147
162
|
c = self.commands.pop()
|
|
163
|
+
logger.debug("CacheReady firing")
|
|
148
164
|
self.cache.signal_data.emit(c)
|
|
149
165
|
|
|
150
166
|
def _start_delay(self):
|
|
@@ -189,7 +205,9 @@ class ViewParams:
|
|
|
189
205
|
self.end,
|
|
190
206
|
)
|
|
191
207
|
|
|
192
|
-
def __eq__(self, other:
|
|
208
|
+
def __eq__(self, other: object) -> bool:
|
|
209
|
+
if not isinstance(other, ViewParams):
|
|
210
|
+
return False
|
|
193
211
|
return (
|
|
194
212
|
(not self.force)
|
|
195
213
|
and self.online == other.online
|
|
@@ -248,11 +266,15 @@ class DataCacheContext(object):
|
|
|
248
266
|
raise ValueError("cache context exited more times than it entered.")
|
|
249
267
|
if self.cache.next_view_params == self.cache.prev_view_params:
|
|
250
268
|
return
|
|
251
|
-
|
|
252
|
-
self.cache.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
269
|
+
try:
|
|
270
|
+
if self.cache.next_view_params.needs_new_view(self.cache.prev_view_params):
|
|
271
|
+
self.cache._new_scope_view()
|
|
272
|
+
else:
|
|
273
|
+
self.cache._set_scope_view()
|
|
274
|
+
self.cache.signal_channel_change.emit(None)
|
|
275
|
+
clear_message("cache_context")
|
|
276
|
+
except UnknownChannelError as e:
|
|
277
|
+
set_error("cache_context", str(e))
|
|
256
278
|
|
|
257
279
|
def __getattr__(self, name):
|
|
258
280
|
return self.cache.__getattribute__(name)
|
|
@@ -281,12 +303,6 @@ class DataCache(QtCore.QObject):
|
|
|
281
303
|
# payload a TrendType
|
|
282
304
|
signal_trend_change = Signal("PyQt_PyObject")
|
|
283
305
|
|
|
284
|
-
# status and error message from cache data stream
|
|
285
|
-
# payload is a single string
|
|
286
|
-
signal_stream_status = Signal("PyQt_PyObject")
|
|
287
|
-
signal_online_status = Signal("PyQt_PyObject")
|
|
288
|
-
signal_stream_error = Signal("PyQt_PyObject")
|
|
289
|
-
|
|
290
306
|
# data buffer in response to data request
|
|
291
307
|
# payload: tuple of
|
|
292
308
|
# trend type string
|
|
@@ -300,6 +316,7 @@ class DataCache(QtCore.QObject):
|
|
|
300
316
|
|
|
301
317
|
def __init__(self, scope: "NDScope", url_text: str, replay: dttlib.Replay) -> None:
|
|
302
318
|
super().__init__()
|
|
319
|
+
|
|
303
320
|
# needed to get the channel list
|
|
304
321
|
self.scope = scope
|
|
305
322
|
self.next_view_params = ViewParams(
|
|
@@ -337,7 +354,7 @@ class DataCache(QtCore.QObject):
|
|
|
337
354
|
url_text=url_text,
|
|
338
355
|
replay=replay,
|
|
339
356
|
scope_data_handler=self.handle_scope_result,
|
|
340
|
-
message_handler=self.
|
|
357
|
+
message_handler=self._handle_dtt_messages,
|
|
341
358
|
scope_view_done_handler=self._handle_view_done,
|
|
342
359
|
)
|
|
343
360
|
|
|
@@ -384,7 +401,6 @@ class DataCache(QtCore.QObject):
|
|
|
384
401
|
# f"{k}: start = {d.gps_start} len = {len(d.data)} rate = {d.channel.rate_hz}"
|
|
385
402
|
# )
|
|
386
403
|
|
|
387
|
-
fire = False
|
|
388
404
|
with self.data_lock:
|
|
389
405
|
self.data_buffers.add_buffer(new_buffer)
|
|
390
406
|
self.saved_data.add_buffer(new_buffer)
|
|
@@ -581,10 +597,8 @@ class DataCache(QtCore.QObject):
|
|
|
581
597
|
)
|
|
582
598
|
|
|
583
599
|
if self.next_view_params.online:
|
|
584
|
-
msg = f"Retrieving {self._command_description('online', self.next_view_params.trend)}..."
|
|
585
600
|
self.view = self.dtt.new_online_scope_view(view_set, span_pip)
|
|
586
601
|
else:
|
|
587
|
-
msg = f"Retrieving {self._command_description('', self.next_view_params.trend)}..."
|
|
588
602
|
if self.next_view_params.snapshot:
|
|
589
603
|
self.view = self.dtt.new_snapshot_scope_view(
|
|
590
604
|
view_set, start_pip, end_pip
|
|
@@ -734,22 +748,6 @@ class DataCache(QtCore.QObject):
|
|
|
734
748
|
|
|
735
749
|
##########
|
|
736
750
|
|
|
737
|
-
def _command_description(self, cmd, trend: dttlib.TrendType):
|
|
738
|
-
if cmd == "online":
|
|
739
|
-
desc = "online "
|
|
740
|
-
else:
|
|
741
|
-
desc = ""
|
|
742
|
-
if trend == "sec":
|
|
743
|
-
desc += "second trend data"
|
|
744
|
-
elif trend == "min":
|
|
745
|
-
desc += "minute trend data"
|
|
746
|
-
elif trend == "raw":
|
|
747
|
-
desc += "raw data"
|
|
748
|
-
return desc
|
|
749
|
-
|
|
750
|
-
def remote_cmd(self, cmd, **kwargs):
|
|
751
|
-
raise Exception("no longer used")
|
|
752
|
-
|
|
753
751
|
def remote_recv_channels(self, channels: Union[str, Dict[str, "dttlib.Channel"]]):
|
|
754
752
|
if isinstance(channels, str):
|
|
755
753
|
# error message
|
|
@@ -760,7 +758,11 @@ class DataCache(QtCore.QObject):
|
|
|
760
758
|
self.available_channels = channels
|
|
761
759
|
self.signal_channel_list_ready.emit()
|
|
762
760
|
if self.prime_invalid_recheck:
|
|
763
|
-
|
|
761
|
+
user_message = dttlib.UserMessage(
|
|
762
|
+
dttlib.Severity.Error, "Invalid channel"
|
|
763
|
+
)
|
|
764
|
+
msg_job = dttlib.MessageJob.SetMessage("stream_error", user_message)
|
|
765
|
+
self.handle_invalid_channels(msg_job)
|
|
764
766
|
|
|
765
767
|
@Slot()
|
|
766
768
|
def remote_done(self):
|
|
@@ -784,51 +786,6 @@ class DataCache(QtCore.QObject):
|
|
|
784
786
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
785
787
|
return set([task.result() for task in tasks if task.result()])
|
|
786
788
|
|
|
787
|
-
##################################################################
|
|
788
|
-
|
|
789
|
-
def _handle_messages(self, msg: dttlib.MessageJob) -> None:
|
|
790
|
-
"""
|
|
791
|
-
Handle incoming user messages from dttlib
|
|
792
|
-
"""
|
|
793
|
-
logger.debug("msg=%s", str(msg))
|
|
794
|
-
if isinstance(msg, dttlib.MessageJob.SetMessage):
|
|
795
|
-
self.messages[msg.tag] = msg.msg
|
|
796
|
-
elif isinstance(msg, dttlib.MessageJob.ClearMessage):
|
|
797
|
-
if msg.tag in self.messages:
|
|
798
|
-
del self.messages[msg.tag]
|
|
799
|
-
else:
|
|
800
|
-
logger.info("Unknown message type %s", msg)
|
|
801
|
-
tag = msg.get_tag()
|
|
802
|
-
if tag == "stream_status":
|
|
803
|
-
self._handle_stream_status()
|
|
804
|
-
elif tag == "stream_error" or tag == "REQSIZE":
|
|
805
|
-
if (
|
|
806
|
-
not isinstance(msg, dttlib.MessageJob.SetMessage)
|
|
807
|
-
or msg.msg.severity >= dttlib.Severity.Error
|
|
808
|
-
):
|
|
809
|
-
self._handle_stream_error()
|
|
810
|
-
elif tag == "online_status":
|
|
811
|
-
self._handle_online_status()
|
|
812
|
-
elif tag == "find_channels":
|
|
813
|
-
if "Request SASL authentication protocol" in str(msg):
|
|
814
|
-
self.signal_request_sasl.emit()
|
|
815
|
-
else:
|
|
816
|
-
logger.info("got unhandled message tag %s", tag)
|
|
817
|
-
|
|
818
|
-
def _handle_stream_status(self):
|
|
819
|
-
""" """
|
|
820
|
-
if "stream_status" in self.messages:
|
|
821
|
-
self._set_stream_status(self.messages["stream_status"].message)
|
|
822
|
-
else:
|
|
823
|
-
self._set_stream_status("")
|
|
824
|
-
|
|
825
|
-
def _handle_online_status(self):
|
|
826
|
-
""" """
|
|
827
|
-
if "online_status" in self.messages:
|
|
828
|
-
self._set_online_status(self.messages["online_status"].message)
|
|
829
|
-
else:
|
|
830
|
-
self._set_online_status("")
|
|
831
|
-
|
|
832
789
|
def get_most_similar_channel(self, channel: str) -> Optional[str]:
|
|
833
790
|
"""Return the channel in available_channels that's string-wise most similar
|
|
834
791
|
|
|
@@ -902,25 +859,25 @@ class DataCache(QtCore.QObject):
|
|
|
902
859
|
msg = f"'{suspect}' is not valid for {trend}{online}"
|
|
903
860
|
return msg
|
|
904
861
|
|
|
905
|
-
def
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
def _handle_stream_error(self):
|
|
911
|
-
""" """
|
|
912
|
-
if "stream_error" in self.messages:
|
|
913
|
-
self._post_stream_message(self.messages["stream_error"].message)
|
|
914
|
-
elif "REQSIZE" in self.messages:
|
|
915
|
-
self._set_stream_error(self.messages["REQSIZE"].message)
|
|
916
|
-
else:
|
|
917
|
-
self._set_stream_error("")
|
|
862
|
+
def _handle_dtt_messages(self, msg_job: dttlib.MessageJob):
|
|
863
|
+
if msg_job.get_tag() == "stream_error":
|
|
864
|
+
if isinstance(msg_job, dttlib.MessageJob.SetMessage):
|
|
865
|
+
msg_job = self.handle_invalid_channels(msg_job)
|
|
866
|
+
handle_dtt_messages(msg_job)
|
|
918
867
|
|
|
919
|
-
def
|
|
920
|
-
self.
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
868
|
+
def handle_invalid_channels(
|
|
869
|
+
self, msg_job: dttlib.MessageJob.SetMessage
|
|
870
|
+
) -> dttlib.MessageJob:
|
|
871
|
+
"""
|
|
872
|
+
re-writes and re-sets an invalid channel message to include a guess at the correct channel
|
|
873
|
+
all other messages are passed through (and reset) unchanged.
|
|
924
874
|
|
|
925
|
-
|
|
926
|
-
|
|
875
|
+
This could potentially lead to a race where a message gets cleared and set very quickly.
|
|
876
|
+
"""
|
|
877
|
+
self.prime_invalid_recheck = False
|
|
878
|
+
new_msg = self.guess_invalid_channel(msg_job.msg.message)
|
|
879
|
+
if new_msg != msg_job.msg.message:
|
|
880
|
+
new_msg = dttlib.UserMessage(msg_job.msg.severity, new_msg)
|
|
881
|
+
new_msg_job = dttlib.MessageJob.SetMessage(msg_job.tag, new_msg)
|
|
882
|
+
return new_msg_job
|
|
883
|
+
return msg_job
|
|
@@ -13,6 +13,12 @@ from qtpy.QtCore import Signal # type: ignore
|
|
|
13
13
|
from ndscope.math_config import MathConfigDialog
|
|
14
14
|
|
|
15
15
|
from ._qt import load_ui
|
|
16
|
+
from .configuration.configuration import load_config
|
|
17
|
+
from .data_source_determination import (
|
|
18
|
+
parse_data_source_args,
|
|
19
|
+
add_data_source_args,
|
|
20
|
+
get_first_data_source,
|
|
21
|
+
)
|
|
16
22
|
from .plot import NDScopePlotChannel
|
|
17
23
|
|
|
18
24
|
from typing import TYPE_CHECKING, Set, List, Dict
|
|
@@ -1415,10 +1421,7 @@ def main():
|
|
|
1415
1421
|
description=DESCRIPTION,
|
|
1416
1422
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
1417
1423
|
)
|
|
1418
|
-
parser
|
|
1419
|
-
parser.add_argument(
|
|
1420
|
-
"--nds", metavar="HOST[:PORT]", help=f"NDS server [{const.NDSSERVER}]"
|
|
1421
|
-
)
|
|
1424
|
+
add_data_source_args(parser)
|
|
1422
1425
|
parser.add_argument(
|
|
1423
1426
|
"glob",
|
|
1424
1427
|
nargs="?",
|
|
@@ -1428,7 +1431,10 @@ def main():
|
|
|
1428
1431
|
|
|
1429
1432
|
args = parser.parse_args()
|
|
1430
1433
|
|
|
1431
|
-
|
|
1434
|
+
args_conf = parse_data_source_args(args)
|
|
1435
|
+
load_config(args_conf)
|
|
1436
|
+
|
|
1437
|
+
url_text = get_first_data_source()
|
|
1432
1438
|
|
|
1433
1439
|
server, server_formatted = util.format_nds_server_string(url_text)
|
|
1434
1440
|
|
|
@@ -1447,7 +1453,7 @@ def main():
|
|
|
1447
1453
|
logger.debug("done transform")
|
|
1448
1454
|
|
|
1449
1455
|
dtt = dttlib.DTT(chans_callback)
|
|
1450
|
-
cache = dttlib.NDS2Cache(1 << 30, "").as_ref()
|
|
1456
|
+
cache = dttlib.NDS2Cache(1 << 30, "", url_text, dttlib.Replay.NoReplay()).as_ref()
|
|
1451
1457
|
dtt.set_data_source(cache)
|
|
1452
1458
|
logger.debug(
|
|
1453
1459
|
f"Fetching channel list from {server} (channel glob: '{args.glob}')... "
|
|
File without changes
|