@@ -70,53 +70,90 @@ def scan_devices():
70
70
71
71
@app .route ("/connect_device" , methods = ["POST" ])
72
72
def connect_device ():
73
- global npg_process , npg_running , npg_connection_thread
73
+ global npg_process , npg_running , npg_connection_thread , current_message
74
74
75
75
device_address = request .form .get ("device_address" )
76
76
if not device_address :
77
77
return jsonify ({"status" : "error" , "message" : "No device selected" })
78
78
79
79
session ['selected_device' ] = device_address
80
80
81
+ if npg_connection_thread and npg_connection_thread .is_alive ():
82
+ if npg_process and npg_process .poll () is None :
83
+ npg_process .terminate ()
84
+ try :
85
+ npg_process .wait (timeout = 2 )
86
+ except subprocess .TimeoutExpired :
87
+ npg_process .kill ()
88
+
81
89
def connect_and_monitor ():
82
90
global npg_process , npg_running , current_message
83
91
84
92
try :
85
93
script_path = os .path .join (os .path .dirname (os .path .abspath (__file__ )), "npg-ble.py" )
86
- npg_process = subprocess .Popen ([sys .executable , script_path , "--connect" , device_address ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , text = True , bufsize = 1 )
94
+ creation_flags = subprocess .CREATE_NO_WINDOW if sys .platform == "win32" else 0
95
+ npg_process = subprocess .Popen ([sys .executable , script_path , "--connect" , device_address ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , text = True , bufsize = 1 , universal_newlines = True , creationflags = creation_flags )
96
+ time .sleep (1 )
87
97
88
98
# Monitor the output for connection status
89
99
connected = False
90
- start_time = time .time ()
91
- while time .time () - start_time < 10 : # 10 second timeout
92
- line = npg_process .stdout .readline ()
93
- if not line :
94
- break
100
+ for line in iter (npg_process .stdout .readline , '' ):
95
101
if "Connected to" in line :
96
102
connected = True
97
- npg_running = True
98
- current_message = f"Connected to { device_address } "
103
+ if npg_process .poll () is not None :
99
104
break
100
105
101
- if not connected :
106
+ if connected :
107
+ current_message = f"Connected to { device_address } "
108
+ npg_running = True
109
+ monitor_thread = Thread (target = monitor_process_output , args = (npg_process , "npg" ), daemon = True )
110
+ monitor_thread .start ()
111
+ else :
102
112
current_message = f"Failed to connect to { device_address } "
113
+ npg_running = False
103
114
if npg_process .poll () is None :
104
115
npg_process .terminate ()
105
- npg_running = False
106
116
107
117
except Exception as e :
108
118
current_message = f"Connection error: { str (e )} "
109
119
npg_running = False
120
+ if npg_process and npg_process .poll () is None :
121
+ npg_process .terminate ()
110
122
111
- # Start the connection in a separate thread
112
- npg_connection_thread = Thread (target = connect_and_monitor )
123
+ # Start the connection in a new thread
124
+ npg_connection_thread = Thread (target = connect_and_monitor , daemon = True )
113
125
npg_connection_thread .start ()
114
126
115
127
return jsonify ({"status" : "pending" })
116
128
117
129
@app .route ("/check_connection" , methods = ["GET" ])
118
130
def check_connection ():
119
- return jsonify ({"connected" : npg_running , "message" : current_message })
131
+ global npg_running , current_message , npg_process
132
+
133
+ if npg_process is None or npg_process .poll () is not None :
134
+ npg_running = False
135
+ if npg_process :
136
+ output = npg_process .stdout .read ()
137
+ current_message = f"Connection terminated: { output } "
138
+ else :
139
+ current_message = "No active connection"
140
+ return jsonify ({"connected" : False , "message" : current_message })
141
+
142
+ while True :
143
+ line = npg_process .stdout .readline ()
144
+ if not line :
145
+ break
146
+
147
+ if "Connected to" in line :
148
+ npg_running = True
149
+ current_message = line .strip ()
150
+ return jsonify ({"connected" : True , "message" : current_message })
151
+ elif "Data Interrupted" in line or "Data Interrupted (Bluetooth disconnected)" in line :
152
+ npg_running = False
153
+ current_message = line .strip ()
154
+ return jsonify ({"connected" : False , "message" : current_message })
155
+
156
+ return jsonify ({"connected" : npg_running , "message" : current_message or "Connecting..." })
120
157
121
158
def monitor_process_output (process , process_type ):
122
159
global lsl_running , npg_running , current_message , app_processes
@@ -138,18 +175,23 @@ def monitor_process_output(process, process_type):
138
175
139
176
print (f"{ process_type } output:" , line .strip ()) # Debug logging
140
177
141
- if process_type == "lsl" and ("Error while closing serial connection" in line or "disconnected" in line .lower ()):
142
- lsl_running = False
143
- current_message = "LSL stream error - connection closed"
144
- stop_dependent_apps ("lsl" )
178
+ if process_type == "npg" and ("Data Interrupted" in line or "Data Interrupted (Bluetooth disconnected)" in line ):
179
+ current_message = "NPG connection lost - stopping all applications"
180
+ npg_running = False
181
+ stop_dependent_apps ("npg" )
182
+
145
183
if process .poll () is None :
146
184
process .terminate ()
185
+ try :
186
+ process .wait (timeout = 0.5 )
187
+ except subprocess .TimeoutExpired :
188
+ process .kill ()
147
189
break
148
190
149
- elif process_type == "npg " and ("Data Interrupted " in line or "Data Interrupted (Bluetooth disconnected) " in line ):
150
- npg_running = False
151
- current_message = "NPG stream error - data interrupted"
152
- stop_dependent_apps ("npg " )
191
+ elif process_type == "lsl " and ("Error while closing serial connection " in line or "disconnected" in line . lower () ):
192
+ current_message = "LSL stream error - connection closed"
193
+ lsl_running = False
194
+ stop_dependent_apps ("lsl " )
153
195
if process .poll () is None :
154
196
process .terminate ()
155
197
break
@@ -289,10 +331,10 @@ def run_app():
289
331
@app .route ("/stream_events" )
290
332
def stream_events ():
291
333
def event_stream ():
292
- last_state = { "lsl_running" : False , "npg_running" : False , "running_apps" : [], "message" : "" }
334
+ last_state = None
293
335
294
336
while True :
295
- current_state = {"lsl_running" : lsl_running , "npg_running" : npg_running , "running_apps" : [k for k ,v in app_processes .items () if v .poll () is None ], "message" : current_message }
337
+ current_state = {"lsl_running" : lsl_running , "npg_running" : npg_running , "running_apps" : [k for k ,v in app_processes .items () if v .poll () is None ], "message" : current_message , "stream_interrupted" : ( "Data Interrupted" in current_message if current_message else False ) }
296
338
if current_state != last_state :
297
339
yield f"data: { json .dumps (current_state )} \n \n "
298
340
last_state = current_state .copy ()
0 commit comments