diff options
-rwxr-xr-x | _locales/en/messages.json | 30 | ||||
-rw-r--r-- | images/icons/cf_icon_sdcard.svg | 83 | ||||
-rwxr-xr-x | js/data_storage.js | 16 | ||||
-rw-r--r-- | js/gui.js | 2 | ||||
-rw-r--r-- | js/msp.js | 47 | ||||
-rwxr-xr-x | main.html | 12 | ||||
-rw-r--r-- | main.js | 4 | ||||
-rw-r--r-- | tabs/dataflash.css | 221 | ||||
-rw-r--r-- | tabs/dataflash.html | 66 | ||||
-rw-r--r-- | tabs/dataflash.js | 288 | ||||
-rw-r--r-- | tabs/onboard_logging.css | 298 | ||||
-rw-r--r-- | tabs/onboard_logging.html | 141 | ||||
-rw-r--r-- | tabs/onboard_logging.js | 470 |
13 files changed, 1088 insertions, 590 deletions
diff --git a/_locales/en/messages.json b/_locales/en/messages.json index ff0b4b1e..77a03a0b 100755 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -103,10 +103,10 @@ "message": "CLI" }, "tabLogging": { - "message": "Logging" + "message": "Tethered Logging" }, - "tabDataflash": { - "message": "Dataflash" + "tabOnboardLogging": { + "message": "Blackbox" }, "tabAdjustments": { "message": "Adjustments" @@ -988,10 +988,30 @@ "message": "Automatically loaded previous log file: <strong>$1</strong>" }, + "blackboxNotSupported": { + "message": "Your flight controller's firmware does not support Blackbox logging." + }, + "blackboxMaybeSupported": { + "message": "Your flight controller's firmware is too old to support this tab, or the Blackbox feature is disabled on the Configuration tab." + }, + "blackboxConfiguration": { + "message": "Blackbox configuration" + }, + "blackboxButtonSave": { + "message": "Save and reboot" + }, + + "serialLoggingSupportedNote": { + "message": "You can log to an external logging device (such as an OpenLog or compatible clone) by using a serial port. Configure the port on the Ports tab." + }, + "sdcardNote": { + "message": "Flight logs can be recorded to your flight controller's onboard SD card slot." + }, + "dataflashNote": { - "message": "Blackbox flight logs can be recorded to your flight controller's onboard dataflash chip." + "message": "Flight logs can be recorded to your flight controller's onboard dataflash chip." }, - "dataflashNotSupportedNote": { + "dataflashNotPresentNote": { "message": "Your flight controller does not have a compatible dataflash chip available." }, "dataflashFirmwareUpgradeRequired": { diff --git a/images/icons/cf_icon_sdcard.svg b/images/icons/cf_icon_sdcard.svg new file mode 100644 index 00000000..c429edd8 --- /dev/null +++ b/images/icons/cf_icon_sdcard.svg @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="81.504448mm" + height="109.72666mm" + viewBox="0 0 288.79529 388.79528" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="cf_icon_sdcard.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#9e9e9e" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="1.979899" + inkscape:cx="62.562956" + inkscape:cy="45.52524" + inkscape:document-units="px" + inkscape:current-layer="layer2" + showgrid="true" + showguides="true" + inkscape:guide-bbox="true" + fit-margin-top="1.1" + fit-margin-left="1.1" + fit-margin-right="1.1" + fit-margin-bottom="1.1" + inkscape:window-width="1920" + inkscape:window-height="1076" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1"> + <inkscape:grid + type="xygrid" + id="grid3336" + originx="-168.45952" + originy="-373.45953" /> + <sodipodi:guide + position="241.54049,446.54052" + orientation="0,1" + id="guide3343" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-168.45951,-290.10742)" /> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + transform="translate(-168.45951,-290.10742)"> + <path + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m 172.85715,294.50506 240,0 40,50 0,330 -280,0 0,-230 10,0 0,-50 -10,0 z" + id="path3341" + inkscape:connector-curvature="0" /> + </g> +</svg> diff --git a/js/data_storage.js b/js/data_storage.js index 2d75ed18..b3a7f821 100755 --- a/js/data_storage.js +++ b/js/data_storage.js @@ -174,7 +174,23 @@ var _3D = { var DATAFLASH = { ready: false, + supported: false, sectors: 0, totalSize: 0, usedSize: 0 }; + +var SDCARD = { + supported: false, + state: 0, + filesystemLastError: 0, + freeSizeKB: 0, + totalSizeKB: 0, +}; + +var BLACKBOX = { + supported: false, + blackboxDevice: 0, + blackboxRateNum: 1, + blackboxRateDenom: 1 +}; @@ -25,7 +25,7 @@ var GUI_control = function () { 'gps', 'led_strip', 'logging', - 'dataflash', + 'onboard_logging', 'modes', 'motors', 'pid_tuning', @@ -29,6 +29,10 @@ var MSP_codes = { MSP_DATAFLASH_ERASE: 72, MSP_LOOP_TIME: 73, MSP_SET_LOOP_TIME: 74, + + MSP_SDCARD_SUMMARY: 79, + MSP_BLACKBOX_CONFIG: 80, + MSP_SET_BLACKBOX_CONFIG: 81, // Multiwii MSP commands MSP_IDENT: 100, @@ -811,13 +815,17 @@ var MSP = { break; case MSP_codes.MSP_DATAFLASH_SUMMARY: if (data.byteLength >= 13) { - DATAFLASH.ready = (data.getUint8(0) & 1) != 0; + var + flags = data.getUint8(0); + DATAFLASH.ready = (flags & 1) != 0; + DATAFLASH.supported = (flags & 2) != 0 || DATAFLASH.ready; DATAFLASH.sectors = data.getUint32(1, 1); DATAFLASH.totalSize = data.getUint32(5, 1); DATAFLASH.usedSize = data.getUint32(9, 1); } else { // Firmware version too old to support MSP_DATAFLASH_SUMMARY DATAFLASH.ready = false; + DATAFLASH.supported = false; DATAFLASH.sectors = 0; DATAFLASH.totalSize = 0; DATAFLASH.usedSize = 0; @@ -829,6 +837,24 @@ var MSP = { case MSP_codes.MSP_DATAFLASH_ERASE: console.log("Data flash erase begun..."); break; + case MSP_codes.MSP_SDCARD_SUMMARY: + var flags = data.getUint8(0); + + SDCARD.supported = (flags & 0x01) != 0; + SDCARD.state = data.getUint8(1); + SDCARD.filesystemLastError = data.getUint8(2); + SDCARD.freeSizeKB = data.getUint32(3, 1); + SDCARD.totalSizeKB = data.getUint32(7, 1); + break; + case MSP_codes.MSP_BLACKBOX_CONFIG: + BLACKBOX.supported = (data.getUint8(0) & 1) != 0; + BLACKBOX.blackboxDevice = data.getUint8(1); + BLACKBOX.blackboxRateNum = data.getUint8(2); + BLACKBOX.blackboxRateDenom = data.getUint8(3); + break; + case MSP_codes.MSP_SET_BLACKBOX_CONFIG: + console.log("Blackbox config saved"); + break; case MSP_codes.MSP_SET_MODE_RANGE: console.log('Mode range saved'); break; @@ -1176,6 +1202,19 @@ MSP.setRawRx = function(channels) { MSP.send_message(MSP_codes.MSP_SET_RAW_RC, buffer, false); } +MSP.sendBlackboxConfiguration = function(onDataCallback) { + var + message = [ + BLACKBOX.blackboxDevice & 0xFF, + BLACKBOX.blackboxRateNum & 0xFF, + BLACKBOX.blackboxRateDenom & 0xFF + ]; + + MSP.send_message(MSP_codes.MSP_SET_BLACKBOX_CONFIG, message, false, function(response) { + onDataCallback(); + }); +} + /** * 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). @@ -1438,3 +1477,9 @@ MSP.serialPortFunctionsToMask = function(functions) { } return mask; } + +MSP.SDCARD_STATE_NOT_PRESENT = 0; +MSP.SDCARD_STATE_FATAL = 1; +MSP.SDCARD_STATE_CARD_INIT = 2; +MSP.SDCARD_STATE_FS_INIT = 3; +MSP.SDCARD_STATE_READY = 4; @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> @@ -21,7 +21,7 @@ <link type="text/css" rel="stylesheet" href="./tabs/sensors.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/cli.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/logging.css" media="all" /> -<link type="text/css" rel="stylesheet" href="./tabs/dataflash.css" media="all" /> +<link type="text/css" rel="stylesheet" href="./tabs/onboard_logging.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/firmware_flasher.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/adjustments.css" media="all" /> <link type="text/css" rel="stylesheet" href="./tabs/auxiliary.css" media="all" /> @@ -73,7 +73,7 @@ <script type="text/javascript" src="./tabs/sensors.js"></script> <script type="text/javascript" src="./tabs/cli.js"></script> <script type="text/javascript" src="./tabs/logging.js"></script> -<script type="text/javascript" src="./tabs/dataflash.js"></script> +<script type="text/javascript" src="./tabs/onboard_logging.js"></script> <script type="text/javascript" src="./tabs/firmware_flasher.js"></script> <title></title> </head> @@ -188,9 +188,9 @@ title="LED Strip"></a></li> <li class="tab_sensors"><a href="#" i18n="tabRawSensorData" class="tabicon ic_sensors" title="Sensors"></a></li> - <li class="tab_logging"><a href="#" i18n="tabLogging" class="tabicon ic_log" title="Logging"></a></li> - <li class="tab_dataflash"><a href="#" i18n="tabDataflash" class="tabicon ic_data" - title="Dataflash"></a></li> + <li class="tab_logging"><a href="#" i18n="tabLogging" class="tabicon ic_log" title="Tethered Logging"></a></li> + <li class="tab_onboard_logging"><a href="#" i18n="tabOnboardLogging" class="tabicon ic_data" + title="Onboard Logging"></a></li> <li class="tab_cli"><a href="#" i18n="tabCLI" class="tabicon ic_cli" title="CLI"></a></li> <!-- spare icons <li class=""><a href="#"class="tabicon ic_mission">Mission (spare icon)</a></li> @@ -157,8 +157,8 @@ $(document).ready(function () { case 'logging': TABS.logging.initialize(content_ready); break; - case 'dataflash': - TABS.dataflash.initialize(content_ready); + case 'onboard_logging': + TABS.onboard_logging.initialize(content_ready); break; case 'cli': TABS.cli.initialize(content_ready); diff --git a/tabs/dataflash.css b/tabs/dataflash.css deleted file mode 100644 index 01ebdb04..00000000 --- a/tabs/dataflash.css +++ /dev/null @@ -1,221 +0,0 @@ -.tab-dataflash .info { - margin: 0 0 10px 0; - position: relative; -} - -.tab-dataflash .info .progressLabel { - position: absolute; - width: 100%; - height: 26px; - top: 0; - left: 0; - text-align: center; - line-height: 24px; - color: white; - font-weight: bold; - /* text-shadow: 1px 0px 2px rgba(0, 0, 0, 0.9);*/ -} - -.tab-dataflash .properties { - margin-top: 10px; -} - -.tab-dataflash .dataflash-info { - overflow: hidden; -} - -.tab-dataflash .dataflash-info dt { - float: left; - width: 12em; - height: 20px; - line-height: 20px; - font-weight: bold; -} - -.tab-dataflash .dataflash-info dd { - display: block; - height: 20px; - line-height: 20px; -} - -.tab-dataflash .speed { - margin-top: 5px; - width: 80px; - border: 1px solid silver; -} - -.tab-dataflash .info { - margin-top: 10px; -} - -.tab-dataflash .info dt { - float: left; - width: 120px; - height: 20px; - line-height: 20px; - font-weight: bold; -} - -.tab-dataflash .info dd { - display: block; - margin-left: 130px; - height: 20px; - line-height: 20px; -} - -.tab-dataflash .buttons { - width: calc(100% - 20px); - position: absolute; - bottom: 10px; -} - -.tab-dataflash .dataflash-progress { - display: none; -} - -.tab-dataflash .dataflash-contents { - margin-top: 5px; - border: 1px solid silver; - background-color: #eee; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: flex-start; - border-radius: 6px; -} - -.tab-dataflash .dataflash-contents li { - height: 26px; - position: relative; - box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.20); - border-radius: 4px; -} - -.tab-dataflash .dataflash-contents li div { - position: absolute; - top: 26px; - margin-top: 4px; - text-align: center; - left: 0; - right: 0; -} - -.tab-dataflash .dataflash-used { - background-color: #59AA29; - border-radius: 4px; -} - -.tab-dataflash progress::-webkit-progress-bar { - height: 24px; - background-color: #eee; -} - -.tab-dataflash progress::-webkit-progress-value { - background-color: #bcf; -} - -.tab-dataflash dialog { - width: 40em; - border-radius: 5px; -} - -.tab-dataflash dialog .buttons { - position: static; - margin-top: 2em; - float: left; -} - -.tab-dataflash .buttons a { - margin-top: 9px; - margin-bottom: 0px; - margin-right: 10px; - background-color: #59aa29; - border-radius: 3px; - border: 1px solid #4c8829; - color: #fff; - float: left; - font-family: 'open_sansbold', Arial; - font-size: 12px; - text-shadow: 0px 1px rgba(0, 0, 0, 0.25); - display: block; - cursor: pointer; - transition: all ease 0.2s; - padding: 0px; - padding-left: 9px; - padding-right: 9px; - line-height: 28px; -} - -.tab-dataflash .buttons a:hover { - background-color: #6ac435; - border: 1px solid #4d9324; - text-shadow: 0px 1px rgba(0, 0, 0, 0.25); - color: #fff; - transition: all ease 0.2s; -} - -.tab-dataflash .buttons a:active { - background-color: #4d9324; - transition: all ease 0.0s; - box-shadow: inset 0px 1px 5px rgba(0, 0, 0, 0.35); -} - -.tab-dataflash dialog h3 { - margin-bottom: 0.5em; -} - -.dataflash-confirm-erase .dataflash-erase-progress { - height: 125px; - display: none; - border-radius: 5px; -} - -.dataflash-confirm-erase.erasing .dataflash-erase-progress { - display: block; -} - -.dataflash-confirm-erase.erasing h3, .dataflash-confirm-erase.erasing .erase-flash-confirm, .dataflash-confirm-erase.erasing .dataflash-confirm-erase-note - { - display: none; -} - -.tab-dataflash progress { - display: block; - width: 100%; - margin: 1em 0; -} - -.dataflash-saving .dataflash-saving-after { - display: none; -} - -.dataflash-saving.done .dataflash-saving-before { - display: none; -} - -.dataflash-saving.done .dataflash-saving-after { - display: block; -} - -.require-dataflash { - display: none; -} - -.tab-dataflash.supported .require-dataflash { - display: block; -} - -.require-no-dataflash { - display: block; -} - -.tab-dataflash.supported .require-no-dataflash { - display: none; -} - -@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) { - .tab-dataflash table thead tr:first-child { - font-size: 12px; - height: 22px; - } -}
\ No newline at end of file diff --git a/tabs/dataflash.html b/tabs/dataflash.html deleted file mode 100644 index eac22fb5..00000000 --- a/tabs/dataflash.html +++ /dev/null @@ -1,66 +0,0 @@ -<div class="tab-dataflash toolbar_fixed_bottom"> - <div class="content_wrapper"> - <div class="tab_title" i18n="tabDataflash"></div> - <div class="cf_doc_version_bt"> - <a id="button-documentation" href="https://github.com/cleanflight/cleanflight/releases" target="_blank"></a> - </div> - <div class="require-dataflash"> - <div class="note" style="margin-bottom: 20px;"> - <div class="note_spacer"> - <p i18n="dataflashNote"></p> - </div> - </div> - <dialog class="dataflash-confirm-erase"> - <h3 i18n="dataflashConfirmEraseTitle"></h3> - <div class="dataflash-confirm-erase-note" i18n="dataflashConfirmEraseNote"></div> - <div class="dataflash-erase-progress"> - <div class="data-loading"> - <p>Erase in progress, please wait...</p> - </div> - </div> - <div class="buttons"> - <a href="#" class="erase-flash-cancel" i18n="dataflashButtonEraseCancel"></a> <a href="#" - class="erase-flash-confirm" i18n="dataflashButtonEraseConfirm"></a> - </div> - </dialog> - <dialog class="dataflash-saving"> - <h3 i18n="dataflashSavingTitle"></h3> - <div class="dataflash-saving-before"> - <div i18n="dataflashSavingNote"></div> - <progress value="0" min="0" max="100"></progress> - <div class="buttons"> - <a href="#" class="save-flash-cancel" i18n="dataflashButtonSaveCancel"></a> - </div> - </div> - <div class="dataflash-saving-after"> - <div i18n="dataflashSavingNoteAfter"></div> - <div class="buttons"> - <a href="#" class="save-flash-dismiss" i18n="dataflashButtonSaveDismiss"></a> - </div> - </div> - </dialog> - <h3>Dataflash contents</h3> - <ul class="dataflash-contents"> - <li class="dataflash-used"> - <div class="legend">Used space</div> - </li> - <li class="dataflash-free"> - <div class="legend">Free space</div> - </li> - </ul> - </div> - <div class="require-no-dataflash note"> - <div class="note_spacer"> - <p i18n="require-no-dataflash"></p> - </div> - </div> - </div> - <div class="content_toolbar"> - <div class="btn erase_btn"> - <a class="erase-flash" href="#" i18n="dataflashButtonErase"></a> - </div> - <div class="btn save_btn"> - <a class="save-flash" href="#" i18n="dataflashButtonSaveFile"></a> - </div> - </div> -</div> diff --git a/tabs/dataflash.js b/tabs/dataflash.js deleted file mode 100644 index 60e9214e..00000000 --- a/tabs/dataflash.js +++ /dev/null @@ -1,288 +0,0 @@ -'use strict'; - -TABS.dataflash = { - available: false -}; -TABS.dataflash.initialize = function (callback) { - var - self = this, - saveCancelled, eraseCancelled; - - if (GUI.active_tab != 'dataflash') { - GUI.active_tab = 'dataflash'; - googleAnalytics.sendAppView('dataflash'); - } - - var - requested_properties = [], - samples = 0, - requests = 0, - log_buffer = []; - - if (CONFIGURATOR.connectionValid) { - TABS.dataflash.available = semver.gte(CONFIG.apiVersion, "1.6.0"); - - if (!TABS.dataflash.available) { - load_html(); - return; - } - - MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, load_html); - } - - function load_html() { - $('#content').load("./tabs/dataflash.html", function() { - create_html(); - }); - } - - function formatFilesize(bytes) { - if (bytes < 1024) { - return bytes + "B"; - } - - var kilobytes = bytes / 1024; - - if (kilobytes < 1024) { - return Math.round(kilobytes) + "kB"; - } - - var megabytes = kilobytes / 1024; - - return megabytes.toFixed(1) + "MB"; - } - - function update_html() { - if (DATAFLASH.usedSize > 0) { - $(".tab-dataflash .dataflash-used").css({ - width: (DATAFLASH.usedSize / DATAFLASH.totalSize * 100) + "%", - display: 'block' - }); - - $(".tab-dataflash .dataflash-used div").text('Used space ' + formatFilesize(DATAFLASH.usedSize)); - } else { - $(".tab-dataflash .dataflash-used").css({ - display: 'none' - }); - } - - if (DATAFLASH.totalSize - DATAFLASH.usedSize > 0) { - $(".tab-dataflash .dataflash-free").css({ - width: ((DATAFLASH.totalSize - DATAFLASH.usedSize) / DATAFLASH.totalSize * 100) + "%", - display: 'block' - }); - $(".tab-dataflash .dataflash-free div").text('Free space ' + formatFilesize(DATAFLASH.totalSize - DATAFLASH.usedSize)); - } else { - $(".tab-dataflash .dataflash-free").css({ - display: 'none' - }); - } - - $(".btn a.erase-flash, .btn a.save-flash").toggleClass("disabled", DATAFLASH.usedSize == 0); - } - - function create_html() { - - // translate to user-selected language - localize(); - - - if (TABS.dataflash.available) { - var supportsDataflash = DATAFLASH.totalSize > 0; - - $(".tab-dataflash").toggleClass("supported", supportsDataflash); - - if (supportsDataflash) { - // UI hooks - $('.tab-dataflash a.erase-flash').click(ask_to_erase_flash); - - $('.tab-dataflash a.erase-flash-confirm').click(flash_erase); - $('.tab-dataflash a.erase-flash-cancel').click(flash_erase_cancel); - - $('.tab-dataflash a.save-flash').click(flash_save_begin); - $('.tab-dataflash a.save-flash-cancel').click(flash_save_cancel); - $('.tab-dataflash a.save-flash-dismiss').click(dismiss_saving_dialog); - - update_html(); - } else { - $(".tab-dataflash .note_spacer").html(chrome.i18n.getMessage('dataflashNotSupportedNote')); - } - } else { - $(".tab-dataflash").removeClass("supported"); - $(".tab-dataflash .note").html(chrome.i18n.getMessage('dataflashFirmwareUpgradeRequired')); - } - - - GUI.content_ready(callback); - } - - // 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(MSP_codes.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() { - var - maxBytes = DATAFLASH.usedSize; - - prepare_file(function(fileWriter) { - var - nextAddress = 0; - - show_saving_dialog(); - - function onChunkRead(chunkAddress, chunkDataView) { - if (chunkDataView != null) { - // Did we receive any data? - if (chunkDataView.byteLength > 0) { - nextAddress += chunkDataView.byteLength; - - $(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100); - - var - blob = new Blob([chunkDataView]); - - fileWriter.onwriteend = function(e) { - if (saveCancelled || nextAddress >= maxBytes) { - if (saveCancelled) { - dismiss_saving_dialog(); - } else { - mark_saving_dialog_done(); - } - } else { - MSP.dataflashRead(nextAddress, onChunkRead); - } - }; - - fileWriter.write(blob); - } 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 - MSP.dataflashRead(nextAddress, onChunkRead); - } - } - - // Fetch the initial block - MSP.dataflashRead(nextAddress, onChunkRead); - }); - }); - } - } - - function prepare_file(onComplete) { - var - date = new Date(), - 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); - - chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, - accepts: [{extensions: ['TXT']}]}, function(fileEntry) { - var error = chrome.runtime.lastError; - - if (error) { - console.error(error.message); - - if (error.message != "User cancelled") { - GUI.log(chrome.i18n.getMessage('dataflashFileWriteFailed')); - } - return; - } - - // echo/console log path specified - chrome.fileSystem.getDisplayPath(fileEntry, function(path) { - console.log('Dataflash dump file path: ' + path); - }); - - fileEntry.createWriter(function (fileWriter) { - fileWriter.onerror = function (e) { - console.error(e); - - // stop logging if the procedure was/is still running - }; - - onComplete(fileWriter); - }, function (e) { - // File is not readable or does not exist! - console.error(e); - GUI.log(chrome.i18n.getMessage('dataflashFileWriteFailed')); - }); - }); - } - - 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 (!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(MSP_codes.MSP_DATAFLASH_ERASE, false, false, poll_for_erase_completion); - } - - function flash_erase_cancel() { - eraseCancelled = true; - $(".dataflash-confirm-erase")[0].close(); - } -}; - -TABS.dataflash.cleanup = function (callback) { - if (callback) callback(); -};
\ No newline at end of file diff --git a/tabs/onboard_logging.css b/tabs/onboard_logging.css new file mode 100644 index 00000000..25fa7b04 --- /dev/null +++ b/tabs/onboard_logging.css @@ -0,0 +1,298 @@ +.tab-onboard_logging .info { + margin: 0 0 10px 0; + position: relative; +} + +.tab-onboard_logging .info .progressLabel { + position: absolute; + width: 100%; + height: 26px; + top: 0; + left: 0; + text-align: center; + line-height: 24px; + color: white; + font-weight: bold; + /* text-shadow: 1px 0px 2px rgba(0, 0, 0, 0.9);*/ +} + +.tab-onboard_logging .properties { + margin-top: 10px; +} + +.tab-onboard_logging .dataflash-info { + overflow: hidden; +} + +.tab-onboard_logging .dataflash-info dt { + float: left; + width: 12em; + height: 20px; + line-height: 20px; + font-weight: bold; +} + +.tab-onboard_logging .dataflash-info dd { + display: block; + height: 20px; + line-height: 20px; +} + +.tab-onboard_logging .speed { + margin-top: 5px; + width: 80px; + border: 1px solid silver; +} + +.tab-onboard_logging .info { + margin-top: 10px; +} + +.tab-onboard_logging .info dt { + float: left; + width: 120px; + height: 20px; + line-height: 20px; + font-weight: bold; +} + +.tab-onboard_logging .info dd { + display: block; + margin-left: 130px; + height: 20px; + line-height: 20px; +} + +.tab-onboard_logging .buttons { + width: calc(100% - 20px); + position: absolute; + bottom: 10px; +} + +.tab-onboard_logging .dataflash-progress { + display: none; +} + +.tab-onboard_logging .dataflash-contents, +.tab-onboard_logging .sdcard-contents { + margin-top: 15px; + margin-bottom:26px; + border: 1px solid silver; + background-color: #eee; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-start; + border-radius: 6px; +} + +.tab-onboard_logging .dataflash-contents li, +.tab-onboard_logging .sdcard-contents li { + height: 26px; + position: relative; + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.20); + border-radius: 4px; +} + +.tab-onboard_logging .dataflash-contents li div, +.tab-onboard_logging .sdcard-contents li div { + position: absolute; + top: 26px; + margin-top: 4px; + text-align: center; + left: 0; + right: 0; + white-space: nowrap; +} + +.tab-onboard_logging .dataflash-used, +.tab-onboard_logging .sdcard-other { + background-color: #59AA29; + border-radius: 4px; +} + +.tab-onboard_logging progress::-webkit-progress-bar { + height: 24px; + background-color: #eee; +} + +.tab-onboard_logging progress::-webkit-progress-value { + background-color: #bcf; +} + +.tab-onboard_logging dialog { + width: 40em; + border-radius: 5px; +} + +.tab-onboard_logging dialog .buttons { + position: static; + margin-top: 2em; +} + +.tab-onboard_logging dialog h3 { + margin-bottom: 0.5em; +} + +.dataflash-confirm-erase .dataflash-erase-progress { + height: 125px; + display: none; + border-radius: 5px; +} + +.dataflash-confirm-erase.erasing .dataflash-erase-progress { + display: block; +} + +.dataflash-confirm-erase.erasing h3, .dataflash-confirm-erase.erasing .erase-flash-confirm, .dataflash-confirm-erase.erasing .dataflash-confirm-erase-note + { + display: none; +} + +.tab-onboard_logging progress { + display: block; + width: 100%; + margin: 1em 0; +} + +.dataflash-saving .dataflash-saving-after { + display: none; +} + +.dataflash-saving.done .dataflash-saving-before { + display: none; +} + +.dataflash-saving.done .dataflash-saving-after { + display: block; +} + +.require-dataflash-present, +.require-dataflash-supported, +.require-sdcard-ready, +.require-sdcard-supported, +.require-blackbox-supported, +.require-blackbox-maybe-supported, +.require-blackbox-unsupported, +.require-blackbox-config-supported, +.tab-onboard_logging.dataflash-present .require-dataflash-not-present, +.tab-onboard_logging.dataflash-supported .require-dataflash-unsupported, +.tab-onboard_logging.sdcard-supported .require-sdcard-unsupported, +.tab-onboard_logging.blackbox-config-supported .require-blackbox-config-unsupported { + display: none; +} + +.tab-onboard_logging.dataflash-present .require-dataflash-present, +.tab-onboard_logging.dataflash-supported .require-dataflash-supported, +.tab-onboard_logging.sdcard-ready .require-sdcard-ready, +.tab-onboard_logging.sdcard-supported .require-sdcard-supported, +.tab-onboard_logging.blackbox-supported .require-blackbox-supported, +.tab-onboard_logging.blackbox-maybe-supported .require-blackbox-maybe-supported, +.tab-onboard_logging.blackbox-unsupported .require-blackbox-unsupported, +.tab-onboard_logging.blackbox-config-supported .require-blackbox-config-supported { + display: block; +} + +.require-no-dataflash { + display: block; +} + +.tab-onboard_logging.supported .require-no-dataflash { + display: none; +} + +@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) { + .tab-onboard_logging table thead tr:first-child { + font-size: 12px; + height: 22px; + } +} + +.tab-onboard_logging .line { + clear: left; +} +.tab-onboard_logging .blackboxRate select, +.tab-onboard_logging .blackboxDevice select { + float: left; + width: 180px; + height: 20px; + margin: 0 10px 5px 0; + border: 1px solid silver; +} +.tab-onboard_logging .blackboxRate span, +.tab-onboard_logging .blackboxDevice span { + line-height: 20px; +} + +.tab-onboard_logging .sdcard { + padding:10px; + float:left; +} +.tab-onboard_logging .sdcard-status { + padding-top: 4px; + text-align: center; +} +.tab-onboard_logging.sdcard-error .sdcard-icon { + background-color: #e60000; + border: 1px solid #fe0000; +} +.tab-onboard_logging.sdcard-initializing .sdcard-icon { + background-color: #64a5f6; + border: 1px solid #68a7ff; +} +.tab-onboard_logging.sdcard-ready .sdcard-icon { + background-color: #56ac1d; + border: 1px solid #5bbb1b; +} +.tab-onboard_logging .sdcard-icon { + box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.35); + width: 90px; + height: 90px; + background-image: url(/images/icons/cf_icon_sdcard.svg); + background-position: 21px 20px; + background-size: 50px 50px; + background-repeat: no-repeat; + + background-color: #808080; + border: 1px solid #888888; + + border-radius: 45px; +} + +.tab-onboard_logging .regular-button { + margin-top: 8px; + margin-bottom: 8px; + margin-right: 10px; + background-color: #59aa29; + border-radius: 3px; + border: 1px solid #4c8829; + color: #fff; + font-family: 'open_sansbold', Arial; + font-size: 12px; + text-shadow: 0px 1px rgba(0, 0, 0, 0.25); + display: inline-block; + cursor: pointer; + transition: all ease 0.2s; + padding: 0px; + padding-left: 9px; + padding-right: 9px; + line-height: 28px; +} +.tab-onboard_logging .regular-button:hover { + background-color: #6ac435; + transition: all ease 0.2s; +} +.tab-onboard_logging .regular-button:active { + background-color: #4d9324; + transition: all ease 0.0s; + box-shadow: inset 0px 1px 5px rgba(0, 0, 0, 0.35); +} +.tab-onboard_logging .regular-button.disabled { + cursor: default; + color: #fff; + background-color: #AFAFAF; + border: 1px solid #AFAFAF; + pointer-events: none; + text-shadow: none; + opacity: 0.5; +}
\ No newline at end of file diff --git a/tabs/onboard_logging.html b/tabs/onboard_logging.html new file mode 100644 index 00000000..359f8214 --- /dev/null +++ b/tabs/onboard_logging.html @@ -0,0 +1,141 @@ +<div class="tab-onboard_logging toolbar_fixed_bottom"> + <div class="content_wrapper"> + <div class="tab_title" i18n="tabOnboardLogging"></div> + <div class="cf_doc_version_bt"> + <a id="button-documentation" href="https://github.com/cleanflight/cleanflight/releases" target="_blank"></a> + </div> + <div class="require-blackbox-unsupported note"> + <div class="note_spacer"> + <p i18n="blackboxNotSupported"></p> + </div> + </div> + <div class="require-blackbox-maybe-supported note"> + <div class="note_spacer"> + <p i18n="blackboxMaybeSupported"></p> + </div> + </div> + <div class="require-blackbox-supported"> + <div class="gui_box grey require-blackbox-config-supported"> + <div class="gui_box_titlebar"> + <div class="spacer_box_title" i18n="blackboxConfiguration"></div> + </div> + <div class="spacer_box"> + <div class="line blackboxDevice"> + <select name="blackbox_device"> + </select> + <span>Blackbox logging device</span> + </div> + <div class="line blackboxRate"> + <select name="blackbox_rate"> + </select> + <span>Portion of flight loop iterations to log (logging rate)</span> + </div> + <div class="line"> + <a href="#" class="save-settings regular-button" i18n="blackboxButtonSave"></a> + </div> + </div> + </div> + + <div class="gui_box grey"> + <div class="gui_box_titlebar" align="left"> + <div class="spacer_box_title"> + Outboard serial logging device + </div> + </div> + <div class="spacer_box"> + <p i18n="serialLoggingSupportedNote"></p> + </div> + </div> + + <div class="gui_box grey require-dataflash-supported"> + <div class="gui_box_titlebar" align="left"> + <div class="spacer_box_title"> + Onboard dataflash chip + </div> + </div> + <div class="spacer_box"> + <div class="require-dataflash-supported"> + <p i18n="dataflashNote"></p> + + <dialog class="dataflash-confirm-erase"> + <h3 i18n="dataflashConfirmEraseTitle"></h3> + <div class="dataflash-confirm-erase-note" i18n="dataflashConfirmEraseNote"></div> + <div class="dataflash-erase-progress"> + <div class="data-loading"> + <p>Erase in progress, please wait...</p> + </div> + </div> + <div class="buttons"> + <a href="#" class="erase-flash-confirm regular-button" i18n="dataflashButtonEraseConfirm"></a> + <a href="#" class="erase-flash-cancel regular-button" i18n="dataflashButtonEraseCancel"></a> + </div> + </dialog> + + <dialog class="dataflash-saving"> + <h3 i18n="dataflashSavingTitle"></h3> + <div class="dataflash-saving-before"> + <div i18n="dataflashSavingNote"></div> + <progress value="0" min="0" max="100"></progress> + <div class="buttons"> + <a href="#" class="save-flash-cancel regular-button" i18n="dataflashButtonSaveCancel"></a> + </div> + </div> + <div class="dataflash-saving-after"> + <div i18n="dataflashSavingNoteAfter"></div> + <div class="buttons"> + <a href="#" class="save-flash-dismiss regular-button" i18n="dataflashButtonSaveDismiss"></a> + </div> + </div> + </dialog> + + <ul class="dataflash-contents"> + <li class="dataflash-used"> + <div class="legend"></div> + </li> + <li class="dataflash-free"> + <div class="legend"></div> + </li> + </ul> + + <div> + <a class="regular-button erase-flash" href="#" i18n="dataflashButtonErase"></a> + <a class="regular-button save-flash" href="#" i18n="dataflashButtonSaveFile"></a> + </div> + </div> + + <p class="require-dataflash-not-present" i18n="dataflashNotPresentNote"></p> + <p class="require-dataflash-unsupported" i18n="dataflashFirmwareUpgradeRequired"></p> + </div> + </div> + <div class="require-sdcard-supported"> + <div class="gui_box grey"> + <div class="gui_box_titlebar" align="left"> + <div class="spacer_box_title"> + Onboard SD card + </div> + </div> + <div class="spacer_box"> + <div class="sdcard"> + <div class="sdcard-icon"></div> + <div class="sdcard-status"></div> + </div> + + <p i18n="sdcardNote"></p> + + <div class="require-sdcard-ready"> + <ul class="sdcard-contents"> + <li class="sdcard-other"> + <div class="legend"></div> + </li> + <li class="sdcard-free"> + <div class="legend"></div> + </li> + </ul> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</div> diff --git a/tabs/onboard_logging.js b/tabs/onboard_logging.js new file mode 100644 index 00000000..d157de0c --- /dev/null +++ b/tabs/onboard_logging.js @@ -0,0 +1,470 @@ +'use strict'; + +var + sdcardTimer; + +TABS.onboard_logging = { + available: false +}; +TABS.onboard_logging.initialize = function (callback) { + var + self = this, + saveCancelled, eraseCancelled; + + if (GUI.active_tab != 'onboard_logging') { + GUI.active_tab = 'onboard_logging'; + googleAnalytics.sendAppView('onboard_logging'); + } + + if (CONFIGURATOR.connectionValid) { + // Blackbox was introduced in 1.5.0, dataflash API was introduced in 1.8.0, BLACKBOX/SDCARD MSP APIs in 1.11.0 + TABS.onboard_logging.available = semver.gte(CONFIG.flightControllerVersion, "1.5.0"); + + if (!TABS.onboard_logging.available) { + load_html(); + return; + } + + MSP.send_message(MSP_codes.MSP_BF_CONFIG, false, false, function() { + if (semver.gte(CONFIG.flightControllerVersion, "1.8.0")) { + MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, function() { + if (semver.gte(CONFIG.flightControllerVersion, "1.11.0")) { + MSP.send_message(MSP_codes.MSP_SDCARD_SUMMARY, false, false, function() { + MSP.send_message(MSP_codes.MSP_BLACKBOX_CONFIG, false, false, load_html); + }); + } else { + load_html(); + } + }); + } else { + load_html(); + } + }); + } + + function gcd(a, b) { + if (b == 0) + return a; + + return gcd(b, a % b); + } + + function save_to_eeprom() { + MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, reboot); + } + + function reboot() { + GUI.log(chrome.i18n.getMessage('configurationEepromSaved')); + + GUI.tab_switch_cleanup(function() { + MSP.send_message(MSP_codes.MSP_SET_REBOOT, false, false, reinitialize); + }); + } + + function reinitialize() { + GUI.log(chrome.i18n.getMessage('deviceRebooting')); + + if (BOARD.find_board_definition(CONFIG.boardIdentifier).vcp) { // VCP-based flight controls may crash old drivers, we catch and reconnect + $('a.connect').click(); + GUI.timeout_add('start_connection',function start_connection() { + $('a.connect').click(); + },2000); + } else { + + GUI.timeout_add('waiting_for_bootup', function waiting_for_bootup() { + MSP.send_message(MSP_codes.MSP_IDENT, false, false, function () { + GUI.log(chrome.i18n.getMessage('deviceReady')); + TABS.onboard_logging.initialize(false, $('#content').scrollTop()); + }); + },1500); // 1500 ms seems to be just the right amount of delay to prevent data request timeouts + } + } + + function load_html() { + $('#content').load("./tabs/onboard_logging.html", function() { + // translate to user-selected language + localize(); + + var + dataflashPresent = DATAFLASH.totalSize > 0, + blackboxSupport; + + /* + * Pre-1.11.0 firmware supported DATAFLASH API (on targets with SPI flash) but not the BLACKBOX config API. + * + * The best we can do on those targets is check the BLACKBOX feature bit to identify support for Blackbox instead. + */ + if (BLACKBOX.supported || DATAFLASH.supported + || semver.gte(CONFIG.flightControllerVersion, "1.5.0") && semver.lte(CONFIG.flightControllerVersion, "1.10.0") && bit_check(BF_CONFIG.features, 19)) { + blackboxSupport = 'yes'; + } else if (semver.gte(CONFIG.flightControllerVersion, "1.5.0") && semver.lte(CONFIG.flightControllerVersion, "1.10.0")) { + blackboxSupport = 'maybe'; + } else { + blackboxSupport = 'no'; + } + + $(".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 == 'yes') + .toggleClass("blackbox-maybe-supported", blackboxSupport == 'maybe') + .toggleClass("blackbox-unsupported", blackboxSupport == 'no'); + + 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); + } + + 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); + + MSP.sendBlackboxConfiguration(save_to_eeprom); + }); + } + + populateLoggingRates(); + populateDevices(); + + update_html(); + + GUI.content_ready(callback); + }); + } + + function populateDevices() { + var + deviceSelect = $(".blackboxDevice select").empty(); + + deviceSelect.append('<option value="0">Serial port</option>'); + if (DATAFLASH.ready) { + deviceSelect.append('<option value="1">On-board dataflash chip</option>'); + } + if (SDCARD.supported) { + deviceSelect.append('<option value="2">On-board SD card slot</option>'); + } + + 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('<option value="' + userRate.num + '/' + userRate.denom + '">' + + userRate.num + '/' + userRate.denom + ' (' + Math.round(userRate.num / userRate.denom * 100) + '%)</option>'); + } + addedCurrentValue = true; + } + + loggingRatesSelect.append('<option value="' + loggingRates[i].num + '/' + loggingRates[i].denom + '">' + + loggingRates[i].num + '/' + loggingRates[i].denom + ' (' + Math.round(loggingRates[i].num / loggingRates[i].denom * 100) + '%)</option>'); + + } + 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<br>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(MSP_codes.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(MSP_codes.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() { + var + maxBytes = DATAFLASH.usedSize; + + prepare_file(function(fileWriter) { + var + nextAddress = 0; + + show_saving_dialog(); + + function onChunkRead(chunkAddress, chunkDataView) { + if (chunkDataView != null) { + // Did we receive any data? + if (chunkDataView.byteLength > 0) { + nextAddress += chunkDataView.byteLength; + + $(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100); + + var + blob = new Blob([chunkDataView]); + + fileWriter.onwriteend = function(e) { + if (saveCancelled || nextAddress >= maxBytes) { + if (saveCancelled) { + dismiss_saving_dialog(); + } else { + mark_saving_dialog_done(); + } + } else { + MSP.dataflashRead(nextAddress, onChunkRead); + } + }; + + fileWriter.write(blob); + } 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 + MSP.dataflashRead(nextAddress, onChunkRead); + } + } + + // Fetch the initial block + MSP.dataflashRead(nextAddress, onChunkRead); + }); + }); + } + } + + function prepare_file(onComplete) { + var + date = new Date(), + 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); + + chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, + accepts: [{extensions: ['TXT']}]}, function(fileEntry) { + var error = chrome.runtime.lastError; + + if (error) { + console.error(error.message); + + if (error.message != "User cancelled") { + GUI.log(chrome.i18n.getMessage('dataflashFileWriteFailed')); + } + return; + } + + // echo/console log path specified + chrome.fileSystem.getDisplayPath(fileEntry, function(path) { + console.log('Dataflash dump file path: ' + path); + }); + + fileEntry.createWriter(function (fileWriter) { + fileWriter.onerror = function (e) { + console.error(e); + + // stop logging if the procedure was/is still running + }; + + onComplete(fileWriter); + }, function (e) { + // File is not readable or does not exist! + console.error(e); + GUI.log(chrome.i18n.getMessage('dataflashFileWriteFailed')); + }); + }); + } + + 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(MSP_codes.MSP_DATAFLASH_ERASE, false, false, poll_for_erase_completion); + } + + function flash_erase_cancel() { + eraseCancelled = true; + $(".dataflash-confirm-erase")[0].close(); + } +}; + +TABS.onboard_logging.cleanup = function (callback) { + if (sdcardTimer) { + clearTimeout(sdcardTimer); + sdcardTimer = false; + } + + if (callback) { + callback(); + } +};
\ No newline at end of file |