JNP3 16/17

Dependencies:   mbed nRF24L01P

frontend/kubus.js

Committer:
Micha? ?azowik
Date:
2017-01-09
Revision:
25:4fd33808f095
Parent:
24:a929fcdb1208
Child:
26:52e8eee05dc6

File content as of revision 25:4fd33808f095:

Chart.defaults.global.defaultFontFamily = "triplicate, Courier, 'Courier New', monospace";
Chart.defaults.global.defaultFontSize = 15;
Chart.defaults.global.defaultFontColor = "#efefef";

const UPDATE_INTERVAL_SECONDS = 5;
const KEEP_DATA_POINTS = 10;

class Charts {
    constructor(containerId) {
        this.container = document.getElementById(containerId);
        this.charts = {};
    }

    addSensorData(sensors) {
        for (let sensor of sensors) {
            if (!(sensor.name in this.charts)) {
                this._addChart(SensorChartFactory.create(sensor));
            }

            this.charts[sensor.name].addDataPoint({
                x: new Date(),
                y: sensor.value
            });
        }
    }

    _addChart(chart) {
        this.charts[chart.getName()] = chart;
        this._addToContainer(chart);
    }

    _addToContainer(chart) {
        const chartContainer = this._createChartContainer();
        const title = this._createChartTitle(chart.getName());

        chartContainer.appendChild(title);
        chartContainer.appendChild(chart.getCanvas());

        this.container.appendChild(chartContainer);

        chart.render();
    }

    _createChartContainer() {
        const chartContainer = document.createElement("div");
        chartContainer.classList.add('chart');
        return chartContainer;
    }

    _createChartTitle(name) {
        const title = document.createElement("h2");
        title.innerHTML = name;
        title.classList.add('chart__title');
        return title;
    }
}

class SensorChart {
    constructor(name) {
        this.canvas = document.createElement("canvas");
        this.canvas.height = "70";
        this.name = name;
        this.dataSize = 0;
    }

    getCanvas() {
        return this.canvas;
    }

    getName() {
        return this.name;
    }

    render() {
        throw Error("Not implemented");
    }

    addDataPoint(dataPoint) {
        this.chart.data.datasets[0].data.push(dataPoint);
        this.dataSize++;

        if (this.dataSize > KEEP_DATA_POINTS) {
            this.chart.data.datasets[0].data.shift();
            this.dataSize--;
        }

        this.chart.update();
    }

    _getConfig(data) {
        return {
            type: 'line',
            data: {
                datasets: [
                    {
                        label: this.name,
                        data: data,
                        backgroundColor: "rgba(75, 192, 192, 0.1)",
                        borderColor: "rgba(75, 192, 192, 1)"
                    }
                ]
            },
            options: {
                scales: {
                    xAxes: [{
                        type: 'time',
                        position: 'bottom',
                        time: {
                            unit: 'second',
                            unitStepSize: UPDATE_INTERVAL_SECONDS
                        }
                    }],
                    yAxes: [{
                        display: true,
                        ticks: {
                            max: 100,
                            min: 0
                        },
                        scaleLabel: {
                            display: false,
                            labelString: ""
                        }
                    }]
                },
                legend: {
                    display: false
                }
            }
        }
    }
}

class NumberChart extends SensorChart {
    constructor(name, options) {
        super(name);
        this.range = options.range;
        this.unit = options.unit;
    }

    render() {
        this.chart = new Chart(this.canvas, this._getConfig([]));
    }

    _getConfig(data) {
        const config = super._getConfig(data);
        config.options.scales.yAxes[0].ticks.max = this.range[1];
        config.options.scales.yAxes[0].ticks.min = this.range[0];
        config.options.scales.yAxes[0].scaleLabel.display = true;
        config.options.scales.yAxes[0].scaleLabel.labelString = this.unit;
        return config;
    }
}

class BoolChart extends SensorChart {
    render() {
        this.chart = new Chart(this.canvas, this._getConfig([]));
    }

    _getConfig(data) {
        const config = super._getConfig(data);
        config.data.datasets[0].steppedLine = true;
        config.options.scales.yAxes[0].ticks.max = 1;
        config.options.scales.yAxes[0].ticks.min = 0;
        return config;
    }
}

class SensorChartFactory {
    static create(sensor) {
        switch (sensor.type) {
            case "number":
                return new NumberChart(sensor.name, {
                    range: sensor.range,
                    unit: sensor.unit
                });
                break;
            case "bool":
                return new BoolChart(sensor.name);
                break;
            default:
                throw Error(`Sensor type ${sensor.type} not supported`);
        }
    }
}

const sensorCharts = new Charts("charts");

function fakeUpdate() {
    sensorCharts.addSensorData([
        {
            name: "Distance at the entrance",
            type: "number",
            range: [0, 90],
            unit: "cm",
            value: Math.random() * 90,
        },
        {
            name: "Motion at the stairway",
            type: "bool",
            value: Math.random() >= 0.5
        }
    ]);

    setTimeout(fakeUpdate, UPDATE_INTERVAL_SECONDS * 1000);
}

function update() {
    fetch("/api/sensors")
        .then((response) => {
            return response.json();
        })
        .then((json) => {
            sensorCharts.addSensorData(json);
        });

    setTimeout(update, UPDATE_INTERVAL_SECONDS * 1000);
}

const query = window.location.search.substring(1);

if (query === "fake") {
    fakeUpdate();
} else {
    update();
}