Skip to content

Commit

Permalink
lestarch: final pre-look at realtime high-rate charting
Browse files Browse the repository at this point in the history
  • Loading branch information
LeStarch committed Jul 29, 2021
1 parent edd60eb commit dfe4d99
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 117 deletions.
56 changes: 31 additions & 25 deletions src/fprime_gds/flask/static/addons/chart-display/addon-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,41 @@ export let chart_wrapper_template = `
<div class="fp-flex-repeater">
<div class="row mt-2">
<div class="col-md-12">
<button class="btn btn-sm btn-secondary" v-on:click="addChart('chart-display')">
<div class="col-md-10">
<button class="btn btn-sm btn-secondary" v-on:click="addChart">
<span class="fp-chart-btn-icon">&plus;</span><span class="fp-chart-btn-text">Add Chart</span>
</button>
<button class="btn btn-sm" :class="{'btn-secondary': !this.siblings.in_sync, 'btn-success': siblings.in_sync}" v-on:click="siblings.in_sync = !siblings.in_sync">
<span class="fp-chart-btn-text">Lock Timescales</span>
</button>
</div>
<div class="col-md-2">
<button class="btn btn-sm btn-secondary float-right" v-on:click="isHelpActive = !isHelpActive">
<span class="fp-chart-btn-text">Help</span>
</button>
</div>
</div>
<component v-for="(chartInst, index) in chartInstances" v-bind:is="chartInst.type" :key="chartInst.id"
<transition name="fade">
<div v-if="isHelpActive">
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<div class="row">
<div class="col-6">
<strong>Zoom in and out</strong> by holding <strong>ALT</strong> and using mouse wheel to scroll while hovering over an axis <br/>
<strong>Zoom in</strong> by holding <strong>ALT</strong> and clicking and dragging a selection on the chart
</div>
<div class="col-6">
<strong>Pan</strong> by holding <strong>SHIFT</strong> and clicking and dragging the chart <br/>
<strong>Change size</strong> by clicking and dragging the icon at the bottom right of the chart box
</div>
<p>
<button type="button" class="close">
<span v-on:click="isHelpActive = !isHelpActive">&times;</span>
</button>
</div>
</div>
</transition>
<component v-for="(chartInst, index) in wrappers" is="chart-display" :key="chartInst.id"
:id="chartInst.id" :siblings="siblings" v-on:delete-chart="deleteChart">
</component>
</div>
Expand All @@ -28,14 +52,11 @@ export let chart_display_template = `
<button type="button" class="close ml-2">
<span v-on:click="emitDeleteChart(id)">&times;</span>
</button>
<button type="button" class="close ml-2" v-on:click="toggleCollapseChart()">
<button type="button" class="close ml-2" v-on:click="isCollapsed = !isCollapsed">
<span v-if="!isCollapsed">&minus;</span>
<span v-if="isCollapsed">&#9744;</span>
</button>
<button type="button" class="close ml-2">
<span v-if="showControlBtns" v-on:click="toggleShowHelp()">&quest;</span>
</button>
<span class="card-subtitle text-muted">{{ channelName }} </span>
<span class="card-subtitle text-muted">{{ selected }} </span>
</div>
<div class="card-body" v-bind:class="{'collapse': isCollapsed}">
Expand All @@ -52,30 +73,15 @@ export let chart_display_template = `
<div class="row justify-content-between">
<div class="col-md-4 mt-2">
<button type="button" class="btn" v-bind:class="{'btn-warning': !pause, 'btn-success': pause}"
v-on:click="toggleStreamFlow()" v-if="showControlBtns">
v-on:click="toggleStreamFlow()" v-if="chart != null">
<span v-if="!pause">&#10074;&#10074;</span>
<span v-if="pause">&#9654;</span>
</button>
<button type="button" class="btn btn-warning" v-on:click="resetZoom()" v-if="showControlBtns">
<button type="button" class="btn btn-warning" v-on:click="resetZoom()" v-if="chart != null">
Reset Zoom
</button>
</div>
<transition name="fade">
<div class="col-md-4" v-if="isHelpActive">
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<p>
<strong>Zoom in and out</strong> holding <strong>ALT</strong> and scrolling over an axis<br/>
<strong>Zoom in</strong> holding <strong>ALT</strong> and dragging a selection on the chart<br/>
<strong>Pan</strong> holding <strong>SHIFT</strong> dragging the chart<br/>
<strong>Change size</strong> by dragging bottom right of the chart box</p>
<button type="button" class="close">
<span v-on:click="dismissHelp()">&times;</span>
</button>
</div>
</div>
</transition>
</div>
<div class="row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,34 @@
*
* @author saba-ja
*/
import {generate_chart_config, realtime_config} from "./config.js";
import {generate_chart_config} from "./config.js";
import {chart_wrapper_template, chart_display_template} from "./addon-templates.js";
import { _datastore } from '../../js/datastore.js';
import {_loader} from "../../js/loader.js";
import '../../third-party/js/chart.js';
import '../../third-party/js/chartjs-adapter-luxon.min.js';
import '../../third-party/js/hammer.min.js';
import '../../third-party/js/chartjs-plugin-zoom.js';
import '../../third-party/js/chartjs-plugin-streaming.js';
import {SiblingSet} from './sibling.js';

import './vendor/chart.js';
import './vendor/chartjs-adapter-luxon.min.js';
import './vendor/hammer.min.js';
// Note: these are modified versions of the original plugin files
import './modified-vendor/chartjs-plugin-zoom.js';
import './modified-vendor/chartjs-plugin-streaming.js';


function timeToDate(time) {
let date = new Date((time.seconds * 1000) + (time.microseconds/1000));
return date;
}
















/**
* Wrapper component to allow user add multiple charts to the same page
*/
Vue.component("chart-wrapper", {
data: function () {
return {
locked: false,
counter: 0, // Auto incrementing id of each chart box
chartInstances: [], // list of chart objects
isHelpActive: true,
wrappers: [{"id": 0}],
siblings: new SiblingSet()
};
},
Expand All @@ -55,34 +42,16 @@ Vue.component("chart-wrapper", {
* Add new chart
*/
addChart(type) {
this.chartInstances.push({'id': this.counter, 'type': type});
this.wrappers.push({'id': this.counter});
this.counter += 1;
},
/**
* Remove chart with the given id
*/
deleteChart(id) {
const index = this.chartInstances.findIndex(f => f.id === id);
this.chartInstances.splice(index,1);
const index = this.wrappers.findIndex(f => f.id === id);
this.wrappers.splice(index,1);
},
/*siblingZoom(input) {
let chart = (input || {}).chart || (this.siblings || [null])[0];
if (chart == null || !this.locked) {
return;
}
const other_siblings = this.siblings.filter((sibling) => {return sibling !== chart});
for (let i = 0; i < other_siblings.length; i++) {
syncToSibling(chart, other_siblings[i]);
}
},
sync(chart) {
// Find first sibling and bail if we are on first sibling
let prime = (this.siblings || [null])[0];
if (!this.locked || prime == null || chart === prime) {
return;
}
syncToSibling(prime, chart);
}*/
}
});

Expand All @@ -91,7 +60,7 @@ Vue.component("chart-wrapper", {
*/
Vue.component("chart-display", {
template: chart_display_template,
props: ["id", "siblings", "siblingZoom", "siblingSync"],
props: ["id", "siblings"],
data: function () {
let names = Object.values(_loader.endpoints["channel-dict"].data).map((value) => {return value.full_name});

Expand All @@ -101,38 +70,27 @@ Vue.component("chart-display", {
oldSelected: null,

isCollapsed: false,
isHelpActive: false,
pause: false,

chartObj: null,
channelName: "",
showControlBtns: false,
chart: null,
};
},
methods: {
/**
* Allow user to pause the chart stream
*/
toggleStreamFlow() {
const realtimeOpts = this.chartObj.options.scales.x.realtime;
const realtimeOpts = this.chart.options.scales.x.realtime;
realtimeOpts.pause = !realtimeOpts.pause;
this.chartObj.update("none");
this.siblings.pause(!realtimeOpts.pause);
this.pause = realtimeOpts.pause;
this.siblings.pause(realtimeOpts.pause);
},
/**
* Register a new chart object
*/
registerChart() {
// If there is a chart object destroy it to reset the chart
if (this.chartObj !== null) {
_datastore.deregisterChannelConsumer(this);
this.chartObj.data.datasets.forEach((dataset) => {
dataset.data = [];
});
this.chartObj.destroy();
this.showControlBtns = false;
this.siblings.remove(this.chartObj);
}

this.destroy();
_datastore.registerChannelConsumer(this);
let config = generate_chart_config(this.selected);
config.options.plugins.zoom.zoom.onZoom = this.siblings.syncToAll;
Expand All @@ -141,54 +99,43 @@ Vue.component("chart-display", {
config.options.scales.x.realtime.onRefresh = this.siblings.sync;
this.showControlBtns = true;
try {
this.chartObj = new Chart(this.$el.querySelector("#ds-line-chart"), config);
this.chart = new Chart(this.$el.querySelector("#ds-line-chart"), config);
} catch(err) {
// Todo. This currently suppresses the following bug error
// See ChartJs bug report https://github.com/chartjs/Chart.js/issues/9368
}
this.siblings.add(this.chartObj);
this.siblings.add(this.chart);
},
/**
* Reset chart zoom back to default
*/
resetZoom() {
this.chartObj.resetZoom("none");
this.chart.resetZoom("none");
this.siblings.reset();
},

/**
* Allow user to collapse or open chart display box
*/
toggleCollapseChart() {
this.isCollapsed = !this.isCollapsed;
},

/**
* Show or remove alert box when user click on the help button
*/
toggleShowHelp() {
this.isHelpActive = !this.isHelpActive;
},

/**
* Remove alert box when user click on the close button of help alert
*/
dismissHelp () {
this.isHelpActive = false;
destroy() {
// Guard against destroying that which is destroyed
if (this.chart == null) {
return;
}
_datastore.deregisterChannelConsumer(this);
this.chart.data.datasets.forEach((dataset) => {dataset.data = [];});
this.chart.destroy();
this.siblings.remove(this.chart);
this.chart = null;
},

/**
* sending message up to the parent to remove this chart with this id
* @param {int} id of current chart instance known to the parent
*/
emitDeleteChart(id) {
if (this.chartObj) {
this.chartObj.destroy();
}
this.destroy();
this.$emit('delete-chart', id);
},

sendChannels(channels) {
if (this.selected == null || this.chartObj == null) {
if (this.selected == null || this.chart == null) {
return;
}
let name = this.selected;
Expand All @@ -201,8 +148,8 @@ Vue.component("chart-display", {
}
);

this.chartObj.data.datasets[0].data.push(...new_channels);
this.chartObj.update('quiet');
this.chart.data.datasets[0].data.push(...new_channels);
this.chart.update('quiet');
}
},
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* NOTE: this file was modified by M Starch from its original version. This added following:
*
* - setRealtimeScale()
* - plugin.updateRangeFunctions.realtime = setRealtimeScale;
*
* and is dependent on a modified chartjs-plugin-zoom.js.
*/
/*!
* chartjs-plugin-streaming v2.0.0
* https://nagix.github.io/chartjs-plugin-streaming
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* NOTE: this file was modified by M Starch from its original version. This added following:
*
* - doUpdateRange function following the pattern of doZoom to allow for plugins to modify updateRange behavior as
* in line with doZoom's dispatch to plugins.
*/

/*!
* chartjs-plugin-zoom v1.1.1
* undefined
Expand Down
2 changes: 1 addition & 1 deletion src/fprime_gds/flask/static/addons/enabled.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Add addon imports here, try used to prevent errors from crashing GDS
import "./image-display/addon.js"
import "./chart-display/chart-addon.js"
import "./chart-display/addon.js"
Empty file.
Empty file.

0 comments on commit dfe4d99

Please sign in to comment.