forked from armooo/meshview
-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathmvrun.py
More file actions
139 lines (110 loc) · 4.38 KB
/
mvrun.py
File metadata and controls
139 lines (110 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import argparse
import logging
import os
import signal
import subprocess
import sys
import threading
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(filename)s:%(lineno)d [pid:%(process)d] %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
# Global list to track running processes
running_processes = []
pid_files = []
def cleanup_pid_file(pid_file):
"""Remove a PID file if it exists"""
if os.path.exists(pid_file):
try:
os.remove(pid_file)
logger.info(f"Removed PID file {pid_file}")
except Exception as e:
logger.error(f"Error removing PID file {pid_file}: {e}")
def signal_handler(sig, frame):
"""Handle Ctrl-C gracefully"""
logger.info("Received interrupt signal (Ctrl-C), shutting down gracefully...")
# Terminate all running processes
for process in running_processes:
if process and process.poll() is None: # Process is still running
try:
logger.info(f"Terminating process PID {process.pid}")
process.terminate()
# Give it a moment to terminate gracefully
try:
process.wait(timeout=5)
logger.info(f"Process PID {process.pid} terminated successfully")
except subprocess.TimeoutExpired:
logger.warning(f"Process PID {process.pid} did not terminate, forcing kill")
process.kill()
process.wait()
except Exception as e:
logger.error(f"Error terminating process PID {process.pid}: {e}")
# Clean up PID files
for pid_file in pid_files:
cleanup_pid_file(pid_file)
logger.info("Shutdown complete")
sys.exit(0)
# Run python in subprocess
def run_script(python_executable, script_name, pid_file, *args):
process = None
try:
# Combine the script name and arguments
command = [python_executable, '-u', script_name] + list(args)
# Run the subprocess (output goes directly to console for real-time viewing)
process = subprocess.Popen(command)
# Track the process globally
running_processes.append(process)
# Write PID to file
with open(pid_file, 'w') as f:
f.write(str(process.pid))
logger.info(f"Started {script_name} with PID {process.pid}, written to {pid_file}")
# Wait for the process to complete
process.wait()
except Exception as e:
logger.error(f"Error running {script_name}: {e}")
finally:
# Clean up PID file when process exits
cleanup_pid_file(pid_file)
# Parse runtime argument (--config) and start subprocess threads
def main():
# Register signal handler for Ctrl-C
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
parser = argparse.ArgumentParser(
description="Helper script to run the database and web frontend in separate threads."
)
# Add --config runtime argument
parser.add_argument('--config', help="Path to the configuration file.", default='config.ini')
parser.add_argument('--pid_dir', help="PID files path.", default='.')
parser.add_argument('--py_exec', help="Path to the Python executable.", default=sys.executable)
args = parser.parse_args()
# PID file paths
db_pid_file = os.path.join(args.pid_dir, 'meshview-db.pid')
web_pid_file = os.path.join(args.pid_dir, 'meshview-web.pid')
# Track PID files globally for cleanup
pid_files.append(db_pid_file)
pid_files.append(web_pid_file)
# Database Thread
dbthrd = threading.Thread(
target=run_script, args=(args.py_exec, 'startdb.py', db_pid_file, '--config', args.config)
)
# Web server thread
webthrd = threading.Thread(
target=run_script, args=(args.py_exec, 'main.py', web_pid_file, '--config', args.config)
)
# Start Meshview subprocess threads
logger.info(f"Starting Meshview with config: {args.config}")
logger.info("Starting database thread...")
dbthrd.start()
logger.info("Starting web server thread...")
webthrd.start()
try:
dbthrd.join()
webthrd.join()
except KeyboardInterrupt:
# This shouldn't be reached due to signal handler, but just in case
signal_handler(signal.SIGINT, None)
if __name__ == '__main__':
main()