1
- from flask import Flask , render_template , jsonify , request
1
+ from flask import Flask , render_template , request , redirect , url_for , jsonify
2
2
import subprocess
3
3
import psutil
4
- import os
5
4
import signal
6
5
import sys
7
6
import atexit
8
- import threading
7
+ import time
9
8
10
9
app = Flask (__name__ )
11
10
lsl_process = None
12
11
lsl_running = False
13
12
npg_running = False
14
13
npg_process = None
15
14
app_processes = {}
15
+ current_message = None
16
16
17
17
def is_process_running (name ):
18
18
for proc in psutil .process_iter (['pid' , 'name' ]):
@@ -22,107 +22,126 @@ def is_process_running(name):
22
22
23
23
@app .route ("/" )
24
24
def home ():
25
- return render_template ("index.html" , lsl_started = False , lsl_status = "Stopped" , lsl_color = "red" )
25
+ return render_template ("index.html" , lsl_started = lsl_running , npg_started = npg_running , running_apps = [ k for k , v in app_processes . items () if v . poll () is None ], message = current_message )
26
26
27
27
@app .route ("/start_lsl" , methods = ["POST" ])
28
28
def start_lsl ():
29
- global lsl_process , lsl_running
29
+ global lsl_process , lsl_running , current_message
30
+ save_csv = request .form .get ('csv' , 'false' ).lower () == 'true'
31
+
32
+ if npg_running :
33
+ current_message = "Please stop NPG stream first"
34
+ return redirect (url_for ('home' ))
30
35
31
36
if lsl_running :
32
- return jsonify ({"status" : "LSL stream already running" , "lsl_started" : True })
37
+ current_message = "LSL stream already running"
38
+ return redirect (url_for ('home' ))
33
39
34
40
try :
35
- if sys .platform == "win32" :
36
- lsl_process = subprocess .Popen (["python" , "chords.py" , "--lsl" ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , creationflags = subprocess .CREATE_NO_WINDOW , text = True , bufsize = 1 )
37
- else :
38
- lsl_process = subprocess .Popen (["python" , "chords.py" , "--lsl" ], stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , bufsize = 1 )
41
+ command = ["python" , "chords.py" , "--lsl" ]
42
+ if save_csv :
43
+ command .append ("--csv" )
39
44
40
- output = lsl_process . stderr . readline (). strip ()
41
- print ( output )
45
+ creation_flags = subprocess . CREATE_NO_WINDOW if sys . platform == "win32" else 0
46
+ lsl_process = subprocess . Popen ( command , stdout = subprocess . PIPE , stderr = subprocess . PIPE , creationflags = creation_flags , text = True , bufsize = 1 )
42
47
48
+ time .sleep (2 )
49
+ output = lsl_process .stderr .readline ().strip ()
43
50
if "No" in output :
51
+ current_message = "Failed to start LSL stream"
44
52
lsl_running = False
45
- return render_template ("index.html" , lsl_started = False , lsl_status = "Failed to Start" , lsl_color = "red" , apps_enabled = False )
46
53
else :
54
+ current_message = "LSL stream started successfully"
47
55
lsl_running = True
48
- return render_template ("index.html" , lsl_started = True , lsl_status = "Running" , lsl_color = "green" , apps_enabled = True )
49
56
50
57
except Exception as e :
51
- return render_template ("index.html" , lsl_started = False , lsl_status = f"Error: { e } " , lsl_color = "red" )
52
-
53
- def read_npg_output ():
54
- global npg_process , npg_running
58
+ current_message = f"Error starting LSL: { str (e )} "
59
+ lsl_running = False
55
60
56
- if npg_process :
57
- for line in iter (npg_process .stdout .readline , '' ):
58
- line = line .strip ()
59
- if "NPG WebSocket connected!" in line :
60
- npg_running = True
61
+ return redirect (url_for ('home' ))
61
62
62
63
@app .route ("/start_npg" , methods = ["POST" ])
63
64
def start_npg ():
64
- global npg_process , npg_running
65
+ global npg_process , npg_running , current_message
66
+
67
+ if lsl_running :
68
+ current_message = "Please stop LSL stream first"
69
+ return redirect (url_for ('home' ))
65
70
66
71
if npg_running :
67
- return jsonify ({"status" : "NPG already running" , "npg_started" : True })
72
+ current_message = "NPG already running"
73
+ return redirect (url_for ('home' ))
68
74
69
75
try :
70
- if sys .platform == "win32" :
71
- npg_process = subprocess .Popen (["python" , "npg.py" ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , creationflags = subprocess .CREATE_NO_WINDOW , text = True , bufsize = 1 )
72
- else :
73
- npg_process = subprocess .Popen (["python3" , "npg.py" ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , text = True , bufsize = 1 )
76
+ creation_flags = subprocess .CREATE_NO_WINDOW if sys .platform == "win32" else 0
77
+ npg_process = subprocess .Popen (["python" , "npg.py" ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , creationflags = creation_flags , text = True , bufsize = 1 )
74
78
75
- # Start a separate thread to read npg.py output
76
- threading .Thread (target = read_npg_output , daemon = True ).start ()
77
- return render_template ("index.html" , npg_started = True , npg_status = "Starting..." , npg_color = "yellow" , apps_enabled = False )
79
+ time .sleep (2 )
80
+ for line in iter (npg_process .stdout .readline , '' ):
81
+ if "NPG WebSocket connected!" in line .strip ():
82
+ current_message = "NPG stream started successfully"
83
+ npg_running = True
84
+ break
85
+ else :
86
+ current_message = "Failed to connect NPG stream"
87
+ npg_running = False
78
88
79
89
except Exception as e :
90
+ current_message = f"Error starting NPG: { str (e )} "
80
91
npg_running = False
81
- return render_template ("index.html" , npg_started = False , npg_status = f"Error: { e } " , npg_color = "red" , apps_enabled = False )
92
+
93
+ return redirect (url_for ('home' ))
82
94
83
95
@app .route ("/run_app" , methods = ["POST" ])
84
96
def run_app ():
85
- global lsl_running , npg_running
97
+ global current_message
86
98
app_name = request .form .get ("app_name" )
99
+ valid_apps = ["heartbeat_ecg" , "emgenvelope" , "eog" , "ffteeg" , "game" , "beetle" , "gui" , "keystroke" , "csvplotter" ]
87
100
88
101
if not (lsl_running or npg_running ):
89
- return render_template ("index.html" , message = "Start LSL or NPG first!" , running_apps = app_processes .keys ())
102
+ current_message = "Start LSL or NPG first!"
103
+ return redirect (url_for ('home' ))
104
+
105
+ if app_name not in valid_apps :
106
+ current_message = "Invalid application"
107
+ return redirect (url_for ('home' ))
90
108
91
109
if app_name in app_processes and app_processes [app_name ].poll () is None :
92
- return render_template ("index.html" , message = f"{ app_name } is already running" , running_apps = app_processes .keys ())
110
+ current_message = f"{ app_name } is already running"
111
+ return redirect (url_for ('home' ))
93
112
94
113
try :
95
- # Start the app subprocess
96
- if sys .platform == "win32" :
97
- process = subprocess .Popen (["python" , f"{ app_name } .py" ], creationflags = subprocess .CREATE_NO_WINDOW )
98
- else :
99
- process = subprocess .Popen (["python" , f"{ app_name } .py" ])
100
-
114
+ creation_flags = subprocess .CREATE_NO_WINDOW if sys .platform == "win32" else 0
115
+ process = subprocess .Popen (["python" , f"{ app_name } .py" ], creationflags = creation_flags )
116
+
101
117
app_processes [app_name ] = process
102
- return render_template ( "index.html" , running_apps = app_processes . keys (), message = None )
118
+ current_message = f" { app_name } started successfully"
103
119
except Exception as e :
104
- return render_template ( "index.html" , message = f"Error starting { app_name } : { e } " , running_apps = app_processes . keys ())
120
+ current_message = f"Error starting { app_name } : { str ( e ) } "
105
121
106
- @app .route ("/app_status" , methods = ["GET" ])
107
- def app_status ():
108
- # Check the status of all apps
109
- try :
110
- statuses = {
111
- "lsl_started" : lsl_running ,
112
- "npg_started" : npg_running
113
- }
114
- statuses .update ({app_name : (process .poll () is None ) for app_name , process in app_processes .items ()})
115
- return jsonify (statuses )
116
- except Exception as e :
117
- return jsonify ({"error" : str (e )}), 500
118
-
119
- @app .route ("/stop_lsl" , methods = ['POST' ])
120
- def stop_lsl ():
122
+ return redirect (url_for ('home' ))
123
+
124
+ @app .route ("/stop_all" , methods = ['POST' ])
125
+ def stop_all ():
126
+ global current_message
121
127
stop_all_processes ()
122
- return jsonify ({'status' : 'LSL Stream and applications stopped and server is shutting down.' })
128
+ current_message = "All processes stopped"
129
+ return redirect (url_for ('home' ))
130
+
131
+ def cleanup_processes ():
132
+ global app_processes
133
+ app_processes = {
134
+ k : v for k , v in app_processes .items ()
135
+ if v .poll () is None # Only keep running processes
136
+ }
137
+
138
+ @app .route ("/check_app_status" , methods = ["GET" ])
139
+ def check_app_status ():
140
+ cleanup_processes () # Remove finished processes
141
+ return jsonify ({"running_apps" : list (app_processes .keys ())})
123
142
124
143
def stop_all_processes ():
125
- global lsl_process , npg_process , app_processes , lsl_running , npg_running
144
+ global lsl_process , npg_process , app_processes , lsl_running , npg_running , current_message
126
145
127
146
# Terminate LSL process
128
147
if lsl_process and lsl_process .poll () is None :
0 commit comments