diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 7efa5fbd87..364d4e4485 100755
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -845,13 +845,42 @@
"message": "Automatically loaded previous log file: $1"
},
+ "dataflashNote": {
+ "message": "Blackbox flight logs can be stored on the onboard dataflash chip if your flight controller supports one."
+ },
"dataflashButtonSaveFile": {
"message": "Save flash to file..."
},
"dataflashButtonErase": {
"message": "Erase flash"
},
-
+ "dataflashConfirmEraseTitle": {
+ "message": "Confirm dataflash erase"
+ },
+ "dataflashConfirmEraseNote": {
+ "message": "This will erase any Blackbox logs or other data contained in the dataflash which will take about 20 seconds, are you sure?"
+ },
+ "dataflashSavingTitle": {
+ "message": "Saving dataflash to file"
+ },
+ "dataflashSavingNote": {
+ "message": "Saving could take several minutes, please wait."
+ },
+ "dataflashSavingNoteAfter": {
+ "message": "Save completed! Press \"Ok\" to continue."
+ },
+ "dataflashButtonSaveCancel": {
+ "message": "Cancel"
+ },
+ "dataflashButtonSaveDismiss": {
+ "message": "Ok"
+ },
+ "dataflashButtonEraseConfirm": {
+ "message": "Yes, erase dataflash"
+ },
+ "dataflashButtonEraseCancel": {
+ "message": "Cancel"
+ },
"firmwareFlasherReleaseSummaryHead": {
"message": "Release info"
},
diff --git a/js/data_storage.js b/js/data_storage.js
index 1c4e1fbb66..9e397de8c9 100755
--- a/js/data_storage.js
+++ b/js/data_storage.js
@@ -151,6 +151,8 @@ var MISC = {
};
var DATAFLASH = {
+ ready: false,
sectors: 0,
- totalSize: 0
+ totalSize: 0,
+ usedSize: 0
};
\ No newline at end of file
diff --git a/js/msp.js b/js/msp.js
index bb03ecd55c..466266d165 100644
--- a/js/msp.js
+++ b/js/msp.js
@@ -670,14 +670,16 @@ var MSP = {
console.log('Led strip config saved');
break;
case MSP_codes.MSP_DATAFLASH_SUMMARY:
- DATAFLASH.sectors = data.getUint32(0, 1);
- DATAFLASH.totalSize = data.getUint32(4, 1);
+ DATAFLASH.ready = (data.getUint8(0) & 1) != 0;
+ DATAFLASH.sectors = data.getUint32(1, 1);
+ DATAFLASH.totalSize = data.getUint32(5, 1);
+ DATAFLASH.usedSize = data.getUint32(9, 1);
break;
case MSP_codes.MSP_DATAFLASH_READ:
// No-op, let callback handle it
break;
case MSP_codes.MSP_DATAFLASH_ERASE:
- console.log("Data flash erased");
+ console.log("Data flash erase begun...");
break;
case MSP_codes.MSP_SET_MODE_RANGE:
console.log('Mode range saved');
diff --git a/main.css b/main.css
index cc8918d537..c066167726 100755
--- a/main.css
+++ b/main.css
@@ -353,3 +353,14 @@ input[type="number"]::-webkit-inner-spin-button {
text-align: center;
font-weight: bold;
}
+
+dialog {
+ background-color: white;
+ padding: 1em;
+ height: auto;
+ margin: auto auto;
+ position: absolute;
+ width: 50%;
+ border-radius: 5px;
+ border: 1px solid silver;
+}
\ No newline at end of file
diff --git a/tabs/dataflash.css b/tabs/dataflash.css
index 669151b817..50404d5baf 100644
--- a/tabs/dataflash.css
+++ b/tabs/dataflash.css
@@ -1,12 +1,28 @@
-.tab-dataflash {
-}
-.tab-dataflash .dataflash-info dd{
-
+.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 .note {
padding: 5px;
border: 1px dashed silver;
+ margin-bottom: 8px;
}
.tab-dataflash .properties {
margin-top: 10px;
@@ -76,6 +92,88 @@
.tab-dataflash .buttons a:hover {
background-color: #dedcdc;
}
-.tab-dataflash .buttons .back {
+.tab-dataflash .buttons a.disabled {
+ cursor: default;
+ color: #999;
+ pointer-events: none;
+}
+.tab-dataflash .dataflash-progress {
display: none;
+}
+.tab-dataflash .dataflash-contents {
+ margin:9px 16px;
+
+ 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;
+}
+.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:#bcf;
+}
+.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;
+}
+.tab-dataflash dialog .buttons {
+ position:static;
+ margin-top: 2em;
+ overflow: hidden;
+ width:auto;
+}
+.tab-dataflash dialog h3 {
+ margin-bottom: 0.5em;
+}
+
+.dataflash-confirm-erase .dataflash-erase-progress {
+ height:125px;
+ display:none;
+}
+.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;
}
\ No newline at end of file
diff --git a/tabs/dataflash.html b/tabs/dataflash.html
index 8b22737104..484372cadc 100644
--- a/tabs/dataflash.html
+++ b/tabs/dataflash.html
@@ -1,14 +1,63 @@
-
Dataflash
-
- - Capacity (bytes)
-
- - Capacity (sectors)
-
-
+
+
+
+
+
+
+
+
Dataflash contents
+
+ -
+
+ Used space
+
+
+ -
+
+ Free space
+
+
+
+
\ No newline at end of file
diff --git a/tabs/dataflash.js b/tabs/dataflash.js
index 38d846d320..e2460385fb 100644
--- a/tabs/dataflash.js
+++ b/tabs/dataflash.js
@@ -2,39 +2,93 @@
TABS.dataflash = {};
TABS.dataflash.initialize = function (callback) {
- var self = this;
+ var
+ self = this,
+ saveCancelled, eraseCancelled;
if (GUI.active_tab != 'dataflash') {
GUI.active_tab = 'dataflash';
googleAnalytics.sendAppView('dataflash');
}
- var requested_properties = [],
+ var
+ requested_properties = [],
samples = 0,
requests = 0,
log_buffer = [];
if (CONFIGURATOR.connectionValid) {
MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, function() {
- $('#content').load("./tabs/dataflash.html", process_html);
+ $('#content').load("./tabs/dataflash.html", function() {
+ create_html();
+ });
});
}
- function process_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'
+ });
+ }
+
+ $(".tab-dataflash a.erase-flash, .tab-dataflash a.save-flash").toggleClass("disabled", DATAFLASH.usedSize == 0);
+ }
+
+ function create_html() {
// translate to user-selected language
localize();
- $(".tab-dataflash .dataflash-capacity").text(DATAFLASH.totalSize);
- $(".tab-dataflash .dataflash-sectors").text(DATAFLASH.sectors);
-
// UI hooks
- $('.tab-dataflash a.erase_flash').click(erase_flash);
-
- $('.tab-dataflash a.save_to_file').click(stream_flash_to_file);
+ $('.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();
+
if (callback) callback();
}
-
+
// IO related methods
function zeroPad(value, width) {
value = "" + value;
@@ -46,21 +100,60 @@ TABS.dataflash.initialize = function (callback) {
return value;
}
- function stream_flash_to_file() {
+ 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_save_begin() {
+ var
+ maxBytes = DATAFLASH.usedSize;
+
if (GUI.connected_to) {
prepare_file(function(fileWriter) {
var
nextAddress = 0;
+ show_saving_dialog();
+
function onChunkRead(chunkAddress, chunkDataView) {
// If we didn't get a zero-byte chunk (indicating end-of-file), request more
if (chunkDataView.byteLength > 0) {
- var blob = new Blob([chunkDataView]);
+ nextAddress += chunkDataView.byteLength;
+
+ $(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100);
+
+ var
+ blob = new Blob([chunkDataView]);
fileWriter.write(blob);
-
- nextAddress += chunkDataView.byteLength;
- MSP.dataflashRead(nextAddress, onChunkRead);
+
+ if (saveCancelled || nextAddress >= maxBytes) {
+ if (saveCancelled) {
+ dismiss_saving_dialog();
+ } else {
+ mark_saving_dialog_done();
+ }
+ } else {
+ MSP.dataflashRead(nextAddress, onChunkRead);
+ }
+ } else {
+ mark_saving_dialog_done();
}
}
@@ -88,41 +181,51 @@ TABS.dataflash.initialize = function (callback) {
console.log('Dataflash dump file path: ' + path);
});
- prepare_writer(fileEntry, onComplete);
- });
- }
+ fileEntry.createWriter(function (fileWriter) {
+ fileWriter.onerror = function (e) {
+ console.error(e);
- function prepare_writer(fileEntry, onComplete) {
- fileEntry.createWriter(function (fileWriter) {
- fileWriter.onerror = function (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);
-
- // stop logging if the procedure was/is still running
- };
-
- fileWriter.onwriteend = function () {
- };
-
- onComplete(fileWriter);
- }, function (e) {
- // File is not readable or does not exist!
- console.error(e);
+ });
});
}
- function erase_flash() {
- /* var dialog = $("");
-
- $("body").append(dialog);
-
- dialog[0].showModal();
-
- TODO modal dialog to confirm erase */
-
- MSP.send_message(MSP_codes.MSP_DATAFLASH_ERASE, false, false, function(data) {
-
+ function ask_to_erase_flash() {
+ eraseCancelled = false;
+ $(".dataflash-confirm-erase").removeClass('erasing');
+
+ $(".dataflash-confirm-erase")[0].showModal();
+ }
+
+ function poll_for_erase_completion() {
+ MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, function() {
+ update_html();
+ 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) {