Skip to content

Commit 7741610

Browse files
authored
Merge pull request #218 from atomgomba/feature-csv-export
CSV export feature
2 parents 0b91bc3 + 7565bf1 commit 7741610

File tree

7 files changed

+129
-4
lines changed

7 files changed

+129
-4
lines changed

gulpfile.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ function dist() {
250250
'./js/tools.js',
251251
'./js/user_settings_dialog.js',
252252
'./js/video_export_dialog.js',
253+
'./js/csv-exporter.js',
254+
'./js/webworkers/csv-export-worker.js',
253255
'./js/vendor/FileSaver.js',
254256
'./js/vendor/bootstrap.js',
255257
'./js/vendor/bootstrap.min.js',

index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ <h1>Welcome to the Enhanced Blackbox Explorer!</h1>
180180
<a class="btn btn-primary btn-video-export" data-toggle="tooltip" title="Export your vide and chart setup to file"> Export video...</a>
181181
<a class="btn btn-primary btn-workspaces-export" data-toggle="tooltip" title="Export your workspace configurations to file">
182182
Export Workspaces...</a>
183+
<a class="btn btn-primary btn-csv-export" data-toggle="tooltip" title="Export your current graphs to CSV file"> Export CSV...</a>
183184
<span class="btn btn-primary btn-file" data-toggle="tooltip" title="Open another log file, video file, exported workspace file or configuration dump file">
184185
Open log file/video
185186
<input type="file" class="file-open" accept=".bbl,.txt,.cfl,.bfl,.log,.avi,.mov,.mp4,.mpeg,.json" multiple />
@@ -2391,6 +2392,7 @@ <h4 class="modal-title">Advanced User Settings</h4>
23912392
<script src="js/vendor/FileSaver.js"></script>
23922393
<script src="js/vendor/webm-writer-0.1.1.js"></script>
23932394
<script src="js/vendor/semver.js"></script>
2395+
<script src="node_modules/lodash/lodash.min.js"></script>
23942396
<script src="js/pref_storage.js"></script>
23952397
<script src="js/tools.js"></script>
23962398
<script src="js/cache.js"></script>
@@ -2411,6 +2413,7 @@ <h4 class="modal-title">Advanced User Settings</h4>
24112413
<script src="js/graph_config_dialog.js"></script>
24122414
<script src="js/graph_spectrum.js"></script>
24132415
<script src="js/sticks.js"></script>
2416+
<script src="js/csv-exporter.js"></script>
24142417
<script src="js/gui.js"></script>
24152418
<script src="js/header_dialog.js"></script>
24162419
<script src="js/keys_dialog.js"></script>

js/csv-exporter.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"use strict";
2+
3+
/**
4+
* @typedef {object} ExportOptions
5+
* @property {string} columnDelimiter
6+
* @property {string} stringDelimiter
7+
* @property {boolean} quoteStrings
8+
*/
9+
10+
/**
11+
* @constructor
12+
* @param {FlightLog} flightLog
13+
* @param {ExportOptions} [opts={}]
14+
*/
15+
let CsvExporter = function(flightLog, opts={}) {
16+
17+
var opts = _.merge({
18+
columnDelimiter: ",",
19+
stringDelimiter: "\"",
20+
quoteStrings: true,
21+
}, opts);
22+
23+
/**
24+
* @param {function} success is a callback triggered when export is done
25+
*/
26+
function dump(success) {
27+
let frames = _(flightLog.getChunksInTimeRange(flightLog.getMinTime(), flightLog.getMaxTime()))
28+
.map(chunk => chunk.frames).value(),
29+
worker = new Worker("/js/webworkers/csv-export-worker.js");
30+
31+
worker.onmessage = event => {
32+
success(event.data);
33+
worker.terminate();
34+
};
35+
worker.postMessage({
36+
sysConfig: flightLog.getSysConfig(),
37+
fieldNames: flightLog.getMainFieldNames(),
38+
frames: frames,
39+
opts: opts,
40+
});
41+
}
42+
43+
// exposed functions
44+
return {
45+
dump: dump,
46+
};
47+
};

js/main.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,28 @@ function BlackboxLogViewer() {
861861
reader.readAsText(file);
862862
}
863863

864+
function exportCsv(file, options={}) {
865+
866+
function onSuccess(data) {
867+
console.debug("CSV export finished in", (performance.now() - startTime) / 1000, "secs");
868+
if (!data) {
869+
console.debug("Empty data, nothing to save");
870+
return;
871+
}
872+
let blob = new Blob([data], {type: 'text/csv'}),
873+
e = document.createEvent('MouseEvents'),
874+
a = document.createElement('a');
875+
a.download = file || $(".log-filename").text() + ".csv";
876+
a.href = window.URL.createObjectURL(blob);
877+
a.dataset.downloadurl = ['text/csv', a.download, a.href].join(':');
878+
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
879+
a.dispatchEvent(e);
880+
}
881+
882+
let startTime = performance.now();
883+
CsvExporter(flightLog, options).dump(onSuccess);
884+
}
885+
864886
// New workspaces feature; local storage of user configurations
865887
prefs.get('workspaceGraphConfigs', function(item) {
866888
if(item) {
@@ -1344,7 +1366,12 @@ function BlackboxLogViewer() {
13441366
saveWorkspaces();
13451367
e.preventDefault();
13461368
});
1347-
1369+
1370+
$(".btn-csv-export").click(function(e) {
1371+
setGraphState(GRAPH_STATE_PAUSED);
1372+
exportCsv();
1373+
e.preventDefault();
1374+
});
13481375

13491376
if (FlightLogVideoRenderer.isSupported()) {
13501377
$(".btn-video-export").click(function(e) {

js/webworkers/csv-export-worker.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
importScripts("/node_modules/lodash/lodash.min.js");
2+
3+
onmessage = function(event) {
4+
5+
/**
6+
* Converts `null` and other empty non-numeric values to empty string.
7+
*
8+
* @param {object} value is not a number
9+
* @returns {string}
10+
*/
11+
function normalizeEmpty(value) {
12+
return !!value ? value : "";
13+
}
14+
15+
/**
16+
* @param {array} columns
17+
* @returns {string}
18+
*/
19+
function joinColumns(columns) {
20+
return _(columns)
21+
.map(value =>
22+
_.isNumber(value)
23+
? value
24+
: stringDelim + normalizeEmpty(value) + stringDelim)
25+
.join(opts.columnDelimiter);
26+
}
27+
28+
let opts = event.data.opts,
29+
stringDelim = opts.quoteStrings
30+
? opts.stringDelimiter
31+
: "",
32+
mainFields = _([joinColumns(event.data.fieldNames)])
33+
.concat(_(event.data.frames)
34+
.flatten()
35+
.map(row => joinColumns(row))
36+
.value())
37+
.join("\n"),
38+
headers = _(event.data.sysConfig)
39+
.map((value, key) => joinColumns([key, value]))
40+
.join("\n"),
41+
result = headers + "\n" + mainFields;
42+
43+
postMessage(result);
44+
45+
};

package-lock.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
},
2323
"author": "The Betaflight open source project.",
2424
"license": "GPL-3.0",
25-
"dependencies": {},
25+
"dependencies": {
26+
"lodash": "^4.17.10"
27+
},
2628
"devDependencies": {
2729
"command-exists": "^1.2.7",
2830
"del": "^3.0.0",

0 commit comments

Comments
 (0)