4
4
import os
5
5
import signal
6
6
import sys
7
+ import atexit
8
+ import threading
7
9
8
10
app = Flask (__name__ )
9
11
lsl_process = None
12
+ lsl_running = False
13
+ npg_running = False
14
+ npg_process = None
10
15
app_processes = {}
11
16
12
17
def is_process_running (name ):
@@ -21,36 +26,70 @@ def home():
21
26
22
27
@app .route ("/start_lsl" , methods = ["POST" ])
23
28
def start_lsl ():
24
- global lsl_process
25
- if lsl_process and lsl_process .poll () is None :
29
+ global lsl_process , lsl_running
30
+
31
+ if lsl_running :
26
32
return jsonify ({"status" : "LSL stream already running" , "lsl_started" : True })
33
+
27
34
try :
28
- # Start the LSL stream as a subprocess
29
35
if sys .platform == "win32" :
30
- lsl_process = subprocess .Popen (["python" , "chords.py" , "--lsl" ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , creationflags = subprocess .CREATE_NO_WINDOW )
36
+ lsl_process = subprocess .Popen (["python" , "chords.py" , "--lsl" ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , creationflags = subprocess .CREATE_NO_WINDOW , text = True )
31
37
else :
32
- lsl_process = subprocess .Popen (["python" , "chords.py" , "--lsl" ],stdout = subprocess .PIPE ,stderr = subprocess .PIPE )
33
- output = lsl_process .stderr .readline ().decode ().strip () # Read the initial stderr line
38
+ lsl_process = subprocess .Popen (["python" , "chords.py" , "--lsl" ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True )
34
39
40
+ output = lsl_process .stderr .readline ().strip ()
35
41
print (output )
36
- if output == "No" :
37
- return render_template ("index.html" , lsl_started = False , lsl_status = "Failed to Start" , lsl_color = "red" )
42
+
43
+ if "No" in output :
44
+ lsl_running = False
45
+ return jsonify ({"lsl_started" : False , "lsl_status" : "Failed to Start" , "lsl_color" : "red" })
38
46
else :
39
- return render_template ("index.html" , lsl_started = True , lsl_status = "Running" , lsl_color = "green" )
40
- except subprocess .TimeoutExpired :
41
- return render_template (
42
- "index.html" , lsl_started = False , lsl_status = "Timeout Error" , lsl_color = "red"
43
- )
47
+ lsl_running = True
48
+ return jsonify ({"lsl_started" : True , "lsl_status" : "Running" , "lsl_color" : "green" })
49
+
44
50
except Exception as e :
45
- return render_template ("index.html" , lsl_started = False , lsl_status = f"Error: { e } " , lsl_color = "red" )
51
+ return jsonify ({"lsl_started" : False , "lsl_status" : f"Error: { e } " , "lsl_color" : "red" })
52
+
53
+ def read_npg_output ():
54
+ global npg_process
55
+
56
+ if npg_process :
57
+ for line in iter (npg_process .stdout .readline , '' ):
58
+ print (line .strip ()) # Print npg.py output to the terminal
59
+
60
+ @app .route ("/start_npg" , methods = ["POST" ])
61
+ def start_npg ():
62
+ global npg_process , npg_running
63
+
64
+ if npg_running :
65
+ return jsonify ({"status" : "NPG already running" , "npg_started" : True })
66
+
67
+ try :
68
+ if sys .platform == "win32" :
69
+ npg_process = subprocess .Popen (["python" , "npg.py" ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , creationflags = subprocess .CREATE_NO_WINDOW , text = True , bufsize = 1 )
70
+ else :
71
+ npg_process = subprocess .Popen (["python3" , "npg.py" ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , text = True , bufsize = 1 )
72
+
73
+ # Start a separate thread to read npg.py output
74
+ threading .Thread (target = read_npg_output , daemon = True ).start ()
75
+
76
+ npg_running = True
77
+ return render_template ("index.html" , npg_started = True , npg_status = "Running" , npg_color = "green" , apps_enabled = True )
78
+
79
+ except Exception as e :
80
+ npg_running = False
81
+ return render_template ("index.html" , npg_started = False , npg_status = f"Error: { e } " , npg_color = "red" , apps_enabled = False )
46
82
47
83
@app .route ("/run_app" , methods = ["POST" ])
48
84
def run_app ():
85
+ global lsl_running , npg_running
49
86
app_name = request .form .get ("app_name" )
50
87
51
- # Check if the app is already running
88
+ if not (lsl_running or npg_running ):
89
+ return render_template ("index.html" , message = "Start LSL or NPG first!" , running_apps = app_processes .keys ())
90
+
52
91
if app_name in app_processes and app_processes [app_name ].poll () is None :
53
- return render_template ("index.html" , lsl_started = True , lsl_status = "Running" , lsl_color = "green" , message = f"{ app_name } is already Running " , running_apps = app_processes .keys ())
92
+ return render_template ("index.html" , message = f"{ app_name } is already running " , running_apps = app_processes .keys ())
54
93
55
94
try :
56
95
# Start the app subprocess
@@ -60,19 +99,19 @@ def run_app():
60
99
process = subprocess .Popen (["python" , f"{ app_name } .py" ])
61
100
62
101
app_processes [app_name ] = process
63
- return render_template ("index.html" , lsl_started = True , lsl_status = "Running" , lsl_color = "green" , running_apps = app_processes .keys (), message = None )
64
-
102
+ return render_template ("index.html" , running_apps = app_processes .keys (), message = None )
65
103
except Exception as e :
66
- return render_template ("index.html" , lsl_started = True , lsl_status = "Running" , lsl_color = "green" , message = f"Error starting { app_name } : { e } " , running_apps = app_processes .keys ())
104
+ return render_template ("index.html" , message = f"Error starting { app_name } : { e } " , running_apps = app_processes .keys ())
67
105
68
106
@app .route ("/app_status" , methods = ["GET" ])
69
107
def app_status ():
70
108
# Check the status of all apps
71
109
try :
72
110
statuses = {
73
- app_name : ( process . poll () is None ) # True if running, False if not
74
- for app_name , process in app_processes . items ()
111
+ "lsl_started" : lsl_running ,
112
+ "npg_started" : npg_running
75
113
}
114
+ statuses .update ({app_name : (process .poll () is None ) for app_name , process in app_processes .items ()})
76
115
return jsonify (statuses )
77
116
except Exception as e :
78
117
return jsonify ({"error" : str (e )}), 500
@@ -83,7 +122,7 @@ def stop_lsl():
83
122
return jsonify ({'status' : 'LSL Stream and applications stopped and server is shutting down.' })
84
123
85
124
def stop_all_processes ():
86
- global lsl_process , app_processes
125
+ global lsl_process , npg_process , app_processes , lsl_running , npg_running
87
126
88
127
# Terminate LSL process
89
128
if lsl_process and lsl_process .poll () is None :
@@ -92,25 +131,35 @@ def stop_all_processes():
92
131
lsl_process .wait (timeout = 3 )
93
132
except subprocess .TimeoutExpired :
94
133
lsl_process .kill ()
134
+ lsl_running = False
135
+
136
+ if npg_process and npg_process .poll () is None :
137
+ npg_process .terminate ()
138
+ try :
139
+ npg_process .wait (timeout = 3 )
140
+ except subprocess .TimeoutExpired :
141
+ npg_process .kill ()
142
+ npg_running = False
95
143
96
- # Terminate all app processes
97
- for app_name , process in app_processes .items ():
144
+ for app_name , process in list (app_processes .items ()):
98
145
if process .poll () is None :
99
146
process .terminate ()
100
147
try :
101
148
process .wait (timeout = 3 )
102
149
except subprocess .TimeoutExpired :
103
150
process .kill ()
151
+ del app_processes [app_name ]
104
152
105
- app_processes .clear ()
106
153
print ("All processes terminated." )
107
154
108
155
def handle_sigint (signal_num , frame ):
109
156
print ("\n Ctrl+C pressed! Stopping all processes..." )
110
157
stop_all_processes ()
111
158
sys .exit (0 )
112
159
113
- signal .signal (signal .SIGINT , handle_sigint ) # Register signal handler for Ctrl+C
160
+ # Register signal handler for Ctrl+C
161
+ signal .signal (signal .SIGINT , handle_sigint )
162
+ atexit .register (stop_all_processes )
114
163
115
164
if __name__ == "__main__" :
116
165
app .run (debug = True )
0 commit comments