SpecLog 0.0.3__tar.gz → 0.0.5__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 (41) hide show
  1. speclog-0.0.5/LICENSE +21 -0
  2. speclog-0.0.5/PKG-INFO +178 -0
  3. speclog-0.0.5/README.md +159 -0
  4. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/SpecLog.py +27 -44
  5. speclog-0.0.5/SpecLog/SpecLogger.py +199 -0
  6. speclog-0.0.5/SpecLog/SpecMonitor.py +58 -0
  7. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/config/config.cfg +3 -1
  8. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/config/config.py +15 -11
  9. speclog-0.0.5/SpecLog/config_editor.py +818 -0
  10. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/debugLog.py +11 -0
  11. speclog-0.0.5/SpecLog/device.py +334 -0
  12. speclog-0.0.5/SpecLog/history.py +195 -0
  13. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/loggerConfig.py +47 -7
  14. speclog-0.0.5/SpecLog/logger_status.py +32 -0
  15. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/monitor.py +426 -204
  16. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/run_logger.pyw +25 -12
  17. speclog-0.0.5/SpecLog/run_monitor.pyw +39 -0
  18. speclog-0.0.5/SpecLog/startup_task.py +62 -0
  19. speclog-0.0.5/SpecLog/ui/config_editor.py +251 -0
  20. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/ui/plotting.py +69 -38
  21. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/ui/plotting.ui +83 -57
  22. speclog-0.0.5/SpecLog/version.py +1 -0
  23. speclog-0.0.5/SpecLog.egg-info/PKG-INFO +178 -0
  24. {speclog-0.0.3 → speclog-0.0.5}/SpecLog.egg-info/SOURCES.txt +9 -1
  25. {speclog-0.0.3 → speclog-0.0.5}/SpecLog.egg-info/entry_points.txt +2 -2
  26. {speclog-0.0.3 → speclog-0.0.5}/pyproject.toml +4 -4
  27. speclog-0.0.5/tests/test_core.py +484 -0
  28. speclog-0.0.3/PKG-INFO +0 -19
  29. speclog-0.0.3/README.md +0 -2
  30. speclog-0.0.3/SpecLog/SpecLogger.py +0 -128
  31. speclog-0.0.3/SpecLog/device.py +0 -156
  32. speclog-0.0.3/SpecLog/run_monitor.pyw +0 -27
  33. speclog-0.0.3/SpecLog/version.py +0 -1
  34. speclog-0.0.3/SpecLog.egg-info/PKG-INFO +0 -19
  35. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/__init__.py +0 -0
  36. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/config/__init__.py +0 -0
  37. {speclog-0.0.3 → speclog-0.0.5}/SpecLog/ui/__init__.py +0 -0
  38. {speclog-0.0.3 → speclog-0.0.5}/SpecLog.egg-info/dependency_links.txt +0 -0
  39. {speclog-0.0.3 → speclog-0.0.5}/SpecLog.egg-info/requires.txt +0 -0
  40. {speclog-0.0.3 → speclog-0.0.5}/SpecLog.egg-info/top_level.txt +0 -0
  41. {speclog-0.0.3 → speclog-0.0.5}/setup.cfg +0 -0
