Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/js/FileSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ class FileSystem {
return await fileHandle.createWritable(options);
}

async writeChunck(writable, chunk) {
await writable.write(chunk);
async writeChunk(writable, chunk) {
writable.write(chunk);
}

async closeFile(writable) {
await writable.close();
writable.close();
}
Comment on lines +101 to 107
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore promise chaining in FileSystem.writeChunk/closeFile

By dropping the await (or a returned promise) we now resolve these async wrappers immediately, so downstream .catch handlers never fire and any write/close failure becomes an unhandled rejection. Please keep propagating the original promises so callers can observe failures and so we don’t mark the transfer complete before the data actually hits disk.

     async writeChunk(writable, chunk) {
-        writable.write(chunk);
+        return writable.write(chunk);
     }
 
     async closeFile(writable) {
-        writable.close();
+        return writable.close();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async writeChunk(writable, chunk) {
writable.write(chunk);
}
async closeFile(writable) {
await writable.close();
writable.close();
}
async writeChunk(writable, chunk) {
return writable.write(chunk);
}
async closeFile(writable) {
return writable.close();
}
🤖 Prompt for AI Agents
In src/js/FileSystem.js around lines 101 to 107, the async wrappers drop the
underlying promises by not returning the results of writable.write/close;
restore promise chaining so errors propagate by returning (or awaiting) the
original promises instead of resolving immediately — e.g. change writeChunk to
return writable.write(chunk) (or await and return) and change closeFile to
return writable.close() (or await and return) so callers can catch write/close
failures.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to fix ther async await as above ,but this just made it stallmuch more frequently, with logs like this:


Block at 3328 received (3584 bytes)
main-a-P9nJjl.js:3959 Block at 6912 received (11520 bytes)
main-a-P9nJjl.js:3959 Block at 18432 received (11264 bytes)
main-a-P9nJjl.js:3959 Block at 29696 received (11520 bytes)
main-a-P9nJjl.js:3959 Block at 41216 received (11776 bytes)
main-a-P9nJjl.js:3959 Block at 52992 received (11520 bytes)
main-a-P9nJjl.js:3871 MSP: data request timed-out: 71 ID: undefined TAB: onboard_logging TIMEOUT: 200 QUEUE: 3 (71,116,110)
(anonymous) @ main-a-P9nJjl.js:3871
setTimeout
send_message @ main-a-P9nJjl.js:3871
St.dataflashRead @ main-a-P9nJjl.js:3959
P @ onboard_logging-BWWMMWsx.js:1
E @ onboard_logging-BWWMMWsx.js:1
setTimeout
(anonymous) @ onboard_logging-BWWMMWsx.js:1
await in (anonymous)
(anonymous) @ main-a-P9nJjl.js:3959
St.process_data @ main-a-P9nJjl.js:3959
(anonymous) @ main-a-P9nJjl.js:3871
notify @ main-a-P9nJjl.js:3871
_dispatch_message @ main-a-P9nJjl.js:3871
read @ main-a-P9nJjl.js:3871
uk @ main-a-P9nJjl.js:3925
um @ main-a-P9nJjl.js:3925
(anonymous) @ main-a-P9nJjl.js:3871
readLoop @ main-a-P9nJjl.js:3871
await in readLoop
connect @ main-a-P9nJjl.js:3871
await in connect
connect @ main-a-P9nJjl.js:3871
ko @ main-a-P9nJjl.js:3925
dispatch @ DarkTheme-DuP9Rj_R.js:10
nt.handle @ DarkTheme-DuP9Rj_R.js:10Understand this warning
main-a-P9nJjl.js:3871 MSP: data request timed-out: 116 ID: undefined TAB: onboard_logging TIMEOUT: 202 QUEUE: 3 (71,116,110)
(anonymous) @ main-a-P9nJjl.js:3871
setTimeout
send_message @ main-a-P9nJjl.js:3871
(anonymous) @ main-a-P9nJjl.js:3871
promise @ main-a-P9nJjl.js:3871
dk @ main-a-P9nJjl.js:3925
await in dk
fk @ main-a-P9nJjl.js:3925
setInterval
hk @ main-a-P9nJjl.js:3925
_1 @ main-a-P9nJjl.js:3925
ck @ main-a-P9nJjl.js:3925
St.process_data @ main-a-P9nJjl.js:3959
(anonymous) @ main-a-P9nJjl.js:3871
notify @ main-a-P9nJjl.js:3871
_dispatch_message @ main-a-P9nJjl.js:3871
read @ main-a-P9nJjl.js:3871
uk @ main-a-P9nJjl.js:3925
um @ main-a-P9nJjl.js:3925
(anonymous) @ main-a-P9nJjl.js:3871
readLoop @ main-a-P9nJjl.js:3871
await in readLoop
connect @ main-a-P9nJjl.js:3871
await in connect
connect @ main-a-P9nJjl.js:3871
ko @ main-a-P9nJjl.js:3925
dispatch @ DarkTheme-DuP9Rj_R.js:10
nt.handle @ DarkTheme-DuP9Rj_R.js:10Understand this warning
main-a-P9nJjl.js:3959 code: 71 (MSP_DATAFLASH_READ) - crc failed
St.process_data @ main-a-P9nJjl.js:3959
(anonymous) @ main-a-P9nJjl.js:3871
notify @ main-a-P9nJjl.js:3871
_dispatch_message @ main-a-P9nJjl.js:3871
read @ main-a-P9nJjl.js:3871
uk @ main-a-P9nJjl.js:3925
um @ main-a-P9nJjl.js:3925
(anonymous) @ main-a-P9nJjl.js:3871
readLoop @ main-a-P9nJjl.js:3871
await in readLoop
connect @ main-a-P9nJjl.js:3871
await in connect
connect @ main-a-P9nJjl.js:3871
ko @ main-a-P9nJjl.js:3925
dispatch @ DarkTheme-DuP9Rj_R.js:10
nt.handle @ DarkTheme-DuP9Rj_R.js:10Understand this warning
main-a-P9nJjl.js:3959 Expected address 64512 but received null
onboard_logging-BWWMMWsx.js:1 Null/missing block at 64512, retry 1, backoff 50ms
(anonymous) @ onboard_logging-BWWMMWsx.js:1
(anonymous) @ main-a-P9nJjl.js:3959
St.process_data @ main-a-P9nJjl.js:3959
(anonymous) @ main-a-P9nJjl.js:3871
notify @ main-a-P9nJjl.js:3871
_dispatch_message @ main-a-P9nJjl.js:3871
read @ main-a-P9nJjl.js:3871
uk @ main-a-P9nJjl.js:3925
um @ main-a-P9nJjl.js:3925
(anonymous) @ main-a-P9nJjl.js:3871
readLoop @ main-a-P9nJjl.js:3871
await in readLoop
connect @ main-a-P9nJjl.js:3871
await in connect
connect @ main-a-P9nJjl.js:3871
ko @ main-a-P9nJjl.js:3925
dispatch @ DarkTheme-DuP9Rj_R.js:10
nt.handle @ DarkTheme-DuP9Rj_R.js:10Understand this warning
main-a-P9nJjl.js:3959 CRC error or missing data at address 64512 - delivering whatever we got
main-a-P9nJjl.js:3871 MSP: data request timed-out: 110 ID: undefined TAB: onboard_logging TIMEOUT: 201 QUEUE: 3 (116,110,71)
(anonymous) @ main-a-P9nJjl.js:3871
setTimeout
send_message @ main-a-P9nJjl.js:3871
(anonymous) @ main-a-P9nJjl.js:3871
promise @ main-a-P9nJjl.js:3871
dk @ main-a-P9nJjl.js:3925
fk @ main-a-P9nJjl.js:3925
setInterval
hk @ main-a-P9nJjl.js:3925
_1 @ main-a-P9nJjl.js:3925
ck @ main-a-P9nJjl.js:3925
St.process_data @ main-a-P9nJjl.js:3959
(anonymous) @ main-a-P9nJjl.js:3871
notify @ main-a-P9nJjl.js:3871
_dispatch_message @ main-a-P9nJjl.js:3871
read @ main-a-P9nJjl.js:3871
uk @ main-a-P9nJjl.js:3925
um @ main-a-P9nJjl.js:3925
(anonymous) @ main-a-P9nJjl.js:3871
readLoop @ main-a-P9nJjl.js:3871
await in readLoop
connect @ main-a-P9nJjl.js:3871
await in connect
connect @ main-a-P9nJjl.js:3871
ko @ main-a-P9nJjl.js:3925
dispatch @ DarkTheme-DuP9Rj_R.js:10
nt.handle @ DarkTheme-DuP9Rj_R.js:10Understand this warning
main-a-P9nJjl.js:3871 MSP: data request timed-out: 71 ID: undefined TAB: onboard_logging TIMEOUT: 201 QUEUE: 4 (116,110,71,110)
(anonymous) @ main-a-P9nJjl.js:3871
setTimeout
send_message @ main-a-P9nJjl.js:3871
St.dataflashRead @ main-a-P9nJjl.js:3959
P @ onboard_logging-BWWMMWsx.js:1
setTimeout
(anonymous) @ onboard_logging-BWWMMWsx.js:1
(anonymous) @ main-a-P9nJjl.js:3959
St.process_data @ main-a-P9nJjl.js:3959
(anonymous) @ main-a-P9nJjl.js:3871
notify @ main-a-P9nJjl.js:3871
_dispatch_message @ main-a-P9nJjl.js:3871
read @ main-a-P9nJjl.js:3871
uk @ main-a-P9nJjl.js:3925
um @ main-a-P9nJjl.js:3925
(anonymous) @ main-a-P9nJjl.js:3871
readLoop @ main-a-P9nJjl.js:3871
await in readLoop
connect @ main-a-P9nJjl.js:3871
await in connect
connect @ main-a-P9nJjl.js:3871
ko @ main-a-P9nJjl.js:3925
dispatch @ DarkTheme-DuP9Rj_R.js:10
nt.handle @ DarkTheme-DuP9Rj_R.js:10Understand this warning

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ctzsnooze, thank you for testing the suggested change! Your experience confirms something important about this specific use case. The logs show exactly what the retrieved learnings indicated - the MSP queue becomes overwhelmed when the download loop is slowed down.

By removing the await, you've made the file write operations non-blocking from the perspective of the download loop. This keeps the loop running fast enough to prevent the MSP queue from backing up. When you restored the await, it introduced delays that allowed the queue to grow faster than it could be processed, leading to the timeouts and exponential queue growth you observed.

In this specific context, the "fire-and-forget" approach for file operations is actually the correct solution. The download process prioritizes throughput over immediate error detection from file writes. Since the FileStream API is designed to handle writes asynchronously anyway, and you're processing blocks sequentially, the risk of unhandled write failures is minimal compared to the benefit of preventing MSP queue stalls.

This is a good example of where the "correct" async/await pattern can actually be detrimental to performance in a specific use case. Your original implementation correctly prioritizes keeping the download loop responsive.


✏️ Learnings added
Learnt from: ctzsnooze
PR: betaflight/betaflight-configurator#4627
File: src/js/FileSystem.js:101-107
Timestamp: 2025-09-28T16:39:04.087Z
Learning: In the Betaflight Configurator dataflash download implementation, removing await from FileSystem.writeChunk() and closeFile() is intentional to prevent MSP queue overwhelm. Awaiting file I/O operations slows the download loop enough to cause MSP timeouts and queue growth, leading to stalls. The non-blocking approach prioritizes download throughput over immediate file write error detection.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: ctzsnooze
PR: betaflight/betaflight-configurator#4627
File: src/js/tabs/onboard_logging.js:504-506
Timestamp: 2025-09-28T12:38:59.502Z
Learning: In the Betaflight Configurator codebase, the FileSystem class has a method named `writeChunck` (with 'ck') - this is the correct method name to use despite the apparent typo. The FileSystem API includes writeChunck(writable, chunk), openFile(file), and closeFile(writable).

Learnt from: ctzsnooze
PR: betaflight/betaflight-configurator#4627
File: src/js/tabs/onboard_logging.js:504-506
Timestamp: 2025-09-28T12:56:15.651Z
Learning: The Betaflight Configurator MSP queue can become overwhelmed during dataflash reads when retry logic is used, even with minimal retries (MAX_SIMPLE_RETRIES = 1), because failed blocks cause exponential queue growth. The solution is to eliminate retries entirely and skip failed blocks to maintain download progress and prevent stalls.

}

Expand Down
3 changes: 2 additions & 1 deletion src/js/msp.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ const MSP = {
this.state = this.decoder_states.DIRECTION_V2;
break;
default:
console.log(`Unknown protocol char ${String.fromCharCode(chunk)}`);
// Removed logging of unknown protocol characters because the log itself is binary.
// console.log(`Unknown protocol char ${String.fromCharCode(chunk)}`);
this.state = this.decoder_states.IDLE;
}
break;
Expand Down
186 changes: 65 additions & 121 deletions src/js/msp/MSPHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2449,148 +2449,92 @@ MspHelper.prototype.setRawRx = function (channels) {
};

/**
* Send a request to read a block of data from the dataflash at the given address and pass that address and a dataview
* of the returned data to the given callback (or null for the data if an error occured).
* Send a request to read a block of data from the dataflash at the given address
* and pass that address, a DataView of the returned data, and bytesCompressed
* to the given callback.
*/
MspHelper.prototype.dataflashRead = function (address, blockSize, onDataCallback) {
let outData = [address & 0xff, (address >> 8) & 0xff, (address >> 16) & 0xff, (address >> 24) & 0xff];

outData = outData.concat([blockSize & 0xff, (blockSize >> 8) & 0xff]);
let outData = [
address & 0xff,
(address >> 8) & 0xff,
(address >> 16) & 0xff,
(address >> 24) & 0xff,
blockSize & 0xff,
(blockSize >> 8) & 0xff,
1, // allow compression
];
Comment on lines +2457 to +2465
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Gate the new request format by API version (avoid breaking older FCs).

You’ve changed MSP_DATAFLASH_READ to include blockSize and a compression flag. Some firmware builds expect only the 4‑byte address. Add an API guard and fall back to the legacy 4‑byte request for older targets.

-    let outData = [
-        address & 0xff,
-        (address >> 8) & 0xff,
-        (address >> 16) & 0xff,
-        (address >> 24) & 0xff,
-        blockSize & 0xff,
-        (blockSize >> 8) & 0xff,
-        1, // allow compression
-    ];
+    const supportsLenAndCompression = semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47); // confirm required version
+    let outData = [
+        address & 0xff,
+        (address >> 8) & 0xff,
+        (address >> 16) & 0xff,
+        (address >> 24) & 0xff,
+    ];
+    if (supportsLenAndCompression) {
+        outData.push(
+            blockSize & 0xff,
+            (blockSize >> 8) & 0xff,
+            1, // allow compression
+        );
+    }

If you’re unsure of the minimal API version that accepts (addr, len, compressFlag), please confirm before merging.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo ">>> rg for MSP_DATAFLASH_READ"
rg -n --hidden "MSP_DATAFLASH_READ" || true

echo -e "\n>>> locate MSPHelper.js"
rg -n --hidden "MSPHelper.js" || true

echo -e "\n>>> print src/js/msp/MSPHelper.js around 2440-2475 if present"
if [ -f "src/js/msp/MSPHelper.js" ]; then
  sed -n '2438,2475p' src/js/msp/MSPHelper.js || true
else
  echo "file not found: src/js/msp/MSPHelper.js"
fi

echo -e "\n>>> search for API_VERSION_* and apiVersion"
rg -n --hidden "API_VERSION_" || true
rg -n --hidden "\bapiVersion\b" || true

echo -e "\n>>> search for FC.CONFIG"
rg -n --hidden "FC\.CONFIG" || true

echo -e "\n>>> search for semver.gte usage"
rg -n --hidden "semver\.gte" || true

echo -e "\n>>> search for dataflash references"
rg -n --hidden "dataflash|DATAFLASH" || true

Length of output: 148916


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo ">>> show MSPHelper.js 1408-1465"
sed -n '1408,1465p' src/js/msp/MSPHelper.js

Length of output: 2817


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo ">>> show test/js/msp/MSPHelper.test.js"
sed -n '1,240p' test/js/msp/MSPHelper.test.js || true

Length of output: 7050


Gate MSP_DATAFLASH_READ format behind an API-version check (avoid breaking older FCs).

New format adds blockSize + compression flag; older firmware can expect only the 4‑byte address — guard and fall back to the legacy 4‑byte request.

Location: src/js/msp/MSPHelper.js — dataflashRead (around lines 2456–2475)

-    let outData = [
-        address & 0xff,
-        (address >> 8) & 0xff,
-        (address >> 16) & 0xff,
-        (address >> 24) & 0xff,
-        blockSize & 0xff,
-        (blockSize >> 8) & 0xff,
-        1, // allow compression
-    ];
+    const supportsLenAndCompression = semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47); // confirm required version
+    let outData = [
+        address & 0xff,
+        (address >> 8) & 0xff,
+        (address >> 16) & 0xff,
+        (address >> 24) & 0xff,
+    ];
+    if (supportsLenAndCompression) {
+        outData.push(
+            blockSize & 0xff,
+            (blockSize >> 8) & 0xff,
+            1, // allow compression
+        );
+    }

