Skip to content

Commit 1003c25

Browse files
committed
NPG Connected well but disconnection has some issues(need to resolve)
1 parent 148c97d commit 1003c25

File tree

3 files changed

+109
-50
lines changed

3 files changed

+109
-50
lines changed

app.py

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,53 +70,90 @@ def scan_devices():
7070

7171
@app.route("/connect_device", methods=["POST"])
7272
def connect_device():
73-
global npg_process, npg_running, npg_connection_thread
73+
global npg_process, npg_running, npg_connection_thread, current_message
7474

7575
device_address = request.form.get("device_address")
7676
if not device_address:
7777
return jsonify({"status": "error", "message": "No device selected"})
7878

7979
session['selected_device'] = device_address
8080

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+
8189
def connect_and_monitor():
8290
global npg_process, npg_running, current_message
8391

8492
try:
8593
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)
8797

8898
# Monitor the output for connection status
8999
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, ''):
95101
if "Connected to" in line:
96102
connected = True
97-
npg_running = True
98-
current_message = f"Connected to {device_address}"
103+
if npg_process.poll() is not None:
99104
break
100105

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:
102112
current_message = f"Failed to connect to {device_address}"
113+
npg_running = False
103114
if npg_process.poll() is None:
104115
npg_process.terminate()
105-
npg_running = False
106116

107117
except Exception as e:
108118
current_message = f"Connection error: {str(e)}"
109119
npg_running = False
120+
if npg_process and npg_process.poll() is None:
121+
npg_process.terminate()
110122

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)
113125
npg_connection_thread.start()
114126

115127
return jsonify({"status": "pending"})
116128

117129
@app.route("/check_connection", methods=["GET"])
118130
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..."})
120157

121158
def monitor_process_output(process, process_type):
122159
global lsl_running, npg_running, current_message, app_processes
@@ -138,18 +175,23 @@ def monitor_process_output(process, process_type):
138175

139176
print(f"{process_type} output:", line.strip()) # Debug logging
140177

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+
145183
if process.poll() is None:
146184
process.terminate()
185+
try:
186+
process.wait(timeout=0.5)
187+
except subprocess.TimeoutExpired:
188+
process.kill()
147189
break
148190

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")
153195
if process.poll() is None:
154196
process.terminate()
155197
break
@@ -289,10 +331,10 @@ def run_app():
289331
@app.route("/stream_events")
290332
def stream_events():
291333
def event_stream():
292-
last_state = {"lsl_running": False, "npg_running": False, "running_apps": [], "message": ""}
334+
last_state = None
293335

294336
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)}
296338
if current_state != last_state:
297339
yield f"data: {json.dumps(current_state)}\n\n"
298340
last_state = current_state.copy()

npg-ble.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ async def monitor_connection(self):
103103
self.running = False
104104
break
105105
if self.client and not self.client.is_connected:
106-
print("\nBluetooth disconnected")
106+
print("\nData Interrupted (Bluetooth disconnected)")
107107
self.running = False
108108
break
109109
await asyncio.sleep(0.5)

templates/index.html

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ <h3>Select NPG Device</h3>
292292

293293
connectBtn.disabled = true;
294294
scanBtn.disabled = true;
295-
statusDiv.textContent = `Connecting to device...`;
295+
statusDiv.textContent = 'Connecting to device...';
296296
statusDiv.className = 'scanning-status';
297297