speclog-0.0.5/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yen-Chun Huang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
speclog-0.0.5/PKG-INFO ADDED
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: SpecLog
3
+ Version: 0.0.5
4
+ Summary: A Python Package for logging data from instrumentations
5
+ Author-email: Yen-Chun Huang <yen-chun.huang@bruker.com>
6
+ License-Expression: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: Microsoft :: Windows
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: matplotlib>=3.7.1
13
+ Requires-Dist: numpy>=1.24.3
14
+ Requires-Dist: PySide6>=6.9.0
15
+ Requires-Dist: pyqtgraph>=0.13.3
16
+ Requires-Dist: spinlab>=1.1.3
17
+ Requires-Dist: pyserial>=3.5
18
+ Dynamic: license-file
19
+
20
+ # SpecLog
21
+
22
+ SpecLog records data from connected spectrometer instruments and provides a
23
+ desktop monitor for viewing live and historical measurements.
24
+
25
+ ## Quick start
26
+
27
+ Start the logger:
28
+
29
+ ```text
30
+ SpecLogger
31
+ ```
32
+
33
+ Open the monitor:
34
+
35
+ ```text
36
+ SpecMonitor
37
+ ```
38
+
39
+ Keep the logger running while using the monitor. The logger collects and saves
40
+ instrument data; the monitor reads and displays those saved measurements.
41
+
42
+ ## Configure SpecLog
43
+
44
+ Open the configuration editor:
45
+
46
+ ```text
47
+ SpecLogger --config
48
+ ```
49
+
50
+ The shared configuration is saved at:
51
+
52
+ ```text
53
+ C:\Users\Public\LOG_Config\config.cfg
54
+ ```
55
+
56
+ Use the section list on the left to select global settings or a device.
57
+
58
+ ### Global settings
59
+
60
+ The **SETTINGS** section contains options such as:
61
+
62
+ - Logging interval
63
+ - Log-folder location
64
+ - Maximum log-file size
65
+
66
+ ### Device communication
67
+
68
+ For each device, use the **Communication** tab to configure its connection:
69
+
70
+ - Enable or disable the device
71
+ - Protocol
72
+ - Address or COM port
73
+ - Baud rate
74
+ - Termination
75
+ - Other serial communication attributes
76
+
77
+ ### Device commands
78
+
79
+ Use **Commands > Values** for ordinary measurements. Each command can contain:
80
+
81
+ - Variable name
82
+ - Instrument command
83
+ - Display alias
84
+ - Minimum and maximum limits
85
+ - Expected static value
86
+
87
+ Use **Commands > Status Indicators** for commands that return status bits.
88
+ Click the indicators button to add, remove, reorder, or reverse individual
89
+ indicators. The editor calculates `bits` and `bit_static` automatically.
90
+
91
+ Click **Save** after making changes. The editor validates the configuration and
92
+ keeps the previous file as `config.cfg.bak`. Restart the logger and monitor to
93
+ apply the updated configuration.
94
+
95
+ ## Control the logger
96
+
97
+ Start or stop the logger from a terminal:
98
+
99
+ ```text
100
+ SpecLogger start
101
+ SpecLogger stop
102
+ ```
103
+
104
+ You can also use **Start** and **Stop** at the bottom of the configuration
105
+ editor. Current status is shown as **Running**, **Stopped**, or **Status
106
+ unavailable**. Operation results and errors appear in the **Messages** area.
107
+
108
+ For troubleshooting, start the logger with a visible debug console:
109
+
110
+ ```text
111
+ SpecLogger -debug True
112
+ ```
113
+
114
+ ## Start logging automatically without login
115
+
116
+ Open a terminal as Administrator and enable system startup:
117
+
118
+ ```text
119
+ SpecLogger -startup True
120
+ ```
121
+
122
+ The logger will start automatically after the next computer boot. A 30-second
123
+ delay allows USB and serial drivers to initialize first.
124
+
125
+ To run it immediately without rebooting:
126
+
127
+ ```text
128
+ SpecLogger start
129
+ ```
130
+
131
+ Disable automatic startup from an Administrator terminal:
132
+
133
+ ```text
134
+ SpecLogger -startup False
135
+ ```
136
+
137
+ The configuration editor also provides **Enable** and **Disable** startup
138
+ buttons. Administrator permission is normally required to use them.
139
+
140
+ If the startup task was created by another administrator, a standard account
141
+ may show startup as **Unavailable**. If new log data is still being written, the
142
+ logger status displays **Running (log activity)**. Start and Stop remain disabled
143
+ until the editor is opened with sufficient permission.
144
+
145
+ ## Use the monitor
146
+
147
+ Open the monitor normally:
148
+
149
+ ```text
150
+ SpecMonitor
151
+ ```
152
+
153
+ Use the monitoring-item controls to select which measurements are plotted. Use
154
+ the data-selection controls to choose a start time and duration.
155
+
156
+ To load all available history, leave the date fields at their defaults and click
157
+ **Ok**. The first import may take longer; later queries load only new or changed
158
+ files.
159
+
160
+ Start the monitor with a visible debug console when troubleshooting:
161
+
162
+ ```text
163
+ SpecMonitor -debug True
164
+ ```
165
+
166
+ ## Common status messages
167
+
168
+ - **Running**: The logger is active and directly detectable.
169
+ - **Running (log activity)**: New data confirms that the logger is active, but
170
+ the current Windows account cannot query the system startup task.
171
+ - **Stopped**: The logger is not running.
172
+ - **Status unavailable**: The current account cannot verify the logger state and
173
+ no recent log activity was detected.
174
+ - **Startup Enabled**: Automatic logging is configured for system boot.
175
+ - **Startup Disabled**: The startup task exists but automatic boot startup is
176
+ disabled.
177
+ - **Startup Unavailable**: Open the configuration editor as Administrator to
178
+ inspect or control a task created by another administrator.
@@ -0,0 +1,159 @@
1
+ # SpecLog
2
+
3
+ SpecLog records data from connected spectrometer instruments and provides a
4
+ desktop monitor for viewing live and historical measurements.
5
+
6
+ ## Quick start
7
+
8
+ Start the logger:
9
+
10
+ ```text
11
+ SpecLogger
12
+ ```
13
+
14
+ Open the monitor:
15
+
16
+ ```text
17
+ SpecMonitor
18
+ ```
19
+
20
+ Keep the logger running while using the monitor. The logger collects and saves
21
+ instrument data; the monitor reads and displays those saved measurements.
22
+
23
+ ## Configure SpecLog
24
+
25
+ Open the configuration editor:
26
+
27
+ ```text
28
+ SpecLogger --config
29
+ ```
30
+
31
+ The shared configuration is saved at:
32
+
33
+ ```text
34
+ C:\Users\Public\LOG_Config\config.cfg
35
+ ```
36
+
37
+ Use the section list on the left to select global settings or a device.
38
+
39
+ ### Global settings
40
+
41
+ The **SETTINGS** section contains options such as:
42
+
43
+ - Logging interval
44
+ - Log-folder location
45
+ - Maximum log-file size
46
+
47
+ ### Device communication
48
+
49
+ For each device, use the **Communication** tab to configure its connection:
50
+
51
+ - Enable or disable the device
52
+ - Protocol
53
+ - Address or COM port
54
+ - Baud rate
55
+ - Termination
56
+ - Other serial communication attributes
57
+
58
+ ### Device commands
59
+
60
+ Use **Commands > Values** for ordinary measurements. Each command can contain:
61
+
62
+ - Variable name
63
+ - Instrument command
64
+ - Display alias
65
+ - Minimum and maximum limits
66
+ - Expected static value
67
+
68
+ Use **Commands > Status Indicators** for commands that return status bits.
69
+ Click the indicators button to add, remove, reorder, or reverse individual
70
+ indicators. The editor calculates `bits` and `bit_static` automatically.
71
+
72
+ Click **Save** after making changes. The editor validates the configuration and
73
+ keeps the previous file as `config.cfg.bak`. Restart the logger and monitor to
74
+ apply the updated configuration.
75
+
76
+ ## Control the logger
77
+
78
+ Start or stop the logger from a terminal:
79
+
80
+ ```text
81
+ SpecLogger start
82
+ SpecLogger stop
83
+ ```
84
+
85
+ You can also use **Start** and **Stop** at the bottom of the configuration
86
+ editor. Current status is shown as **Running**, **Stopped**, or **Status
87
+ unavailable**. Operation results and errors appear in the **Messages** area.
88
+
89
+ For troubleshooting, start the logger with a visible debug console:
90
+
91
+ ```text
92
+ SpecLogger -debug True
93
+ ```
94
+
95
+ ## Start logging automatically without login
96
+
97
+ Open a terminal as Administrator and enable system startup:
98
+
99
+ ```text
100
+ SpecLogger -startup True
101
+ ```
102
+
103
+ The logger will start automatically after the next computer boot. A 30-second
104
+ delay allows USB and serial drivers to initialize first.
105
+
106
+ To run it immediately without rebooting:
107
+
108
+ ```text
109
+ SpecLogger start
110
+ ```
111
+
112
+ Disable automatic startup from an Administrator terminal:
113
+
114
+ ```text
115
+ SpecLogger -startup False
116
+ ```
117
+
118
+ The configuration editor also provides **Enable** and **Disable** startup
119
+ buttons. Administrator permission is normally required to use them.
120
+
121
+ If the startup task was created by another administrator, a standard account
122
+ may show startup as **Unavailable**. If new log data is still being written, the
123
+ logger status displays **Running (log activity)**. Start and Stop remain disabled
124
+ until the editor is opened with sufficient permission.
125
+
126
+ ## Use the monitor
127
+
128
+ Open the monitor normally:
129
+
130
+ ```text
131
+ SpecMonitor
132
+ ```
133
+
134
+ Use the monitoring-item controls to select which measurements are plotted. Use
135
+ the data-selection controls to choose a start time and duration.
136
+
137
+ To load all available history, leave the date fields at their defaults and click
138
+ **Ok**. The first import may take longer; later queries load only new or changed
139
+ files.
140
+
141
+ Start the monitor with a visible debug console when troubleshooting:
142
+
143
+ ```text
144
+ SpecMonitor -debug True
145
+ ```
146
+
147
+ ## Common status messages
148
+
149
+ - **Running**: The logger is active and directly detectable.
150
+ - **Running (log activity)**: New data confirms that the logger is active, but
151
+ the current Windows account cannot query the system startup task.
152
+ - **Stopped**: The logger is not running.
153
+ - **Status unavailable**: The current account cannot verify the logger state and
154
+ no recent log activity was detected.
155
+ - **Startup Enabled**: Automatic logging is configured for system boot.
156
+ - **Startup Disabled**: The startup task exists but automatic boot startup is
157
+ disabled.
158
+ - **Startup Unavailable**: Open the configuration editor as Administrator to
159
+ inspect or control a task created by another administrator.
@@ -17,7 +17,7 @@ from .debugLog import *
17
17
 
