mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-26 01:35:28 +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() {
|
constructor() {
|
||||||
this.outputHistory = "";
|
this.outputHistory = "";
|
||||||
this.callback = null;
|
this.callback = null;
|
||||||
|
// Store bound handler references to ensure proper removal
|
||||||
|
this.boundReadSerialAdapter = null;
|
||||||
|
this.boundHandleConnect = null;
|
||||||
|
this.boundHandleDisconnect = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnect(openInfo) {
|
handleConnect(openInfo) {
|
||||||
console.log("Connected to serial port:", openInfo);
|
console.log("Connected to serial port:", openInfo);
|
||||||
if (openInfo) {
|
if (openInfo) {
|
||||||
serial.removeEventListener("receive", this.readSerialAdapter);
|
// Ensure we have a fresh start
|
||||||
serial.addEventListener("receive", this.readSerialAdapter.bind(this));
|
this.cleanupListeners();
|
||||||
|
this.outputHistory = "";
|
||||||
|
|
||||||
|
// Store bound reference for later cleanup
|
||||||
|
this.boundReadSerialAdapter = this.readSerialAdapter.bind(this);
|
||||||
|
serial.addEventListener("receive", this.boundReadSerialAdapter);
|
||||||
|
|
||||||
this.run();
|
this.run();
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,10 +40,25 @@ class AutoBackup {
|
||||||
|
|
||||||
handleDisconnect(event) {
|
handleDisconnect(event) {
|
||||||
gui_log(i18n.getMessage(event.detail ? "serialPortClosedOk" : "serialPortClosedFail"));
|
gui_log(i18n.getMessage(event.detail ? "serialPortClosedOk" : "serialPortClosedFail"));
|
||||||
|
this.cleanupListeners();
|
||||||
|
}
|
||||||
|
|
||||||
serial.removeEventListener("receive", this.readSerialAdapter);
|
// New method to ensure all listeners are properly removed
|
||||||
serial.removeEventListener("connect", this.handleConnect);
|
cleanupListeners() {
|
||||||
serial.removeEventListener("disconnect", this.handleDisconnect);
|
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) {
|
readSerialAdapter(info) {
|
||||||
|
@ -47,7 +71,8 @@ class AutoBackup {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
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();
|
serial.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,14 +108,98 @@ class AutoBackup {
|
||||||
console.log("Running backup");
|
console.log("Running backup");
|
||||||
|
|
||||||
await this.activateCliMode();
|
await this.activateCliMode();
|
||||||
await this.sendCommand("diff all");
|
this.waitForCommandCompletion("diff all");
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(async () => {
|
waitForCommandCompletion(command) {
|
||||||
this.sendCommand("exit", this.onClose);
|
// Clear previous output
|
||||||
// remove the command from the output
|
this.outputHistory = "";
|
||||||
const data = this.outputHistory.split("\n").slice(1).join("\n");
|
|
||||||
await this.save(data);
|
// Add debug mode for troubleshooting
|
||||||
}, 1500);
|
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() {
|
async activateCliMode() {
|
||||||
|
@ -125,13 +234,17 @@ class AutoBackup {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(callback) {
|
execute(callback) {
|
||||||
|
// Reset state at the beginning of a new run
|
||||||
|
this.outputHistory = "";
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.cleanupListeners();
|
||||||
|
|
||||||
const port = PortHandler.portPicker.selectedPort;
|
const port = PortHandler.portPicker.selectedPort;
|
||||||
const baud = PortHandler.portPicker.selectedBauds;
|
const baud = PortHandler.portPicker.selectedBauds;
|
||||||
|
|
||||||
if (port.startsWith("serial")) {
|
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 });
|
serial.connect(port, { baudRate: baud });
|
||||||
} else {
|
} else {
|
||||||
gui_log(i18n.getMessage("firmwareFlasherNoPortSelected"));
|
gui_log(i18n.getMessage("firmwareFlasherNoPortSelected"));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue