fix: Different approach on the COM Port and MQTT Fix

This commit is contained in:
2026-02-27 09:57:49 +02:00
parent 1a5e448e4e
commit 12784462f9

View File

@@ -401,25 +401,37 @@ function StepFlash({ device, onFlashed }) {
// Start reading raw UART output from the device after flash+reset
const startSerialMonitor = async (port) => {
serialActiveRef.current = true;
// Give the OS a moment to fully release the port from esptool before we re-open
await new Promise((r) => setTimeout(r, 500));
// Wait for the OS/browser to fully release the port after esptool closed it
await new Promise((r) => setTimeout(r, 1000));
try {
await port.open({ baudRate: 115200 });
} catch (openErr) {
appendSerial(`[Error opening port: ${openErr.message}]`);
scrollSerial();
return;
}
// Use getReader() directly — avoids locking issues from pipeTo()
let reader;
try {
reader = port.readable.getReader();
} catch (readerErr) {
appendSerial(`[Error getting reader: ${readerErr.message}]`);
scrollSerial();
try { await port.close(); } catch (_) {}
return;
}
const decoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(decoder.writable);
const reader = decoder.readable.getReader();
serialReaderRef.current = reader;
const textDecoder = new TextDecoder();
let lineBuffer = "";
try {
while (serialActiveRef.current) {
const { value, done: streamDone } = await reader.read();
if (streamDone) break;
lineBuffer += value;
lineBuffer += textDecoder.decode(value, { stream: true });
const lines = lineBuffer.split(/\r?\n/);
lineBuffer = lines.pop(); // keep incomplete last fragment
for (const line of lines) {
@@ -431,13 +443,16 @@ function StepFlash({ device, onFlashed }) {
}
} catch (_) {
// Reader cancelled on cleanup — expected
} finally {
try { reader.releaseLock(); } catch (_) {}
}
};
const stopSerialMonitor = async () => {
serialActiveRef.current = false;
try { await serialReaderRef.current?.cancel(); } catch (_) {}
try { portRef.current?.close(); } catch (_) {}
try { serialReaderRef.current?.releaseLock(); } catch (_) {}
try { await portRef.current?.close(); } catch (_) {}
};
const handleFlash = async () => {
@@ -724,31 +739,30 @@ function StepVerify({ device, onVerified }) {
setTimedOut(false);
setError("");
const startTime = Date.now();
const startTime = new Date().toISOString();
intervalRef.current = setInterval(async () => {
try {
const data = await api.get(`/manufacturing/devices/${device.serial_number}`);
if (data.mfg_status === "provisioned") {
clearInterval(intervalRef.current);
clearTimeout(timeoutRef.current);
onVerified(data);
return;
}
// Also accept any last_seen update (heartbeat) as evidence of life
if (data.last_seen) {
const ts = new Date(data.last_seen).getTime();
if (ts > startTime) {
// Poll the heartbeat endpoint — device is verified when it sends a heartbeat
// after we started polling (i.e. after the flash completed)
const hbData = await api.get(
`/mqtt/heartbeats/${device.serial_number}?limit=1&offset=0`
);
if (hbData.heartbeats && hbData.heartbeats.length > 0) {
const latest = hbData.heartbeats[0];
const receivedAt = latest.received_at;
if (receivedAt && receivedAt > startTime) {
clearInterval(intervalRef.current);
clearTimeout(timeoutRef.current);
// Promote to provisioned
// Promote device status to provisioned
try {
await api.request(`/manufacturing/devices/${device.serial_number}/status`, {
method: "PATCH",
body: JSON.stringify({ status: "provisioned", note: "Auto-verified via wizard" }),
});
} catch (_) {}
onVerified({ ...data, mfg_status: "provisioned" });
const deviceData = await api.get(`/manufacturing/devices/${device.serial_number}`);
onVerified({ ...deviceData, mfg_status: "provisioned" });
return;
}
}