18
18
 
19
19
  class SpecLog:
20
- def __init__(self, config_file: str = None):
20
+ def __init__(self, config_file: str = None, protocols=None):
21
21
  self.config = loggerConfig(config_file)
22
22
  self.debugLogger = debugLog(config_file).logger
23
23
  self.settings = self.config.settings
@@ -25,6 +25,7 @@ class SpecLog:
25
25
  self.config.commands
26
26
  ) # dictionary {model: {variable: {command, alias, min, max, static}}}
27
27
  self.device_config = self.config.devices
28
+ self.protocols = protocols
28
29
 
29
30
  self.log_dir = self.settings["log_folder_location"] + "/LOG/"
30
31
  self._checkDirectory()
@@ -60,14 +61,12 @@ class SpecLog:
60
61
  if not self.last_query_time or now - self.last_query_time > self.delay:
61
62
  warning_level = 0
62
63
  self._setTimeInDataDictByVariable() # update time
63
- devices_info = (
64
- self.devices.devices_info
64
+ device_info = (
65
+ self.devices.device_info
65
66
  ) # dictionary: {model: {status, config_status, device, id_command}}
66
67
  for name, info in self.commands.items():
67
68
  delimiter = self.device_config[name]["delimiter"]
68
69
  index = self.device_config[name]["index"]
69
- device = devices_info[name]["device"]
70
- termination = self.device_config[name]["termination"]
71
70
  for variable in info.keys():
72
71
  info[variable]['min'], info[variable]['max'], info[variable]['static']
73
72
  if self.devices.checkDeviceStatus(
@@ -75,13 +74,12 @@ class SpecLog:
75
74
  ): # check the connection of a device
76
75
  try:
77
76
  command = info[variable]["command"]
78
- device.write((command+termination).encode()) # send command to device
79
- data_string = device.read_until(termination.encode()).decode()
77
+ data_string = self.devices.query(name, command)
80
78
  data = self._returnStringConverter(
81
79
  data_string, delimiter, index
82
80
  )
83
81
  except Exception as err:
84
- self.devices.device_info["status"] = False
82
+ self.debugLogger.error(err)
85
83
  data = "nan"
86
84
 
87
85
  else:
@@ -92,22 +90,25 @@ class SpecLog:
92
90
  if data == "nan":
93
91
  warning_level = 2
94
92
 
95
- elif info[variable]['static'] and float(data) != info[variable]['static']:
93
+ elif (
94
+ info[variable]['static'] is not None
95
+ and float(data) != info[variable]['static']
96
+ ):
96
97
  warning_level = 2
97
98
 
98
99
  else:
99
- if info[variable]['min']:
100
- if float(data) <= info[variable]['min'] * 1.05:
101
- warning_level = max(warning_level, 1)
102
- elif float(data) < info[variable]['min']:
100
+ if info[variable]['min'] is not None:
101
+ if float(data) < info[variable]['min']:
103
102
  warning_level = 2
103
+ elif float(data) <= info[variable]['min'] * 1.05:
104
+ warning_level = max(warning_level, 1)
104
105
 
105
106
 
106
- if info[variable]['max']:
107
- if float(data) >= info[variable]['max'] * 0.95:
108
- warning_level = max(warning_level, 1)
109
- elif float(data) > info[variable]['max']:
107
+ if info[variable]['max'] is not None:
108
+ if float(data) > info[variable]['max']:
110
109
  warning_level = 2
110
+ elif float(data) >= info[variable]['max'] * 0.95:
111
+ warning_level = max(warning_level, 1)
111
112
  self.warning = warning_level
112
113
 
113
114
  self.last_query_time = now
@@ -122,37 +123,18 @@ class SpecLog:
122
123
  """
123
124
  Call DEVICE
124
125
  """
125
- self.devices = DEVICE(self.config, self.debugLogger) # establish communication
126
+ self.devices = DEVICE(
127
+ self.config, self.debugLogger, protocols=self.protocols
128
+ ) # establish communication
126
129
  self.devices._getPorts()
127
130
  self.available_addresses = self.devices.deviceAddresses
128
131
  return True
129
132
 
130
133
  def reconnectDevices(self):
131
- restart_DEVICE = False
132
- self.devices._getPorts()
133
- for name in self.device_config.keys():
134
- address = self.device_config[name]["address"]
135
-
136
- if (
137
- address not in self.devices.deviceAddresses
138
- and address in self.available_addresses
139
- ): # when a connection is loss
140
- # This step is to prevent the moment when the device is shown in the resource manager but connection fails
141
- self.available_addresses.remove(address)
142
-
143
- if (
144
- address in self.devices.deviceAddresses
145
- and address not in self.available_addresses
146
- and not self.devices.devices_info[name]["status"]
147
- ):
148
- restart_DEVICE = True
149
-
150
- if restart_DEVICE:
151
- del self.devices # delete the DEVICE class
152
- return self.connectDevices()
153
-
154
- else:
155
- return False
134
+ return self.devices.reconnectDevices()
135
+
136
+ def close(self):
137
+ self.devices.close()
156
138
 
157
139
  def _checkDirectory(self):
158
140
  if not os.path.exists(self.log_dir):
@@ -249,6 +231,7 @@ class SpecLog:
249
231
  Returns:
250
232
  data (string): return data in str
251
233
  """
234
+ data = "nan"
252
235
  try:
253
236
  if delimiter:
254
237
  data = string.split(delimiter)[index]
@@ -259,7 +242,7 @@ class SpecLog:
259
242
  data = data.replace("\t", "")
260
243
  data = data.replace("\r", "")
261
244
  data = data.strip()
262
- except:
245
+ except (IndexError, AttributeError):
263
246
  self.debugLogger.error("String Convert Fail: recived: %s, delimiter: %s, index: %s" %(string, delimiter, index))
264
247
  return data
265
248
 
@@ -0,0 +1,199 @@
1
+ """
2
+ This is the python program to control SpecLog
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import argparse
8
+ import shutil
9
+ import subprocess
10
+ from .SpecLog import *
11
+ from .logger_status import is_logger_running
12
+ from .startup_task import (
13
+ TASK_SCHEDULER,
14
+ control_arguments,
15
+ create_arguments,
16
+ disable_arguments,
17
+ query_arguments,
18
+ )
19
+
20
+ # System-start task and desktop shortcuts (public)
21
+ desktop_folder = os.path.join(os.environ["USERPROFILE"], "Desktop")
22
+
23
+ source_running_logger = os.path.join(
24
+ os.path.dirname(sys.executable), "scripts", "SpecLogger_running.exe"
25
+ )
26
+
27
+ source_monitor = os.path.join(
28
+ os.path.dirname(sys.executable), "scripts", "SpecMonitor_running.exe"
29
+ )
30
+
31
+
32
+ def _run_task_scheduler(arguments, runner=None):
33
+ runner = runner or subprocess.run
34
+ return runner(
35
+ [TASK_SCHEDULER, *arguments],
36
+ capture_output=True,
37
+ text=True,
38
+ creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
39
+ )
40
+
41
+
42
+ def configure_startup(enabled, executable=None, runner=None):
43
+ """Create/enable or disable the no-login SpecLogger startup task."""
44
+ executable = os.path.abspath(executable or source_running_logger)
45
+ if enabled:
46
+ if not os.path.isfile(executable):
47
+ raise RuntimeError(f"SpecLogger runner was not found: {executable}")
48
+ result = _run_task_scheduler(
49
+ create_arguments(executable),
50
+ runner,
51
+ )
52
+ success_message = "SpecLogger will run at system startup without login."
53
+ else:
54
+ query = _run_task_scheduler(
55
+ query_arguments(), runner
56
+ )
57
+ if query.returncode:
58
+ return "SpecLogger startup task is not installed."
59
+ result = _run_task_scheduler(
60
+ disable_arguments(), runner
61
+ )
62
+ success_message = "SpecLogger startup task is disabled."
63
+
64
+ if result.returncode:
65
+ details = (result.stderr or result.stdout).strip()
66
+ raise RuntimeError(
67
+ details
68
+ or "Windows Task Scheduler rejected the request. Run as administrator."
69
+ )
70
+ return success_message
71
+
72
+
73
+ def control_scheduled_logger(action, runner=None):
74
+ """Start/stop the startup task, or return None when it is not installed."""
75
+ query = _run_task_scheduler(
76
+ query_arguments(), runner
77
+ )
78
+ if query.returncode:
79
+ return None
80
+ result = _run_task_scheduler(
81
+ control_arguments(action), runner
82
+ )
83
+ if result.returncode:
84
+ details = (result.stderr or result.stdout).strip()
85
+ raise RuntimeError(
86
+ details
87
+ or "Windows Task Scheduler rejected the request. Run as administrator."
88
+ )
89
+ return (
90
+ "SpecLogger scheduled task start requested."
91
+ if action == "start"
92
+ else "SpecLogger scheduled task stopped."
93
+ )
94
+
95
+ def _build_parser():
96
+ parser = argparse.ArgumentParser(prog="SpecLogger")
97
+ parser.add_argument(
98
+ "status",
99
+ type=str,
100
+ nargs="?",
101
+ default=None,
102
+ choices=["start", "stop"],
103
+ help="To start/stop SpecLogger. If no argument, the SpecLogger will start by default",
104
+ )
105
+ parser.add_argument(
106
+ "-desktop",
107
+ type=str,
108
+ default=False,
109
+ choices=["True", "False"],
110
+ help="To create desktop icons",
111
+ )
112
+ parser.add_argument(
113
+ "-startup",
114
+ type=str,
115
+ default=None,
116
+ choices=["True", "False"],
117
+ help="To enable/disable SpecLogger at startup without login.",
118
+ )
119
+ parser.add_argument(
120
+ "-debug",
121
+ type=str,
122
+ default="False",
123
+ choices=["True", "False"],
124
+ help="To start debug console SpecLogger.",
125
+ )
126
+ parser.add_argument(
127
+ "--config",
128
+ action="store_true",
129
+ help="Open the public SpecLog configuration editor.",
130
+ )
131
+ return parser
132
+
133
+
134
+ def main_func(argv=None):
135
+ args = _build_parser().parse_args(argv)
136
+
137
+ if args.config:
138
+ from .config_editor import main_func as open_config_editor
139
+
140
+ return open_config_editor()
141
+
142
+ if args.startup is not None:
143
+ try:
144
+ print(configure_startup(args.startup == "True"))
145
+ except RuntimeError as error:
146
+ print(f"Could not configure SpecLogger startup: {error}", file=sys.stderr)
147
+ return 1
148
+
149
+ if args.desktop == "True":
150
+ target_logger = os.path.join(desktop_folder, "SpecLogger_running.exe")
151
+ target_monitor = os.path.join(desktop_folder, "SpecMonitor.exe")
152
+
153
+ if not os.path.exists(target_logger):
154
+ shutil.copy(source_running_logger, target_logger)
155
+ print("Create SpecLogger_running.exe on the desktop.")
156
+ else:
157
+ print("SpecLogger_running.exe is on desktop already.")
158
+
159
+ if not os.path.exists(target_monitor):
160
+ shutil.copy(source_monitor, target_monitor)
161
+ print("Create SpecMonitor.exe on the desktop.")
162
+ else:
163
+ print("SpecMonitor.exe is on desktop already.")
164
+
165
+ if not args.startup and not args.desktop and not args.status: # not arguments
166
+ args.status = "start"
167
+
168
+ if args.status in {"start", "stop"}:
169
+ try:
170
+ task_message = control_scheduled_logger(args.status)
171
+ except RuntimeError as error:
172
+ print(f"Could not control SpecLogger scheduled task: {error}", file=sys.stderr)
173
+ return 1
174
+ if task_message is not None:
175
+ print(task_message)
176
+ return 0
177
+
178
+ if args.status == "start":
179
+ if is_logger_running():
180
+ print("SpecLogger has started already.")
181
+ return
182
+ if args.debug == "False":
183
+ subprocess.Popen(
184
+ "SpecLogger_running.exe", creationflags=subprocess.CREATE_NO_WINDOW
185
+ )
186
+ print("SpecLogger started")
187
+ elif args.debug == "True":
188
+ os.startfile("SpecLogger_running.exe")
189
+ print("SpecLogger debug mode started")
190
+
191
+ elif args.status == "stop":
192
+ os.system("taskkill /im SpecLogger_running.exe /F /t")
193
+ print("SpecLogger stopped")
194
+ else: # ignore
195
+ return
196
+
197
+
198
+ if __name__ == "__main__":
199
+ main_func()