298298
try {
@@ -307,22 +307,38 @@ <h3>Select NPG Device</h3>
307307
const data = await response.json();
308308

309309
if (data.status === 'pending') {
310-
connectionCheckInterval = setInterval(async () => {
310+
const checkStatus = async () => {
311311
const statusResponse = await fetch('/check_connection');
312312
const statusData = await statusResponse.json();
313313

314+
console.log("Connection status:", statusData);
314315
if (statusData.connected) {
315316
clearInterval(connectionCheckInterval);
316-
statusDiv.textContent = `Successfully connected to ${selectedDevice}`;
317+
statusDiv.textContent = statusData.message;
317318
statusDiv.className = 'connected-status';
318-
setTimeout(hideNPGPopup, 1500);
319-
} else if (statusData.message) {
319+
setTimeout(() => {
320+
hideNPGPopup();
321+
window.location.reload(); // Force refresh to sync state
322+
}, 1000);
323+
} else if (statusData.message.includes('Error') ||
324+
statusData.message.includes('Failed')) {
325+
clearInterval(connectionCheckInterval);
320326
statusDiv.textContent = statusData.message;
321327
statusDiv.className = 'error-status';
322328
connectBtn.disabled = false;
323329
scanBtn.disabled = false;
324330
}
325-
}, 1000);
331+
};
332+
333+
// Check every 500ms for faster response
334+
connectionCheckInterval = setInterval(checkStatus, 500);
335+
// Initial immediate check
336+
await checkStatus();
337+
} else if (data.status === 'error') {
338+
statusDiv.textContent = data.message;
339+
statusDiv.className = 'error-status';
340+
connectBtn.disabled = false;
341+
scanBtn.disabled = false;
326342
}
327343
} catch (error) {
328344
console.error('Connection error:', error);
@@ -366,12 +382,13 @@ <h3>Select NPG Device</h3>
366382

367383
function setupEventSource() {
368384
if (eventSource) eventSource.close();
369-
385+
370386
eventSource = new EventSource('/stream_events');
371387
eventSource.onmessage = function(e) {
372388
const data = JSON.parse(e.data);
389+
console.log("SSE Update:", data);
373390

374-
// Update LSL button
391+
// Update LSL button state
375392
const lslButton = document.getElementById('start_lsl_button');
376393
if (data.lsl_running) {
377394
lslButton.textContent = 'LSL Stream Running';
@@ -385,7 +402,7 @@ <h3>Select NPG Device</h3>
385402
lslButton.disabled = data.npg_running;
386403
}
387404

388-
// Update NPG button
405+
// Update NPG button state
389406
const npgButton = document.getElementById('start_npg_button');
390407
if (data.npg_running) {
391408
npgButton.textContent = 'NPG Stream Running';
@@ -399,27 +416,27 @@ <h3>Select NPG Device</h3>
399416
npgButton.disabled = data.lsl_running;
400417
}
401418

402-
// Update app buttons container state
403-
const appButtonsDiv = document.querySelector('.app-buttons');
404-
if (data.lsl_running || data.npg_running) {
405-
appButtonsDiv.classList.remove('disabled-apps');
406-
} else {
407-
appButtonsDiv.classList.add('disabled-apps');
419+
// Update app buttons when stream stops
420+
if ((data.message && data.message.includes('terminated')) ||
421+
(data.message && data.message.includes('disconnected'))) {
422+
updateAppButtons([]);
423+
424+
// If we're in the NPG popup, update status
425+
if (document.getElementById('npgDevicePopup').style.display === 'block') {
426+
const statusDiv = document.getElementById('npgPopupStatus');
427+
statusDiv.textContent = data.message;
428+
statusDiv.className = 'error-status';
429+
430+
// Re-enable scan button if connection failed
431+
document.getElementById('scanDevicesBtn').disabled = false;
432+
document.getElementById('npgConnectBtn').disabled = true;
433+
}
408434
}
409435

410-
// Update individual app buttons
436+
// Update running apps list
411437
if (data.running_apps) {
412438
updateAppButtons(data.running_apps);
413439
}
414-
415-
// Update status message if available
416-
if (data.message) {
417-
const messageDiv = document.createElement('div');
418-
messageDiv.className = 'popup fade-out';
419-
messageDiv.innerHTML = `<p>${data.message}</p>`;
420-
document.querySelector('.container').appendChild(messageDiv);
421-
setTimeout(() => messageDiv.remove(), 3000);
422-
}
423440
};
424441

425442
eventSource.onerror = function() {

0 commit comments

Comments
 (0)