mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-25 09:15:49 +03:00
Fix intermittent backup corruption (#4392)
* Fix intermittent backup corruption * Fix sonar * Fix persistence issue and add debugging
This commit is contained in:
parent
616b21446f
commit
1857d80a34
1 changed files with 127 additions and 14 deletions
|
@ -15,13 +15,22 @@ class AutoBackup {
|
|||
constructor() {
|
||||
this.outputHistory = "";
|
||||
this.callback = null;
|
||||
// Store bound handler references to ensure proper removal
|
||||
this.boundReadSerialAdapter = null;
|
||||
this.boundHandleConnect = null;
|
||||
this.boundHandleDisconnect = null;
|
||||
}
|
||||
|
||||
handleConnect(openInfo) {
|
||||
console.log("Connected to serial port:", openInfo);
|
||||
if (openInfo) {
|
||||
serial.removeEventListener("receive", this.readSerialAdapter);
|
||||
serial.addEventListener("receive", this.readSerialAdapter.bind(this));
|
||||
// Ensure we have a fresh start
|
||||
this.cleanupListeners();
|
||||
this.outputHistory = "";
|
||||
|
||||
// Store bound reference for later cleanup
|
||||
this.boundReadSerialAdapter = this.readSerialAdapter.bind(this);
|
||||
serial.addEventListener("receive", this.boundReadSerialAdapter);
|
||||
|
||||
this.run();
|
||||
} else {
|
||||
|
@ -31,10 +40,25 @@ class AutoBackup {
|
|||
|
||||
handleDisconnect(event) {
|
||||
gui_log(i18n.getMessage(event.detail ? "serialPortClosedOk" : "serialPortClosedFail"));
|
||||
this.cleanupListeners();
|
||||
}
|
||||
|
||||
serial.removeEventListener("receive", this.readSerialAdapter);
|
||||
serial.removeEventListener("connect", this.handleConnect);
|
||||
serial.removeEventListener("disconnect", this.handleDisconnect);
|
||||
// New method to ensure all listeners are properly removed
|
||||
cleanupListeners() {
|
||||
if (this.boundReadSerialAdapter) {
|
||||
serial.removeEventListener("receive", this.boundReadSerialAdapter);
|
||||
this.boundReadSerialAdapter = null;
|
||||
}
|
||||
|
||||
if (this.boundHandleConnect) {
|
||||
serial.removeEventListener("connect", this.boundHandleConnect);
|
||||
this.boundHandleConnect = null;
|
||||
}
|
||||
|
||||
if (this.boundHandleDisconnect) {
|
||||
serial.removeEventListener("disconnect", this.boundHandleDisconnect);
|
||||
this.boundHandleDisconnect = null;
|
||||
}
|
||||
}
|
||||
|
||||
readSerialAdapter(info) {
|
||||
|
@ -47,7 +71,8 @@ class AutoBackup {
|
|||
}
|
||||
|
||||
onClose() {
|
||||
serial.addEventListener("disconnect", this.handleDisconnect.bind(this), { once: true });
|
||||
this.boundHandleDisconnect = this.handleDisconnect.bind(this);
|
||||
serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true });
|
||||
serial.disconnect();
|
||||
}
|
||||
|
||||
|
@ -83,14 +108,98 @@ class AutoBackup {
|
|||
console.log("Running backup");
|
||||
|
||||
await this.activateCliMode();
|
||||
await this.sendCommand("diff all");
|
||||
this.waitForCommandCompletion("diff all");
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
this.sendCommand("exit", this.onClose);
|
||||
// remove the command from the output
|
||||
const data = this.outputHistory.split("\n").slice(1).join("\n");
|
||||
await this.save(data);
|
||||
}, 1500);
|
||||
waitForCommandCompletion(command) {
|
||||
// Clear previous output
|
||||
this.outputHistory = "";
|
||||
|
||||
// Add debug mode for troubleshooting
|
||||
const DEBUG = true;
|
||||
|
||||
// Send the command
|
||||
this.sendCommand(command);
|
||||
|
||||
if (DEBUG) console.log(`AutoBackup: Command sent: "${command}"`);
|
||||
|
||||
// Set up a check interval
|
||||
const checkInterval = 100; // Check every 100ms
|
||||
const maxWaitTime = 30000; // Increase to 30 seconds max wait - some configs are large
|
||||
let elapsedTime = 0;
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
elapsedTime += checkInterval;
|
||||
|
||||
if (DEBUG && elapsedTime % 1000 === 0) {
|
||||
console.log(
|
||||
`AutoBackup: Waiting for ${elapsedTime / 1000}s, buffer length: ${this.outputHistory.length} chars`,
|
||||
);
|
||||
if (this.outputHistory.length > 0) {
|
||||
// Show last 30 chars for debugging
|
||||
const lastChars = this.outputHistory.slice(-30).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
||||
console.log(`AutoBackup: Last chars: "${lastChars}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// More robust prompt detection with multiple patterns
|
||||
const hasPrompt =
|
||||
this.outputHistory.endsWith("# ") ||
|
||||
this.outputHistory.endsWith("#\r") ||
|
||||
this.outputHistory.endsWith("#\n") ||
|
||||
this.outputHistory.endsWith("#\r\n") ||
|
||||
this.outputHistory.match(/\r?\n# ?$/);
|
||||
|
||||
if (hasPrompt) {
|
||||
clearInterval(intervalId);
|
||||
|
||||
if (DEBUG) console.log("AutoBackup: Prompt detected, processing output");
|
||||
|
||||
// Process and save the output - more robust parsing
|
||||
let lines = this.outputHistory.split(/\r?\n/);
|
||||
|
||||
// Log line count for debugging
|
||||
if (DEBUG) console.log(`AutoBackup: Received ${lines.length} lines of output`);
|
||||
|
||||
// Check if first line contains the command
|
||||
if (lines[0].includes(command)) {
|
||||
lines = lines.slice(1);
|
||||
if (DEBUG) console.log("AutoBackup: Removed command line from output");
|
||||
}
|
||||
|
||||
// Check if last line is a prompt
|
||||
if (
|
||||
lines.length > 0 &&
|
||||
(lines[lines.length - 1].trim() === "#" || lines[lines.length - 1].trim() === "")
|
||||
) {
|
||||
lines = lines.slice(0, -1);
|
||||
if (DEBUG) console.log("AutoBackup: Removed prompt line from output");
|
||||
}
|
||||
|
||||
const data = lines.join("\n");
|
||||
|
||||
if (DEBUG) console.log(`AutoBackup: Final data length: ${data.length} chars`);
|
||||
|
||||
this.sendCommand("exit", this.onClose.bind(this));
|
||||
this.save(data);
|
||||
}
|
||||
// Check if we've waited too long
|
||||
else if (elapsedTime >= maxWaitTime) {
|
||||
clearInterval(intervalId);
|
||||
console.error(`AutoBackup: Timeout waiting for command completion after ${maxWaitTime / 1000}s`);
|
||||
|
||||
// Try to save what we have (partial data is better than none)
|
||||
if (DEBUG) console.log(`AutoBackup: Saving partial data, buffer length: ${this.outputHistory.length}`);
|
||||
|
||||
const lines = this.outputHistory.split(/\r?\n/);
|
||||
// Remove first line if it contains the command
|
||||
const filteredLines = lines[0].includes(command) ? lines.slice(1) : lines;
|
||||
const data = filteredLines.join("\n");
|
||||
|
||||
this.sendCommand("exit", this.onClose.bind(this));
|
||||
this.save(data);
|
||||
}
|
||||
}, checkInterval);
|
||||
}
|
||||
|
||||
async activateCliMode() {
|
||||
|
@ -125,13 +234,17 @@ class AutoBackup {
|
|||
}
|
||||
|
||||
execute(callback) {
|
||||
// Reset state at the beginning of a new run
|
||||
this.outputHistory = "";
|
||||
this.callback = callback;
|
||||
this.cleanupListeners();
|
||||
|
||||
const port = PortHandler.portPicker.selectedPort;
|
||||
const baud = PortHandler.portPicker.selectedBauds;
|
||||
|
||||
if (port.startsWith("serial")) {
|
||||
serial.addEventListener("connect", this.handleConnect.bind(this), { once: true });
|
||||
this.boundHandleConnect = this.handleConnect.bind(this);
|
||||
serial.addEventListener("connect", this.boundHandleConnect, { once: true });
|
||||
serial.connect(port, { baudRate: baud });
|
||||
} else {
|
||||
gui_log(i18n.getMessage("firmwareFlasherNoPortSelected"));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue