/*global $,MSP,MSPCodes,TABS,GUI,CONFIGURATOR,helper,mspHelper,nwdialog,SDCARD,chrome*/ 'use strict'; var sdcardTimer; TABS.onboard_logging = { }; TABS.onboard_logging.initialize = function (callback) { let saveCancelled, eraseCancelled; //Add future blackbox values here and in messages.json, the checkbox are drawn by js const blackBoxFields = [ "BLACKBOX_FEATURE_NAV_ACC", "BLACKBOX_FEATURE_NAV_POS", "BLACKBOX_FEATURE_NAV_PID", "BLACKBOX_FEATURE_MAG", "BLACKBOX_FEATURE_ACC", "BLACKBOX_FEATURE_ATTITUDE", "BLACKBOX_FEATURE_RC_DATA", "BLACKBOX_FEATURE_RC_COMMAND", "BLACKBOX_FEATURE_MOTORS", "BLACKBOX_FEATURE_GYRO_RAW", "BLACKBOX_FEATURE_GYRO_PEAKS_ROLL", "BLACKBOX_FEATURE_GYRO_PEAKS_PITCH", "BLACKBOX_FEATURE_GYRO_PEAKS_YAW", ]; if (GUI.active_tab != 'onboard_logging') { GUI.active_tab = 'onboard_logging'; googleAnalytics.sendAppView('onboard_logging'); } if (CONFIGURATOR.connectionValid) { MSP.send_message(MSPCodes.MSP_FEATURE, false, false, function() { MSP.send_message(MSPCodes.MSP_DATAFLASH_SUMMARY, false, false, function() { MSP.send_message(MSPCodes.MSP_SDCARD_SUMMARY, false, false, function() { MSP.send_message(MSPCodes.MSP2_BLACKBOX_CONFIG, false, false, load_html); }); }); }); } function gcd(a, b) { if (b == 0) return a; return gcd(b, a % b); } function save_to_eeprom() { MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, reboot); } function reboot() { GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function() { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize); }); } function reinitialize() { GUI.log(chrome.i18n.getMessage('deviceRebooting')); GUI.handleReconnect($('.tab_onboard_logging a')); } function load_html() { GUI.load("./tabs/onboard_logging.html", function() { // translate to user-selected language localize(); var dataflashPresent = DATAFLASH.totalSize > 0, blackboxSupport = false; if ((BLACKBOX.supported || DATAFLASH.supported) && bit_check(FEATURES, 19)) { blackboxSupport = true; } $(".tab-onboard_logging") .addClass("serial-supported") .toggleClass("dataflash-supported", DATAFLASH.supported) .toggleClass("dataflash-present", dataflashPresent) .toggleClass("sdcard-supported", SDCARD.supported) .toggleClass("blackbox-config-supported", BLACKBOX.supported) .toggleClass("blackbox-supported", blackboxSupport) .toggleClass("blackbox-unsupported", !blackboxSupport); if (dataflashPresent) { // UI hooks $('.tab-onboard_logging a.erase-flash').click(ask_to_erase_flash); $('.tab-onboard_logging a.erase-flash-confirm').click(flash_erase); $('.tab-onboard_logging a.erase-flash-cancel').click(flash_erase_cancel); $('.tab-onboard_logging a.save-flash').click(flash_save_begin); $('.tab-onboard_logging a.save-flash-cancel').click(flash_save_cancel); $('.tab-onboard_logging a.save-flash-dismiss').click(dismiss_saving_dialog); } $('.save-blackbox-feature').click(function () { helper.features.reset(); helper.features.fromUI($('.require-blackbox-unsupported')); helper.features.execute(save_to_eeprom); }); if (BLACKBOX.supported) { $(".tab-onboard_logging a.save-settings").click(function() { var rate = $(".blackboxRate select").val().split('/'); BLACKBOX.blackboxRateNum = parseInt(rate[0], 10); BLACKBOX.blackboxRateDenom = parseInt(rate[1], 10); BLACKBOX.blackboxDevice = parseInt($(".blackboxDevice select").val(), 10); BLACKBOX.blackboxIncludeFlags = getIncludeFlags(); helper.features.reset(); helper.features.fromUI($('.require-blackbox-supported')); helper.features.execute(function () { mspHelper.sendBlackboxConfiguration(save_to_eeprom); }); }); } //Add checkboxes for each blackbox field const blackboxFieldsDiv = $("#blackBoxFlagsDiv"); for (let i = 0; i < blackBoxFields.length; i++) { const FIELD_ID = blackBoxFields[i]; const isEnabled = (BLACKBOX.blackboxIncludeFlags & 1<') input.attr("id",FIELD_ID); input.attr("checked",isEnabled); const label = $(""); label.attr("for",FIELD_ID) const span = $(''); span.html(chrome.i18n.getMessage(FIELD_ID)) label.append(span); const checkbox = $('
') .append([ input,label ]) blackboxFieldsDiv.append(checkbox); } populateLoggingRates(); populateDevices(); update_html(); GUI.content_ready(callback); }); } function populateDevices() { var deviceSelect = $(".blackboxDevice select").empty(); deviceSelect.append(''); if (DATAFLASH.ready) { deviceSelect.append(''); } if (SDCARD.supported) { deviceSelect.append(''); } deviceSelect.val(BLACKBOX.blackboxDevice); } function populateLoggingRates() { var userRateGCD = gcd(BLACKBOX.blackboxRateNum, BLACKBOX.blackboxRateDenom), userRate = {num: BLACKBOX.blackboxRateNum / userRateGCD, denom: BLACKBOX.blackboxRateDenom / userRateGCD}; // Offer a reasonable choice of logging rates (if people want weird steps they can use CLI) var loggingRates = [ {num: 1, denom: 32}, {num: 1, denom: 16}, {num: 1, denom: 8}, {num: 1, denom: 5}, {num: 1, denom: 4}, {num: 1, denom: 3}, {num: 1, denom: 2}, {num: 2, denom: 3}, {num: 3, denom: 4}, {num: 4, denom: 5}, {num: 7, denom: 8}, {num: 1, denom: 1}, ], loggingRatesSelect = $(".blackboxRate select"); var addedCurrentValue = false; for (var i = 0; i < loggingRates.length; i++) { if (!addedCurrentValue && userRate.num / userRate.denom <= loggingRates[i].num / loggingRates[i].denom) { if (userRate.num / userRate.denom < loggingRates[i].num / loggingRates[i].denom) { loggingRatesSelect.append(''); } addedCurrentValue = true; } loggingRatesSelect.append(''); } loggingRatesSelect.val(userRate.num + '/' + userRate.denom); } function formatFilesizeKilobytes(kilobytes) { if (kilobytes < 1024) { return Math.round(kilobytes) + "kB"; } var megabytes = kilobytes / 1024, gigabytes; if (megabytes < 900) { return megabytes.toFixed(1) + "MB"; } else { gigabytes = megabytes / 1024; return gigabytes.toFixed(1) + "GB"; } } function formatFilesizeBytes(bytes) { if (bytes < 1024) { return bytes + "B"; } return formatFilesizeKilobytes(bytes / 1024); } function update_bar_width(bar, value, total, label, valuesAreKilobytes) { if (value > 0) { bar.css({ width: (value / total * 100) + "%", display: 'block' }); $("div", bar).text((label ? label + " " : "") + (valuesAreKilobytes ? formatFilesizeKilobytes(value) : formatFilesizeBytes(value))); } else { bar.css({ display: 'none' }); } } function update_html() { update_bar_width($(".tab-onboard_logging .dataflash-used"), DATAFLASH.usedSize, DATAFLASH.totalSize, "Used space", false); update_bar_width($(".tab-onboard_logging .dataflash-free"), DATAFLASH.totalSize - DATAFLASH.usedSize, DATAFLASH.totalSize, "Free space", false); update_bar_width($(".tab-onboard_logging .sdcard-other"), SDCARD.totalSizeKB - SDCARD.freeSizeKB, SDCARD.totalSizeKB, "Unavailable space", true); update_bar_width($(".tab-onboard_logging .sdcard-free"), SDCARD.freeSizeKB, SDCARD.totalSizeKB, "Free space for logs", true); $(".btn a.erase-flash, .btn a.save-flash").toggleClass("disabled", DATAFLASH.usedSize == 0); $(".tab-onboard_logging") .toggleClass("sdcard-error", SDCARD.state == MSP.SDCARD_STATE_FATAL) .toggleClass("sdcard-initializing", SDCARD.state == MSP.SDCARD_STATE_CARD_INIT || SDCARD.state == MSP.SDCARD_STATE_FS_INIT) .toggleClass("sdcard-ready", SDCARD.state == MSP.SDCARD_STATE_READY); switch (SDCARD.state) { case MSP.SDCARD_STATE_NOT_PRESENT: $(".sdcard-status").text("No card inserted"); break; case MSP.SDCARD_STATE_FATAL: $(".sdcard-status").html("Fatal error
Reboot to retry"); break; case MSP.SDCARD_STATE_READY: $(".sdcard-status").text("Card ready"); break; case MSP.SDCARD_STATE_CARD_INIT: $(".sdcard-status").text("Card starting..."); break; case MSP.SDCARD_STATE_FS_INIT: $(".sdcard-status").text("Filesystem starting..."); break; default: $(".sdcard-status").text("Unknown state " + SDCARD.state); } if (SDCARD.supported && !sdcardTimer) { // Poll for changes in SD card status sdcardTimer = setTimeout(function() { sdcardTimer = false; if (CONFIGURATOR.connectionValid) { MSP.send_message(MSPCodes.MSP_SDCARD_SUMMARY, false, false, function() { update_html(); }); } }, 2000); } } // IO related methods function zeroPad(value, width) { value = "" + value; while (value.length < width) { value = "0" + value; } return value; } function flash_save_cancel() { saveCancelled = true; } function show_saving_dialog() { $(".dataflash-saving progress").attr("value", 0); saveCancelled = false; $(".dataflash-saving").removeClass("done"); $(".dataflash-saving")[0].showModal(); } function dismiss_saving_dialog() { $(".dataflash-saving")[0].close(); } function mark_saving_dialog_done() { $(".dataflash-saving").addClass("done"); } function flash_update_summary(onDone) { MSP.send_message(MSPCodes.MSP_DATAFLASH_SUMMARY, false, false, function() { update_html(); if (onDone) { onDone(); } }); } function flash_save_begin() { if (GUI.connected_to) { // Begin by refreshing the occupied size in case it changed while the tab was open flash_update_summary(function() { const maxBytes = DATAFLASH.usedSize; prepare_file(function(filename) { const fs = require('fs'); let nextAddress = 0; show_saving_dialog(); function onChunkRead(chunkAddress, chunk) { if (chunk != null) { // Did we receive any data? if (chunk.byteLength > 0) { nextAddress += chunk.byteLength; $(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100); fs.writeFileSync(filename, new Uint8Array(chunk), { "flag": "a" }) if (saveCancelled) { dismiss_saving_dialog(); } else if (nextAddress >= maxBytes) { mark_saving_dialog_done(); }else { mspHelper.dataflashRead(nextAddress, onChunkRead); } } else { // A zero-byte block indicates end-of-file, so we're done mark_saving_dialog_done(); } } else { // There was an error with the received block (address didn't match the one we asked for), retry mspHelper.dataflashRead(nextAddress, onChunkRead); } } // Fetch the initial block mspHelper.dataflashRead(nextAddress, onChunkRead); }); }); } } function prepare_file(onComplete) { const date = new Date(); const filename = 'blackbox_log_' + date.getFullYear() + '-' + zeroPad(date.getMonth() + 1, 2) + '-' + zeroPad(date.getDate(), 2) + '_' + zeroPad(date.getHours(), 2) + zeroPad(date.getMinutes(), 2) + zeroPad(date.getSeconds(), 2) + '.TXT'; nwdialog.setContext(document); nwdialog.saveFileDialog(filename, function(file) { onComplete(file); }); } function ask_to_erase_flash() { eraseCancelled = false; $(".dataflash-confirm-erase").removeClass('erasing'); $(".dataflash-confirm-erase")[0].showModal(); } function poll_for_erase_completion() { flash_update_summary(function() { if (CONFIGURATOR.connectionValid && !eraseCancelled) { if (DATAFLASH.ready) { $(".dataflash-confirm-erase")[0].close(); } else { setTimeout(poll_for_erase_completion, 500); } } }); } function flash_erase() { $(".dataflash-confirm-erase").addClass('erasing'); MSP.send_message(MSPCodes.MSP_DATAFLASH_ERASE, false, false, poll_for_erase_completion); } function flash_erase_cancel() { eraseCancelled = true; $(".dataflash-confirm-erase")[0].close(); } function getIncludeFlags(){ let flags = 0; for (let i = 0; i < blackBoxFields.length; i++) { const FIELD_ID = blackBoxFields[i]; const checkbox = $("#"+FIELD_ID); if(checkbox.prop("checked")){ flags=flags|1<