totalsync-webinterface 0.1.1__tar.gz → 0.1.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.
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/.gitignore +43 -20
- totalsync_webinterface-0.1.2/.hgignore +7 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/PKG-INFO +6 -2
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/README.md +1 -1
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/pyproject.toml +7 -1
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/serial_dump.py +13 -12
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/teensy_commander.py +105 -10
- totalsync_webinterface-0.1.1/.hgignore +0 -17
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/LICENSE +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/__init__.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/camera.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/curses_interface.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/fake_picamera.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/packet.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/pin_sheet.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/serial_dummy.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/CanvasCamera.js +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/CanvasPlot.js +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/favicon.ico +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/index.html +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/interface.js +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/reconnecting-websocket/LICENSE.txt +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/reconnecting-websocket/README.md +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/reconnecting-websocket/package.json +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/reconnecting-websocket/reconnecting-websocket.js +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/style.css +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/webgl-plot/LICENSE +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/webgl-plot/README.md +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/webgl-plot/dist/webglplot.esm.js +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/webgl-plot/package.json +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web_interface.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/websocket_server/LICENSE +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/websocket_server/__init__.py +0 -0
- {totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/websocket_server/websocket_server.py +0 -0
|
@@ -68,7 +68,6 @@ LogFile/
|
|
|
68
68
|
*.elf
|
|
69
69
|
*.bin
|
|
70
70
|
.build/
|
|
71
|
-
Teensy41_Totalsync/build/
|
|
72
71
|
|
|
73
72
|
# ---------------------------------------------------------------------------
|
|
74
73
|
# Node
|
|
@@ -84,28 +83,49 @@ yarn-error.log*
|
|
|
84
83
|
# ---------------------------------------------------------------------------
|
|
85
84
|
# JetBrains IDEs (PyCharm, CLion)
|
|
86
85
|
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.idea/
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
86
|
+
# Two projects live in this repo: PyCharm at the root and CLion at firmware/.
|
|
87
|
+
# Every pattern is therefore written `**/.idea/...`, not `.idea/...`: a pattern
|
|
88
|
+
# containing a slash anywhere but at the end is anchored to the directory of
|
|
89
|
+
# the .gitignore that holds it, so the plain form covered the root project only
|
|
90
|
+
# and left firmware/.idea/ exposed.
|
|
91
|
+
#
|
|
92
|
+
# Listed file by file rather than as a blanket `.idea/`, because two things in
|
|
93
|
+
# there are deliberately shared: inspectionProfiles/ (the lint settings the
|
|
94
|
+
# project agrees on) and vcs.xml (the Git root mapping — firmware/.idea/vcs.xml
|
|
95
|
+
# is what points CLion at the repository one level up).
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
**/.idea/workspace.xml
|
|
98
|
+
**/.idea/tasks.xml
|
|
99
|
+
**/.idea/usage.statistics.xml
|
|
100
|
+
**/.idea/dictionaries/
|
|
101
|
+
**/.idea/shelf/
|
|
102
|
+
**/.idea/httpRequests/
|
|
103
|
+
**/.idea/queries/
|
|
104
|
+
**/.idea/modules.xml
|
|
105
|
+
**/.idea/modules/
|
|
106
|
+
# The IDE writes its own .idea/.gitignore at project creation; this file
|
|
107
|
+
# supersedes it, and having both means the two can disagree.
|
|
108
|
+
**/.idea/.gitignore
|
|
109
|
+
# Machine-local: PyCharm keeps the interpreter path in misc.xml, CLion the
|
|
110
|
+
# PlatformIO linkage; deployment.xml holds SSH targets; editor.xml the CLion
|
|
111
|
+
# inspection severities. All four are regenerated by the IDE.
|
|
112
|
+
**/.idea/misc.xml
|
|
113
|
+
**/.idea/deployment.xml
|
|
114
|
+
**/.idea/editor.xml
|
|
115
|
+
**/.idea/webResources.xml
|
|
116
|
+
**/.idea/**/dataSources/
|
|
117
|
+
**/.idea/**/dataSources.ids
|
|
118
|
+
**/.idea/**/dataSources.local.xml
|
|
119
|
+
**/.idea/**/sqlDataSources.xml
|
|
120
|
+
**/.idea/**/dynamic.xml
|
|
121
|
+
# Module files, anywhere — covers **/.idea/*.iml too.
|
|
105
122
|
*.iml
|
|
106
123
|
*.iws
|
|
107
124
|
cmake-build-*/
|
|
108
125
|
|
|
126
|
+
# JetBrains AI Assistant / MCP scratch state, written per developer.
|
|
127
|
+
**/.ai/
|
|
128
|
+
|
|
109
129
|
# ---------------------------------------------------------------------------
|
|
110
130
|
# Other editors
|
|
111
131
|
# ---------------------------------------------------------------------------
|
|
@@ -138,9 +158,12 @@ $RECYCLE.BIN/
|
|
|
138
158
|
.directory
|
|
139
159
|
.fuse_hidden*
|
|
140
160
|
.nfs*
|
|
141
|
-
/.idea/webResources.xml
|
|
142
161
|
|
|
143
162
|
# ---------------------------------------------------------------------------
|
|
144
163
|
# Sphinx / ReadTheDocs
|
|
145
164
|
# ---------------------------------------------------------------------------
|
|
146
165
|
docs/_build/
|
|
166
|
+
|
|
167
|
+
# Staged by packages/totalsync_utils/hatch_build.py from firmware/ at build time;
|
|
168
|
+
# the tree at firmware/ is the source of truth.
|
|
169
|
+
/packages/totalsync_utils/totalsync_utils/data/firmware/
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# This repository is git-only. The file exists to stop hatchling's search for an
|
|
2
|
+
# hgignore -- which walks up from the project root and only stops at a .hg directory --
|
|
3
|
+
# from running past the repository into the home directory of whoever runs the build and
|
|
4
|
+
# force-including *that* file into every source distribution.
|
|
5
|
+
#
|
|
6
|
+
# Keep it empty of patterns: .gitignore is the one that matters.
|
|
7
|
+
syntax: glob
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: totalsync-webinterface
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: TotalSync acquisition: serial reader, live browser interface and recorder
|
|
5
|
+
Project-URL: Homepage, https://github.com/NeuroNetMem/totalsync
|
|
6
|
+
Project-URL: Repository, https://github.com/NeuroNetMem/totalsync
|
|
7
|
+
Project-URL: Documentation, https://totalsync.readthedocs.io
|
|
8
|
+
Project-URL: Issues, https://github.com/NeuroNetMem/totalsync/issues
|
|
5
9
|
Author: Ronny Eichler
|
|
6
10
|
Author-email: Morgane Audrain <morganeaudrain@gmail.com>
|
|
7
11
|
License-Expression: GPL-3.0-or-later
|
|
@@ -23,7 +27,7 @@ Description-Content-Type: text/markdown
|
|
|
23
27
|
|
|
24
28
|
# totalsync-webinterface
|
|
25
29
|
|
|
26
|
-
Acquisition side of [TotalSync](https://github.com/
|
|
30
|
+
Acquisition side of [TotalSync](https://github.com/NeuroNetMem/totalsync): reads packets from a
|
|
27
31
|
Teensy 4.1 running the TotalSync firmware over USB serial, records them, and serves a
|
|
28
32
|
live view of all 42 digital/analog channels plus 8 state variables to the browser at
|
|
29
33
|
1 kHz.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# totalsync-webinterface
|
|
2
2
|
|
|
3
|
-
Acquisition side of [TotalSync](https://github.com/
|
|
3
|
+
Acquisition side of [TotalSync](https://github.com/NeuroNetMem/totalsync): reads packets from a
|
|
4
4
|
Teensy 4.1 running the TotalSync firmware over USB serial, records them, and serves a
|
|
5
5
|
live view of all 42 digital/analog channels plus 8 state variables to the browser at
|
|
6
6
|
1 kHz.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "totalsync-webinterface"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "TotalSync acquisition: serial reader, live browser interface and recorder"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -38,6 +38,12 @@ extras = [
|
|
|
38
38
|
[project.scripts]
|
|
39
39
|
totalsync = "totalsync_webinterface.teensy_commander:cli_entry"
|
|
40
40
|
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/NeuroNetMem/totalsync"
|
|
43
|
+
Repository = "https://github.com/NeuroNetMem/totalsync"
|
|
44
|
+
Documentation = "https://totalsync.readthedocs.io"
|
|
45
|
+
Issues = "https://github.com/NeuroNetMem/totalsync/issues"
|
|
46
|
+
|
|
41
47
|
[build-system]
|
|
42
48
|
requires = ["hatchling"]
|
|
43
49
|
build-backend = "hatchling.build"
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/serial_dump.py
RENAMED
|
@@ -2,22 +2,23 @@ import base64
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
import logging
|
|
5
|
-
from tkinter import filedialog
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def browse_button():
|
|
9
|
-
# Allow the user to select a directory to write the dump files into.
|
|
10
|
-
filename = filedialog.askdirectory()
|
|
11
|
-
if filename == "":
|
|
12
|
-
filename = "DataFile/"
|
|
13
|
-
|
|
14
|
-
return filename
|
|
15
5
|
|
|
16
6
|
|
|
17
7
|
class SerialDump:
|
|
18
|
-
|
|
8
|
+
"""Writes the packets coming off the serial line into a timestamped session file.
|
|
9
|
+
|
|
10
|
+
``output_dir`` is asked for by the caller, not by this class. It used to open a
|
|
11
|
+
tkinter directory chooser from here, which meant the recorder could not be used
|
|
12
|
+
without a GUI and -- worse -- that the dialog appeared at whatever point in startup
|
|
13
|
+
this object happened to be constructed, which was after the browser had already been
|
|
14
|
+
pointed at a server that was not listening yet. Choosing the directory is a job for
|
|
15
|
+
whoever is talking to the user; see ``choose_output_directory`` in teensy_commander.
|
|
16
|
+
"""
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
def __init__(self, output_dir):
|
|
19
|
+
self.output_dir = Path(output_dir)
|
|
20
|
+
stamp = datetime.now().strftime("%Y%m%d-%H%M%S_%f")[:-3]
|
|
21
|
+
self.f_b64 = self.output_dir / f'{stamp}.b64'
|
|
21
22
|
self.f_bin = self.f_b64.with_suffix('.bin')
|
|
22
23
|
|
|
23
24
|
self.cobs_file = None
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import logging
|
|
3
|
+
import os
|
|
3
4
|
import sys
|
|
4
5
|
import threading
|
|
5
6
|
import time
|
|
6
7
|
import tkinter as tk
|
|
7
8
|
import webbrowser
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from tkinter import filedialog
|
|
8
11
|
from tkinter import messagebox as mb
|
|
9
12
|
|
|
10
13
|
import serial
|
|
@@ -114,6 +117,58 @@ def choose_serial_port(parent):
|
|
|
114
117
|
return chosen[0] if chosen else ''
|
|
115
118
|
|
|
116
119
|
|
|
120
|
+
def choose_output_directory():
|
|
121
|
+
"""Modal dialog to pick the directory recordings are written into.
|
|
122
|
+
|
|
123
|
+
Returns the directory, or '' if the user cancelled.
|
|
124
|
+
|
|
125
|
+
Deliberately no ``parent=``, however obviously right passing one looks. On macOS Tk
|
|
126
|
+
turns a file dialog with a parent into a *sheet* attached to that window
|
|
127
|
+
(tkMacOSXDialog.c: "Use a sheet if -parent is specified"), and the only window
|
|
128
|
+
available here is the withdrawn root - which still has a backing NSWindow, 200x200
|
|
129
|
+
near the top left corner. The sheet is then anchored under a window a third the
|
|
130
|
+
panel's width, hanging off the edge of the screen, and a sheet cannot be dragged back
|
|
131
|
+
on. Without a parent the panel is free floating, centred and movable. Nothing is
|
|
132
|
+
lost by leaving it out: this dialog is shown before the browser is opened, so it comes
|
|
133
|
+
up in front anyway.
|
|
134
|
+
|
|
135
|
+
``initialdir`` matters more than it looks: askdirectory() without one starts wherever
|
|
136
|
+
Tk was last, which on macOS is commonly '/' - a directory nobody wants to record into
|
|
137
|
+
and one that is not writable anyway. The working directory is where the user cd'd to
|
|
138
|
+
before running the command, so it is the best guess available; a Finder or desktop
|
|
139
|
+
launch, where the working directory is '/', is not, hence the fall back to $HOME.
|
|
140
|
+
"""
|
|
141
|
+
# Not to parent the dialog, but so that a root exists at all: with no default root,
|
|
142
|
+
# tkinter's Dialog.show() builds a throwaway Tk() and destroys it again afterwards,
|
|
143
|
+
# which is the second-interpreter crash get_root() exists to prevent. With one, it
|
|
144
|
+
# reuses it and the teardown is a no-op.
|
|
145
|
+
get_root()
|
|
146
|
+
start = Path.cwd()
|
|
147
|
+
if start == Path(start.anchor) or not os.access(start, os.W_OK):
|
|
148
|
+
start = Path.home()
|
|
149
|
+
return filedialog.askdirectory(
|
|
150
|
+
initialdir=str(start),
|
|
151
|
+
title='Where should TotalSync write this session?')
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def prepare_output_directory(directory):
|
|
155
|
+
"""Make ``directory`` usable as a recording target, or raise ValueError saying why.
|
|
156
|
+
|
|
157
|
+
Called before the serial port and the servers come up, so that a bad path is reported
|
|
158
|
+
while nothing is running yet rather than by the first packet failing to be written.
|
|
159
|
+
"""
|
|
160
|
+
directory = Path(directory).expanduser()
|
|
161
|
+
if directory.exists() and not directory.is_dir():
|
|
162
|
+
raise ValueError(f'not a directory: {directory}')
|
|
163
|
+
try:
|
|
164
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
except OSError as exc:
|
|
166
|
+
raise ValueError(f'cannot create {directory}: {exc}') from None
|
|
167
|
+
if not os.access(directory, os.W_OK):
|
|
168
|
+
raise ValueError(f'not writable: {directory}')
|
|
169
|
+
return directory
|
|
170
|
+
|
|
171
|
+
|
|
117
172
|
def welcome_dialog():
|
|
118
173
|
"""Show the startup window.
|
|
119
174
|
|
|
@@ -127,9 +182,9 @@ def welcome_dialog():
|
|
|
127
182
|
quitting = []
|
|
128
183
|
|
|
129
184
|
def on_play():
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
|
|
185
|
+
# Just close the window: the browser is opened by main(), once the servers are
|
|
186
|
+
# actually listening. Opening it from here is what used to greet the user with
|
|
187
|
+
# "unable to connect".
|
|
133
188
|
win.destroy()
|
|
134
189
|
|
|
135
190
|
def on_quit():
|
|
@@ -161,8 +216,8 @@ def welcome_dialog():
|
|
|
161
216
|
|
|
162
217
|
|
|
163
218
|
class TeensyCommander:
|
|
164
|
-
def __init__(self, serial_port, http_port, ws_port, curses_screen,
|
|
165
|
-
channel_labels=None):
|
|
219
|
+
def __init__(self, serial_port, http_port, ws_port, curses_screen, output_dir,
|
|
220
|
+
write_bin=False, use_dummy=False, channel_labels=None):
|
|
166
221
|
self.n_packet = 0
|
|
167
222
|
self.packets_per_second = 0
|
|
168
223
|
self.packet_timings = []
|
|
@@ -183,7 +238,8 @@ class TeensyCommander:
|
|
|
183
238
|
self.shell_gui = CursesUI(self, curses_screen) if curses_screen is not None else None
|
|
184
239
|
time.sleep(0.05) # give some time to let log display catch all startup messages
|
|
185
240
|
|
|
186
|
-
self.
|
|
241
|
+
self.output_dir = Path(output_dir)
|
|
242
|
+
self.serial_dump = SerialDump(self.output_dir)
|
|
187
243
|
self.serial_port = serial_port
|
|
188
244
|
self.web_server = WebInterface(http_port, ws_port, self, channel_labels=channel_labels)
|
|
189
245
|
|
|
@@ -279,7 +335,6 @@ class TeensyCommander:
|
|
|
279
335
|
logging.debug('Reset')
|
|
280
336
|
try:
|
|
281
337
|
reset = pack_reset_packet()
|
|
282
|
-
SerialDump()
|
|
283
338
|
if reset is None:
|
|
284
339
|
logging.error('Failed to pack!')
|
|
285
340
|
return
|
|
@@ -324,7 +379,8 @@ class TeensyCommander:
|
|
|
324
379
|
|
|
325
380
|
|
|
326
381
|
def main(screen, cli_args, channel_labels=None):
|
|
327
|
-
#
|
|
382
|
+
# menu() builds its control window on this root, so it has to exist before
|
|
383
|
+
# run_forever() gets there.
|
|
328
384
|
get_root()
|
|
329
385
|
logging.info(
|
|
330
386
|
"Known serial ports: " + repr(sorted([comport.device for comport in serial.tools.list_ports.comports()])))
|
|
@@ -335,9 +391,19 @@ def main(screen, cli_args, channel_labels=None):
|
|
|
335
391
|
http_port=cli_args.http_port,
|
|
336
392
|
ws_port=cli_args.ws_port,
|
|
337
393
|
curses_screen=screen,
|
|
394
|
+
output_dir=cli_args.output_dir,
|
|
338
395
|
write_bin=cli_args.binfile,
|
|
339
396
|
use_dummy=cli_args.dummy,
|
|
340
397
|
channel_labels=channel_labels)
|
|
398
|
+
|
|
399
|
+
# Here and not earlier. WebInterface(), constructed inside TeensyCommander.__init__,
|
|
400
|
+
# binds the HTTP port, and the serial port is opened after that -- so reaching this
|
|
401
|
+
# line means the whole stack came up and a request will be answered. The browser used
|
|
402
|
+
# to be opened from the Play button instead, before any of it existed, which is what
|
|
403
|
+
# produced "unable to connect" and made the Reload button necessary.
|
|
404
|
+
if not cli_args.no_browser:
|
|
405
|
+
open_web_interface()
|
|
406
|
+
|
|
341
407
|
try:
|
|
342
408
|
tc.run_forever()
|
|
343
409
|
except KeyboardInterrupt:
|
|
@@ -353,8 +419,17 @@ def cli_entry():
|
|
|
353
419
|
parser = argparse.ArgumentParser()
|
|
354
420
|
parser.add_argument('-s', '--serial_port', default=None,
|
|
355
421
|
help='Serial port of the Teensy. If omitted, a startup dialog asks for one.')
|
|
356
|
-
parser.add_argument('-
|
|
357
|
-
|
|
422
|
+
parser.add_argument('-o', '--output-dir', default=None, metavar='DIR',
|
|
423
|
+
help='Directory to write the recording into. If omitted, a '
|
|
424
|
+
'startup dialog asks for one.')
|
|
425
|
+
parser.add_argument('--no-browser', action='store_true',
|
|
426
|
+
help='Do not open the web interface in a browser at startup')
|
|
427
|
+
parser.add_argument('-w', '--ws_port', type=int, default=WS_PORT,
|
|
428
|
+
help=f'Websocket port (default {WS_PORT}). Note that the browser '
|
|
429
|
+
f'client hardcodes {WS_PORT}, so changing this stops the '
|
|
430
|
+
f'page from receiving data.')
|
|
431
|
+
parser.add_argument('-H', '--http_port', type=int, default=HTTP_PORT,
|
|
432
|
+
help=f'Port the web interface is served on (default {HTTP_PORT})')
|
|
358
433
|
parser.add_argument('-B', '--binfile', action='store_true', help='Write decoded binary serial dump file')
|
|
359
434
|
parser.add_argument('-C', '--curses', action='store_true', help='Use cursesUI in terminal')
|
|
360
435
|
parser.add_argument('-D', '--dummy', action='store_true',
|
|
@@ -409,6 +484,26 @@ def cli_entry():
|
|
|
409
484
|
return
|
|
410
485
|
cli_args.serial_port = port or DEFAULT_SERIAL_PORT
|
|
411
486
|
|
|
487
|
+
# Before the servers and the browser, not after. Nothing downstream needs the
|
|
488
|
+
# directory in order to start -- the HTTP server only wants a port and the channel
|
|
489
|
+
# labels -- but it does have to be answered before the browser is pointed at
|
|
490
|
+
# anything, because the dialog blocks the main thread while it is up. Asking here
|
|
491
|
+
# also means it appears while this application still has the focus, rather than
|
|
492
|
+
# behind a browser window that has just been given it.
|
|
493
|
+
if cli_args.output_dir is None:
|
|
494
|
+
cli_args.output_dir = choose_output_directory()
|
|
495
|
+
if not cli_args.output_dir:
|
|
496
|
+
logging.info('No output directory chosen, so nothing was started.')
|
|
497
|
+
destroy_root()
|
|
498
|
+
return
|
|
499
|
+
try:
|
|
500
|
+
cli_args.output_dir = prepare_output_directory(cli_args.output_dir)
|
|
501
|
+
except ValueError as exc:
|
|
502
|
+
logging.error(f'Cannot record into that directory: {exc}')
|
|
503
|
+
destroy_root()
|
|
504
|
+
return 1
|
|
505
|
+
logging.info(f'Recording into {cli_args.output_dir}')
|
|
506
|
+
|
|
412
507
|
if cli_args.curses and curses is not None:
|
|
413
508
|
curses.wrapper(main, cli_args, channel_labels)
|
|
414
509
|
else:
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Use shell-style glob syntax
|
|
2
|
-
syntax: glob
|
|
3
|
-
|
|
4
|
-
# Compiled Python files
|
|
5
|
-
*.pyc
|
|
6
|
-
|
|
7
|
-
# Folder view configuration files
|
|
8
|
-
.DS_Store
|
|
9
|
-
Desktop.ini
|
|
10
|
-
|
|
11
|
-
# Thumbnail cache files
|
|
12
|
-
._*
|
|
13
|
-
Thumbs.db
|
|
14
|
-
|
|
15
|
-
# Files that might appear on external disks
|
|
16
|
-
.Spotlight-V100
|
|
17
|
-
.Trashes
|
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/__init__.py
RENAMED
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/camera.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/packet.py
RENAMED
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/pin_sheet.py
RENAMED
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/serial_dummy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/favicon.ico
RENAMED
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/index.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{totalsync_webinterface-0.1.1 → totalsync_webinterface-0.1.2}/totalsync_webinterface/web/style.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|