<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Board monitor</title>
        <link rel="stylesheet" type="text/css" href="{{url_for('static', filename='pure-min.css')}}">
        <!--link rel="icon" type="image/png" href="{{url_for('static', filename='favicon-512.png')}}"-->
        <!--link rel="apple-touch-icon" href="{{url_for('static', filename='favicon-512.png')}}"-->
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="{{url_for('static', filename='plotly-2.35.2.min.js')}}" charset="utf-8"></script>
        <script src="{{url_for('static', filename='socket.io.min.js')}}" charset="utf-8"></script>
<style>
#plot_adc1, #plot_adc2 {
    width: 600px;
    height: 250px;
}
</style>
    </head>
    <body>
        <h4>Device settings</h4>
        <div>
            <label for="cb_dev_mesh_flip">
                <input type="checkbox" id="cb_dev_mesh_flip" data-dev-setting="mesh_flip"/>
                MESH_FLIP
            </label>
            <label for="cb_dev_mesh_flip_n">
                <input type="checkbox" id="cb_dev_mesh_flip_n" checked data-dev-setting="mesh_flip_n"/>
                MESH_FLIP_N
            </label>
            <label for="cb_dev_rf_term">
                <input type="checkbox" id="cb_dev_rf_term" data-dev-setting="rf_term"/>
                RF_TERM
            </label>
            <label for="cb_dev_rf_term_short">
                <input type="checkbox" id="cb_dev_rf_term_short" data-dev-setting="rf_term_short"/>
                RF_TERM_SHORT
            </label>
            <label for="sampling_phase_inp">
                Sampling phase <input type="number" id="sampling_phase_inp" min="0" max="180" step="1" value="6" data-dev-setting="sampling_phase_var"/>
            </label>
            <label for="sampling_width_inp">
                Sampling pulse width <input type="number" id="sampling_width_inp" min="2" max="180" step="1" value="12" data-dev-setting="sampling_width"/>
            </label>
            <label for="stim_offset_inp">
                Stimulus pulse offset <input type="number" id="stim_offset_inp" min="0" max="180" step="1" value="104" data-dev-setting="stimulus_offset"/>
            </label>
            <label for="oversampling_inp">
                Oversampling <input type="number" id="oversampling_inp" min="1" max="511" step="1" value="256" data-dev-setting="oversampling"/>
            </label>
        </div>

        <h4>Download data</h4>
        <div class="download">
            <div>
                <label for="dl_meta_info">Meta info: <input type="text" id="dl_meta_info"/> <a id="download-button" href="#">Download</a>
            </div>
            <div>
                <input type="radio" id="dl_meta_suffix_0" name="meta_suffix" value=""/>
                <label for="dl_meta_suffix_1">&lt;none&gt;</label> 
                <input type="radio" id="dl_meta_suffix_1" name="meta_suffix" value="_baseline1"/>
                <label for="dl_meta_suffix_1">baseline1</label> 
                <input type="radio" id="dl_meta_suffix_2" name="meta_suffix" value="_short1"/>
                <label for="dl_meta_suffix_2">short1</label> 
                <input type="radio" id="dl_meta_suffix_3" name="meta_suffix" value="_short2"/>
                <label for="dl_meta_suffix_3">short2</label> 
                <input type="radio" id="dl_meta_suffix_4" name="meta_suffix" value="_short3"/>
                <label for="dl_meta_suffix_4">short3</label> 
                <input type="radio" id="dl_meta_suffix_5" name="meta_suffix" value="_baseline2"/>
                <label for="dl_meta_suffix_5">baseline2</label> 
                <input type="radio" id="dl_meta_suffix_6" name="meta_suffix" value="_probe1a"/>
                <label for="dl_meta_suffix_6">probe1a</label> 
                <input type="radio" id="dl_meta_suffix_7" name="meta_suffix" value="_probe1b"/>
                <label for="dl_meta_suffix_7">probe1b</label> 
                <input type="radio" id="dl_meta_suffix_8" name="meta_suffix" value="_probe2a"/>
                <label for="dl_meta_suffix_8">probe2a</label> 
                <input type="radio" id="dl_meta_suffix_9" name="meta_suffix" value="_probe2b"/>
                <label for="dl_meta_suffix_9">probe2b</label> 
                <input type="radio" id="dl_meta_suffix_10" name="meta_suffix" value="_probe3a"/>
                <label for="dl_meta_suffix_10">probe3a</label> 
                <input type="radio" id="dl_meta_suffix_11" name="meta_suffix" value="_probe3b"/>
                <label for="dl_meta_suffix_11">probe3b</label> 
                <input type="radio" id="dl_meta_suffix_12" name="meta_suffix" value="_baseline3"/>
                <label for="dl_meta_suffix_12">baseline3</label> 
                <input type="radio" id="dl_meta_suffix_13" name="meta_suffix" value="_cut"/>
                <label for="dl_meta_suffix_13">cut</label> 
                <input type="radio" id="dl_meta_suffix_14" name="meta_suffix" value="_baseline4"/>
                <label for="dl_meta_suffix_14">baseline4</label> 
            </div>
        </div>

        <h4 id="adc1_heading">ADC 1</h4>
        <div id="plot_adc1"></div>
        <div class="measurement" id="meas_adc1_thr">
            <h5>Threshold</h5>

            <label for="adc1_thr_v_spin">
                Voltage <input type="number" id="adc1_thr_v_spin" class="v_spin" min="-0.3" max="3.6" step="0.01"/> V
            </label>

            <label for="adc1_thr_v_spin">
                Anchor time: <input type="number" id="adc1_thr_v_spin" class="t_spin" min="0" max="500" step="1"/> ns
            </label>

            <div class="stats" id="table_adc1">
                <table>
                    <tr><td></td><th>10</th><th>100</th><th>1000</th></tr>
                    <tr><th>Mean [ns]</th>
                        <td class="mean_10"></td>
                        <td class="mean_100"></td>
                        <td class="mean_1000"></td>
                    </tr>
                    <tr><th>Stdev [ps]</th>
                        <td class="stdev_10"></td>
                        <td class="stdev_100"></td>
                        <td class="stdev_1000"></td>
                    </tr>
                    <tr><th>Min [ns]</th>
                        <td class="min_10"></td>
                        <td class="min_100"></td>
                        <td class="min_1000"></td>
                    </tr>
                    <tr><th>Max [ns]</th>
                        <td class="max_10"></td>
                        <td class="max_100"></td>
                        <td class="max_1000"></td>
                    </tr>
                </table>
            </div>
        </div>

        <h4 id="adc2_heading">ADC 2</h4>
        <div id="plot_adc2"></div>
        <div class="measurement" id="meas_adc2_thr">
            <h5>Threshold</h5>

            <label for="adc2_thr_v_spin">
                Voltage <input type="number" id="adc2_thr_v_spin" class="v_spin" min="-0.3" max="3.6" step="0.01"/> V
            </label>

            <label for="adc2_thr_v_spin">
                Anchor time: <input type="number" id="adc2_thr_v_spin" class="t_spin" min="0" max="500" step="1"/> ns
            </label>

            <div class="stats" id="table_adc2">
                <table>
                    <tr><td></td><th>10</th><th>100</th><th>1000</th></tr>
                    <tr><th>Mean [ns]</th>
                        <td class="mean_10"></td>
                        <td class="mean_100"></td>
                        <td class="mean_1000"></td>
                    </tr>
                    <tr><th>Stdev [ps]</th>
                        <td class="stdev_10"></td>
                        <td class="stdev_100"></td>
                        <td class="stdev_1000"></td>
                    </tr>
                    <tr><th>Min [ns]</th>
                        <td class="min_10"></td>
                        <td class="min_100"></td>
                        <td class="min_1000"></td>
                    </tr>
                    <tr><th>Max [ns]</th>
                        <td class="max_10"></td>
                        <td class="max_100"></td>
                        <td class="max_1000"></td>
                    </tr>
                </table>
            </div>
        </div>

        <script>
            class ThresholdStats {
                constructor(table_div, control_div) {
                    this.table_div = table_div;
                    this.control_div = control_div;
                    this.history = [];
                    this.thr_v = null;
                    this.thr_t = null;
                }

                update(t_arr, v_arr) {
                    let crossing_t = null;
                    for (var i=1; i<t_arr.length; i++) {
                        if ((v_arr[i-1] <= this.thr_v && v_arr[i] >= this.thr_v) ||
                            (v_arr[i-1] >= this.thr_v && v_arr[i] <= this.thr_v)) {
                            let this_crossing_t = t_arr[i-1] + (t_arr[i] - t_arr[i-1]) * (this.thr_v - v_arr[i-1]) / (v_arr[i] - v_arr[i-1]);
                            if (crossing_t === null || Math.abs(crossing_t - this.thr_t) > Math.abs(this_crossing_t - this.thr_t)) {
                                crossing_t = this_crossing_t;
                            }
                        }
                    }
                    if (crossing_t != null) {
                        this.history.push(crossing_t);
                    }

                    this.update_table(10);
                    this.update_table(100);
                    this.update_table(1000);
                }

                update_table(period) {
                    if (period > this.history.length) {
                        this.table_div.querySelector(`.min_${period}`).innerHTML = '';
                        this.table_div.querySelector(`.max_${period}`).innerHTML = '';
                        this.table_div.querySelector(`.mean_${period}`).innerHTML = '';
                        this.table_div.querySelector(`.stdev_${period}`).innerHTML = '';
                        return;
                    }

                    let min = null;
                    let max = null; 
                    let mean = 0;
                    let stdev = 0;

                    for (let i=this.history.length-period; i<this.history.length; i++) {
                        if (min === null || this.history[i] < min) {
                            min = this.history[i];
                        }
                        if (max === null || this.history[i] > max) {
                            max = this.history[i];
                        }
                        mean += this.history[i];
                        stdev += this.history[i]*this.history[i];
                    }

                    mean /= period;
                    stdev = Math.sqrt(stdev/period - mean*mean);

                    this.table_div.querySelector(`.min_${period}`).innerHTML = min.toFixed(3);
                    this.table_div.querySelector(`.max_${period}`).innerHTML = max.toFixed(3);
                    this.table_div.querySelector(`.mean_${period}`).innerHTML = mean.toFixed(3);
                    this.table_div.querySelector(`.stdev_${period}`).innerHTML = (stdev * 1000).toFixed(0);
                }

                set_thr(thr_v, thr_t) {
                    this.thr_v = thr_v;
                    this.thr_t = thr_t;
                    this.control_div.querySelector('.v_spin').value = thr_v;
                    this.control_div.querySelector('.t_spin').value = thr_t;
                    this.history = [];
                }
            }

            class DataPlot {
                constructor(plot_div, table_div, meas_div, heading_div) {
                    this.lock = false;
                    this.heading = document.querySelector(heading_div);
                    this.div = document.querySelector(plot_div);
                    this.layout_settings = {
                        margin: {t: 20, l: 60, r: 20, b: 40},
                        hovermode: 'closest',
                        yaxis: {title: {text: 'V [V]'}},
                        xaxis: {title: {text: 't [ns]'}},
                        showlegend: false};
                    this.plot = Plotly.newPlot(this.div,
                        [
                            {'x': [], 'y': [], 'type': 'scatter'},
                            {'x': [], 'y': [], 'type': 'scatter'}
                        ], this.layout_settings);
                    this.stats = new ThresholdStats(document.querySelector(table_div), document.querySelector(meas_div));

                    this.div.on('plotly_click', (evt) => {
                        this.stats.set_thr(evt.points[0].y, evt.points[0].x);
                    });
                    this.adc_data = null;
                    this.var_data = null;
                }

                update_plot (in_adc, in_var) {
                    /* 168 MHz system clock, 32 delay-mapped subdivisions per system clock cycle in HRTIM */
                    if (this.lock) {
                        return;
                    }

                    this.adc_data = in_adc;
                    this.var_data = in_var;

                    const sampling_period_ns = 1000 / 168 / 32;
                    let adc_t = Array(in_adc.length);
                    let var_t = Array(2*in_adc.length);
                    let var_data = Array(2*in_adc.length);
                    for (var i=0; i<in_adc.length; i++) {
                        adc_t[i] = i * sampling_period_ns;

                        const i2 = var_t.length - 1 - i;
                        var_t[i] = i * sampling_period_ns;
                        var_t[i2] = i * sampling_period_ns;

                        var_data[i2] = in_adc[i] + Math.sqrt(in_var[i]);
                        var_data[i] = in_adc[i] - Math.sqrt(in_var[i]);
                    }

                    /* This cursed way of doing variance shading is from plotly's own docs lol.
                       https://plotly.com/python/continuous-error-bars/ */
                    this.plot = Plotly.react(this.div, [
                        {
                            'x': var_t,
                            'y': var_data,
                            'type': 'scatter',
                            'fill': 'toself',
                            'fillcolor': 'rgba(255,0,0,0.2)',
                            'line': {'color': 'rgba(255,0,0,0.0)'},
                            'hoverinfo': 'skip',
                            'showlegend': false,
                        }, {
                            'x': adc_t,
                            'y': in_adc,
                            'type': 'scatter',
                            'line': {'color': 'rgba(255,0,0,1.0)'}
                        }], this.layout_settings);

                    this.stats.update(adc_t, in_adc);
                    this.heading.style.backgroundColor = 'red';
                    window.setTimeout(() => {this.heading.style.backgroundColor = null;}, 100);
                }
            };

            adc1_plot = new DataPlot('#plot_adc1', '#table_adc1', '#meas_adc1_thr', '#adc1_heading');
            adc2_plot = new DataPlot('#plot_adc2', '#table_adc2', '#meas_adc2_thr', '#adc2_heading');

            document.querySelector('#download-button').addEventListener('click', (evt) => {
                let a = document.createElement('a');
                const meta = document.querySelector('#dl_meta_info').value;
                const suffix = document.querySelector('input[name="meta_suffix"]:checked').value;
                adc1_plot.lock = true;
                adc2_plot.lock = true;
                a.setAttribute('href', 'data:application/json;charset=utf-8,' + JSON.stringify({
                    meta: meta + suffix,
                    settings: dbg_settings(),
                    adc1: adc1_plot.adc_data,
                    var1: adc1_plot.var_data,
                    adc2: adc2_plot.adc_data,
                    var2: adc2_plot.var_data,
                }));
                adc1_plot.lock = false;
                adc2_plot.lock = false;
                a.setAttribute('download', 'adc-capture-' + (new Date).toISOString() + '.json');
                a.style.display = 'none';
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            });

            const socket = io();
            socket.on('graph_update', (arg, callback) => {
                adc1_plot.update_plot(arg['adc1'], arg['var1']);
                adc2_plot.update_plot(arg['adc2'], arg['var2']);
            });

            function dbg_settings() {
                let out = {};
                document.querySelectorAll('input[data-dev-setting]').forEach((inp) => {
                    const key = inp.getAttribute('data-dev-setting');
                    const value = inp.type == 'checkbox' ? inp.checked : inp.value;
                    out[key] = value;
                });
                return out;
            }
            
            document.querySelectorAll('input[data-dev-setting]').forEach((inp) => {
                const key = inp.getAttribute('data-dev-setting');
                inp.addEventListener('change', (evt) => {
                    const value = inp.type == 'checkbox' ? inp.checked : inp.value;
                    socket.emit('dev_settings', {[key]: value});
                });
            });
            console.log("Sending defaults");
            socket.emit('dev_settings', dbg_settings());
        </script>
    </body>
</html>