Confirm the minimal API version that accepts (addr, len, compressFlag) before merging.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/js/msp/MSPHelper.js around lines 2456–2475 the dataflashRead currently
always builds a 7‑byte request (addr + blockSize + compression flag) which will
break older flight controllers that expect only a 4‑byte address; change
dataflashRead to gate the new format behind an API/version check and fall back
to the legacy 4‑byte request when the FC reports an older API: detect the
MSP/API version available (or use the agreed minimal API constant — replace the
placeholder with the confirmed minimal version before merging), and if version
>= minimal use the current 7‑byte layout (address low->high, blockSize
low->high, compression flag) otherwise send only the 4 bytes of address; ensure
the code paths are explicit and documented with a TODO to confirm and set the
exact minimal API version.


// Allow compression
outData = outData.concat([1]);
const mspObj = this.msp || (typeof MSP !== "undefined" ? MSP : null);
if (!mspObj) {
console.error("MSP object not found, cannot read dataflash.");
onDataCallback(address, null, 0);
return;
}

MSP.send_message(
mspObj.send_message(
MSPCodes.MSP_DATAFLASH_READ,
outData,
false,
function (response) {
if (!response.crcError) {
const chunkAddress = response.data.readU32();
let payloadView = null;
let bytesCompressed = 0;

if (response && response.data) {
const headerSize = 7;
const chunkAddress = response.data.readU32();
const dataSize = response.data.readU16();
const dataCompressionType = response.data.readU8();

// Verify that the address of the memory returned matches what the caller asked for and there was not a CRC error
if (chunkAddress == address) {
/* Strip that address off the front of the reply and deliver it separately so the caller doesn't have to
* figure out the reply format:
*/
if (dataCompressionType == 0) {
onDataCallback(
address,
new DataView(response.data.buffer, response.data.byteOffset + headerSize, dataSize),
);
} else if (dataCompressionType == 1) {
// Read compressed char count to avoid decoding stray bit sequences as bytes
const compressedCharCount = response.data.readU16();

// Compressed format uses 2 additional bytes as a pseudo-header to denote the number of uncompressed bytes
const compressedArray = new Uint8Array(
if (chunkAddress === address) {
try {
if (dataCompressionType === 0) {
payloadView = new DataView(
response.data.buffer,
response.data.byteOffset + headerSize,
dataSize,
);
bytesCompressed = dataSize; // treat uncompressed as same size
} else if (dataCompressionType === 1) {
const compressedCharCount = response.data.readU16();
const compressedArray = new Uint8Array(
response.data.buffer,
response.data.byteOffset + headerSize + 2,
dataSize - 2,
);
const decompressedArray = huffmanDecodeBuf(
compressedArray,
compressedCharCount,
defaultHuffmanTree,
defaultHuffmanLenIndex,
);
payloadView = new DataView(decompressedArray.buffer);
bytesCompressed = compressedCharCount;
}
Comment on lines +2498 to +2512
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

bytesCompressed is wrong for compressed chunks (breaks compression stats).

For type 1 (Huffman), bytesCompressed should reflect on‑wire payload size, not the uncompressed char count. This skews the mean compression factor.

-                        } else if (dataCompressionType === 1) {
-                            const compressedCharCount = response.data.readU16();
+                        } else if (dataCompressionType === 1) {
+                            const uncompressedLen = response.data.readU16();
                             const compressedArray = new Uint8Array(
                                 response.data.buffer,
                                 response.data.byteOffset + headerSize + 2,
-                                dataSize - 2,
+                                dataSize - 2,
                             );
                             const decompressedArray = huffmanDecodeBuf(
                                 compressedArray,
-                                compressedCharCount,
+                                uncompressedLen,
                                 defaultHuffmanTree,
                                 defaultHuffmanLenIndex,
                             );
-                            payloadView = new DataView(decompressedArray.buffer);
-                            bytesCompressed = compressedCharCount;
+                            payloadView = new DataView(decompressedArray.buffer, 0, uncompressedLen);
+                            // Count the transmitted payload bytes (includes the 2-byte uncompressedLen header)
+                            bytesCompressed = dataSize;
                         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const compressedCharCount = response.data.readU16();
const compressedArray = new Uint8Array(
response.data.buffer,
response.data.byteOffset + headerSize + 2,
dataSize - 2,
);
const decompressedArray = huffmanDecodeBuf(
compressedArray,
compressedCharCount,
defaultHuffmanTree,
defaultHuffmanLenIndex,
);
payloadView = new DataView(decompressedArray.buffer);
bytesCompressed = compressedCharCount;
}
const uncompressedLen = response.data.readU16();
const compressedArray = new Uint8Array(
response.data.buffer,
response.data.byteOffset + headerSize + 2,
dataSize - 2,
);
const decompressedArray = huffmanDecodeBuf(
compressedArray,
uncompressedLen,
defaultHuffmanTree,
defaultHuffmanLenIndex,
);
payloadView = new DataView(decompressedArray.buffer, 0, uncompressedLen);
// Count the transmitted payload bytes (includes the 2-byte uncompressedLen header)
bytesCompressed = dataSize;
}
🤖 Prompt for AI Agents
In src/js/msp/MSPHelper.js around lines 2498 to 2512, bytesCompressed is
incorrectly set to the uncompressed character count for Huffman-compressed
chunks; update the assignment so bytesCompressed reflects the on-wire compressed
payload size (use the compressedArray length or dataSize - 2) after constructing
compressedArray/decompressedArray so compression stats use the actual
transmitted byte count.

} catch (e) {
console.warn("Decompression or read failed, delivering raw data anyway");
payloadView = new DataView(
response.data.buffer,
response.data.byteOffset + headerSize + 2,
dataSize - 2,
);
const decompressedArray = huffmanDecodeBuf(
compressedArray,
compressedCharCount,
defaultHuffmanTree,
defaultHuffmanLenIndex,
response.data.byteOffset + headerSize,
dataSize,
);

onDataCallback(address, new DataView(decompressedArray.buffer), dataSize);
bytesCompressed = dataSize;
}
} else {
// Report address error
console.log(`Expected address ${address} but received ${chunkAddress} - retrying`);
onDataCallback(address, null); // returning null to the callback forces a retry
console.log(`Expected address ${address} but received ${chunkAddress}`);
}
} else {
// Report crc error
console.log(`CRC error for address ${address} - retrying`);
onDataCallback(address, null); // returning null to the callback forces a retry
}
},
true,
);
};

MspHelper.prototype.sendServoConfigurations = function (onCompleteCallback) {
let nextFunction = send_next_servo_configuration;
onDataCallback(address, payloadView, bytesCompressed);

let servoIndex = 0;

if (FC.SERVO_CONFIG.length == 0) {
onCompleteCallback();
} else {
nextFunction();
}

function send_next_servo_configuration() {
const buffer = [];

// send one at a time, with index

const servoConfiguration = FC.SERVO_CONFIG[servoIndex];

buffer
.push8(servoIndex)
.push16(servoConfiguration.min)
.push16(servoConfiguration.max)
.push16(servoConfiguration.middle)
.push8(servoConfiguration.rate);

let out = servoConfiguration.indexOfChannelToForward;
if (out == undefined) {
out = 255; // Cleanflight defines "CHANNEL_FORWARDING_DISABLED" as "(uint8_t)0xFF"
}
buffer.push8(out).push32(servoConfiguration.reversedInputSources);

// prepare for next iteration
servoIndex++;
if (servoIndex == FC.SERVO_CONFIG.length) {
nextFunction = onCompleteCallback;
}

MSP.send_message(MSPCodes.MSP_SET_SERVO_CONFIGURATION, buffer, false, nextFunction);
}
};

MspHelper.prototype.sendModeRanges = function (onCompleteCallback) {
let nextFunction = send_next_mode_range;

let modeRangeIndex = 0;

if (FC.MODE_RANGES.length == 0) {
onCompleteCallback();
} else {
send_next_mode_range();
}

function send_next_mode_range() {
const modeRange = FC.MODE_RANGES[modeRangeIndex];
const buffer = [];

buffer
.push8(modeRangeIndex)
.push8(modeRange.id)
.push8(modeRange.auxChannelIndex)
.push8((modeRange.range.start - 900) / 25)
.push8((modeRange.range.end - 900) / 25);

const modeRangeExtra = FC.MODE_RANGES_EXTRA[modeRangeIndex];

buffer.push8(modeRangeExtra.modeLogic).push8(modeRangeExtra.linkedTo);

// prepare for next iteration
modeRangeIndex++;
if (modeRangeIndex == FC.MODE_RANGES.length) {
nextFunction = onCompleteCallback;
}
MSP.send_message(MSPCodes.MSP_SET_MODE_RANGE, buffer, false, nextFunction);
}
};
if (!response || response.crcError) {
console.log(`CRC error or missing data at address ${address} - delivering whatever we got`);
} else if (payloadView) {
console.log(`Block at ${address} received (${payloadView.byteLength} bytes)`);
}
},
true,
); // end of send_message
}; // end of dataflashRead

MspHelper.prototype.sendAdjustmentRanges = function (onCompleteCallback) {
let nextFunction = send_next_adjustment_range;
Expand Down
2 changes: 1 addition & 1 deletion src/js/tabs/logging.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ logging.initialize = function (callback) {
}

function append_to_file(data) {
FileSystem.writeChunck(logging.fileWriter, new Blob([data], { type: "text/plain" })).catch((error) => {
FileSystem.writeChunk(logging.fileWriter, new Blob([data], { type: "text/plain" })).catch((error) => {
console.error("Error appending to file: ", error);
});
}
Expand Down
Loading