<!--
Breath Plot: COVID-19 Respiration Analysis Software
    Copyright (C) 2020  Robert L. Read

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  -->
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

	<!-- Load plotly.js into the DOM -->
  <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>

  <script src='js/respiration_math.js'></script>


    <title>Public Invention Respiration Analysis</title>

</head>
  <style>
#calcarea {
  flex-direction:column;
  background: AliceBlue;
}
.calcnum {
    color: blue;
    font-size: xx-large;
}
.fence {
width: 3em;
}
.value_and_fences {
  display: flex;
flex-direction: row;
justify-content: space-between;
}
.limit {
  display: flex;
  flex-direction: row;
}
.limit  label {
   width: 1em;
}

  .alarmred {
    background: red;
  }

</style>

<!-- Style for toggle switch -->
  <style>
  .switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .slider {
  background-color: #2196F3;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(26px);
  -ms-transform: translateX(26px);
  transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}
</style>
  <body>

    <div class="container-fluid">

      <div class="jumbotron">
        <h1 class="display-4">VentMon Respiration Analysis</h1>
        <p class="lead">This is a work in progress of <a href="https://www.pubinv.org">Public Invention</a>. It can be attached to a data server to produce
an interactive or static analysis of a respiration. It&#39;s primary purpose is to test pandemic ventilators, but it is free software meant to be reused for other purposes.
        </p>
      </div>




      <div class="input-group mb-3">
        <div class="input-group-prepend">
          <span class="input-group-text" id="basic-addon3">PIRDS data server url:</span>
        </div>
        <input type="text" class="form-control" id="dserverurl" aria-describedby="basic-addon3">

        <div class="input-group-append">
          <a class="btn btn-outline-dark btn-sm" href="#" role="button" id="useofficial">Use Ventmon Data Lake: ventmon.coslabs.com</a>
        </div>
      </div>

      <div class="input-group mb-3">
        <div class="input-group-prepend">
          <span class="input-group-text" id="basic-addon3">Trace ID:</span>
        </div>
        <input type="text" class="form-control" id="traceid" aria-describedby="basic-addon3">
      </div>

      <div class="input-group mb-3">
        <div class="input-group-prepend">
  <span class="input-group-text" for="samples_to_plot">Number of Samples (~10s per 15000 samples):</span>
        </div>
        <input type="text" class="form-control" id="samples_to_plot" aria-describedby="samples_to_plot">
      </div>


  <label for="livetoggle">Plot Live:</label>
<label class="switch">
  <input type="checkbox" id="livetoggle" checked>
  <span class="slider round"></span>
</label>

  <div class="container-fluid">
    <div class="row">
          <div class="col-9">
            <div id='PFGraph'><!-- Pressure and Flow Graph --></div>
            <div id='EventsGraph'><!-- Events --></div>
          </div>
          <div class="col-3">
            <div class="alert alert-danger" role="alert" id="main_alert">
  Danger! Flow limits exceeded; volumes will be incorrect.
            </div>
            <div class="container" id="calcarea">
              <div>
                <label for="max">PIP (max): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="max"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="max_h">H:</label>
                      <input class="fence" id="max_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="max_l">L:</label>
                      <input class="fence" id="max_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
             </div>
             <div>
                <label for="avg">P. Mean: </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="avg"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="avg_h">H:</label>
                      <input class="fence" id="avg_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="avg_l">L:</label>
                      <input class="fence" id="avg_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
             </div>
             <div>
                <label for="min">PEEP (min): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="min"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="min_h">H:</label>
                      <input class="fence" id="min_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="min_l">L:</label>
                      <input class="fence" id="min_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
             </div>
             <div>
                <label for="mv">MVs (l/min): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="mv"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="mv_h">H:</label>
                      <input class="fence" id="mv_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="mv_l">L:</label>
                      <input class="fence" id="mv_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
                </div>
              <div>
                <label for="bpm">RR: </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="bpm"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="bmp_h">H:</label>
                      <input class="fence" id="bpm_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="bpm_l">L:</label>
                      <input class="fence" id="bpm_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
                </div>
                <div>
                  <label for="ier">I:E ratio: </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="ier"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="ier_h">H:</label>
                      <input class="fence" id="ier_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="ier_l">L:</label>
                      <input class="fence" id="ier_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
              </div>
                <div>
                <label for="tv">VTd (ml): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="tv"> </label>
                    </div>
                    <div class="vertical_alarms">
                    <div class="limit max">
                      <label for="tv_h">H:</label>
                      <input class="fence" id="tv_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="tv_l">L:</label>
                      <input class="fence" id="tv_l" type='text'> </input>
                    </div>
                    </div>
                  </div>
              </div>
                <div>
                  <label for="fio2">FiO2 Mean (%): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="fio2"> </label>
                    </div>
                    <div class="vertical_alarms">
                      <div class="limit max">
                        <label for="fio2_h">H:</label>
                        <input class="fence" id="fio2_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="fio2_l">L:</label>
                      <input class="fence" id="fio2_l" type='text'> </input>
                    </div>
                  </div>
                </div>
                <div>
                  <label for="taip">Rise Time (ms): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="taip"> </label>
                    </div>
                    <div class="vertical_alarms">
                      <div class="limit max">
                        <label for="taip_h">H:</label>
                        <input class="fence" id="taip_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="taip_l">L:</label>
                      <input class="fence" id="taip_l" type='text'> </input>
                    </div>
                  </div>
                </div>
              </div>
                <div>
                  <label for="taip">Fall Time (ms): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="trip"> </label>
                    </div>
                    <div class="vertical_alarms">
                      <div class="limit max">
                        <label for="trip_h">H:</label>
                        <input class="fence" id="trip_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="trip_l">L:</label>
                      <input class="fence" id="trip_l" type='text'> </input>
                    </div>
                  </div>
                </div>
                <div>
                  <label for="wob">W. of B. (J/L): </label>
                  <div class="value_and_fences">
                    <div class="calcnum">
                      <label id="wob"> </label>
                    </div>
                    <div class="vertical_alarms">
                      <div class="limit max">
                        <label for="wob_h">H:</label>
                        <input class="fence" id="wob_h" type='text'> </input>
                    </div>
                    <div class="limit min">
                      <label for="wob_l">L:</label>
                      <input class="fence" id="wob_l" type='text'> </input>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div> <!-- end main area -->
  </div>

  <div>
  Parameters:
  <div>
        <label for="tarip_h">Rise/Fall High Pressure (cm H2O):</label>
  <input class="fence" id="tarip_h" type='text'> </input>
  </div>
  <div>
        <label for="tarip_l">Rise/Fall Low Pressure (cm H2O):</label>
  <input class="fence" id="tarip_l" type='text'> </input>
  </div>
</div>

<div>
  <button id="import">Import Trace</button>
  <button id="export">Export Trace</button>
</div>
<div>
  <textarea id="json_trace" rows="30" cols="80"></textarea>
</div>

<p>
  This is a work in progress of <a href="https://www.pubinv.org">Public Invention</a>.

<p>
  This is a tester tool for open-source ventilators.
  It uses the <a href="https://github.com/PubInv/respiration-data-standard">PIRDS data format</a>.

<p>
  The basic operation is receive data from web server specified in the URL above.
  Probably for now that will be the VentMon Python web server that
  listens on a serial port for the VentMon device or any other
  device that streams PIRDS events.

</div>
</body>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

  <script>

var TAIP_AND_TRIP_MIN = 2;
var TAIP_AND_TRIP_MAX = 8;

function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[\[\]]/g, '\\$&');
    var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, ' '));
}

// These are defined in PIRDS.h as "constant" key words of special meaning...
var FLOW_LIMITS_EXCEEDED = false;
const FLOW_TOO_HIGH = "FLOW OUT OF RANGE HIGH";
const FLOW_TOO_LOW = "FLOW OUT OF RANGE LOW";

const SAMPLES_BETWEEN_FIO2_REPORTS = 30000;

const VENTMON_DATA_LAKE = "http://ventmon.coslabs.com";
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
var TRACE_ID = urlParams.get('i')
var DSERVER_URL = window.location.protocol + "//" + window.location.host;
var NUM_TO_READ = 500;

var DATA_RETRIEVAL_PERIOD = 50;

var RESPIRATION_RATE_WINDOW_SECONDS = 60;

var intervalID = null;


// This sould be better as time, but is easier
// to do as number of samples.
var MAX_SAMPLES_TO_STORE_S = 16000;
var samples = [];
var INITS_ONLY = true;

// This is just to get the party started!
function init_samples() {
    return [
	{
	    "event": "M",
	    "type": "P",
	    "ms": 19673310,
	    "loc": "A",
	    "num": "0",
	    "val": 10111
	},
	{
	    "event": "M",
	    "type": "D",
	    "ms": 19673310,
	    "loc": "A",
	    "num": "0",
	    "val": 4
	},
	{
	    "event": "M",
	    "type": "F",
	    "ms": 19673310,
	    "loc": "A",
	    "num": "0",
	    "val": 0
	},
	{
	    "event": "M",
	    "type": "P",
	    "ms": 19673376,
	    "loc": "A",
	    "num": "0",
	    "val": 10111
	},
	{
	    "event": "M",
	    "type": "D",
	    "ms": 19673376,
	    "loc": "A",
	    "num": "0",
	    "val": 4
	},
	{
	    "event": "M",
	    "type": "F",
	    "ms": 19673376,
	    "loc": "A",
	    "num": "0",
	    "val": 0
	},
	{
	    "event": "M",
	    "type": "P",
	    "ms": 19673442,
	    "loc": "A",
	    "num": "0",
	    "val": 10110
	},
	{
	    "event": "M",
	    "type": "D",
	    "ms": 19673442,
	    "loc": "A",
	    "num": "0",
	    "val": 3
	},
    ];
}



function unpack(rows, key) {
    return rows.map(function(row) { return row[key]; });
}

// const CONVERT_PIRDS_TO_SLM = 1/1000;

// we have now changed this, there will be flow and
// pressure in the same samples, and we should filter.
// TODO: I need to add maximal start and end
// samples to equalize all the plots.
function samplesToLine(samples) {
    var flows = samples.filter(s => s.event == 'M' && s.type == 'F');

    // These are slm/1000, or ml/minute...
    // so we multiply by 1000 to get liters per minute
    var flow_values = unpack(flows,"val").map(v => v * CONVERT_PIRDS_TO_SLM);
    var fmillis = unpack(flows, 'ms');
    // Convert to seconds...
    var fmin = Math.min(...fmillis);
    var fzeroed = fmillis.map(m =>(m-fmin)/1000.0);

    var pressures = samples.filter(s => s.event == 'M' && s.type == 'D' && s.loc == 'A');

    var pmillis = unpack(pressures, 'ms');
    var pmin = Math.min(...pmillis);
    var pzeroed = pmillis.map(m =>(m-pmin)/1000.0);
    // the PIRDS standard is integral mm H2O, so we divide by 10
    var delta_p = unpack(pressures, 'val').map(p => p / 10);
    var diff_p = {type: "scatter", mode: "lines",
		  name: "pressure",
		  x: pzeroed,
		  y: delta_p,
		  line: {color: "#FF0000"}
		 };

  var flow = {type: "scatter", mode: "lines",
		name: "flow",
		x: fzeroed,
	      y: flow_values,
              xaxis: 'x2',
              yaxis: 'y2',
              fill: 'tozeroy',
		line: {color: '#0000FF'}
             };

  var max_flow = flow_values.reduce(
    function(a, b) {
      return Math.max(Math.abs(a), Math.abs(b));
    }
    ,0);
  var scaled_flow = flow_values.map(f => 100.0 * (f / max_flow));
  var flow_hollow = {type: "scatter", mode: "lines",
		name: "flow ghost",
		     x: fzeroed,
                     // Convert to a percentage
	             y: scaled_flow,
		line: {color: '#8888FF'}
               };
  return [diff_p,flow,flow_hollow];
}
function plot(samples, trans, breaths) {
  var new_data = samplesToLine(samples);
  var millis = unpack(samples, 'ms');
  var min = Math.min(...millis);
  var zeroed = millis.map(m =>(m-min)/1000.0);

  {
    var layout = {
      title: 'VentMon Breath Analysis',
      showlegend: false,
      xaxis: {domain: [0.0,1.0]},
      yaxis: {
        title: 'Airway P(cm H2O)',
        titlefont: {color: 'red'},
        tickfont: {color: 'red'},
      },
      xaxis2: {domain: [0.0,1.0]},
      yaxis2: {
        title: 'Flow l/minute',
        titlefont: {color: 'blue'},
        tickfont: {color: 'blue'}
      },
      grid: {
        rows: 2,
        columns: 1,
        pattern: 'independent',
        roworder: 'top to bottom'}
    }

    var double_plot = [new_data[0],new_data[1]];

    Plotly.newPlot('PFGraph', double_plot, layout);
  }

  // The Y-axis for the events will be percentage.
  // This is somewhat abstract; each trace has
  // a different meaning. In general it will be
  // % as a function of some known value, which
  // will be either a limit or a min or max.
  {
    var event_graph = [];
    if (trans) {
      // Transitions are simply scaled to 50%.
      var tmillis = unpack(trans, 'ms');
      var tzeroed = tmillis.map(m =>(m-min)/1000.0);
      var tstates = unpack(trans, 'state');
      var tstates_amped = tstates.map(m =>m*50);
      var transPlot = {type: "scatter",
                       mode: "lines+markers",
		       name: "trans",
		       x: tzeroed,
		       y: tstates_amped,
                       line: {shape: 'hv',
                              color: 'dkGreen'},
		      };
      // We add a hollow flow line to see in position..
      event_graph.push(new_data[2]);
      event_graph.push(transPlot);
    }

    if (breaths) {
      var bmillis = unpack(breaths, 'ms');
      var bzeroed = bmillis.map(m =>(m-min)/1000.0);
      // I'm goint to a add start and end transition to make the plot
      // come out right
      var ys = breaths.map( b => 0);
      var breathPlot = {type: "scatter", mode: "markers",
		        name: "Transitions",
		        x: bzeroed,
		        y: ys,
		        marker: { size: 8, color: "red",symbol: "diamond" },
                        textposition: 'bottom center',
                        text: bzeroed
		       };
      event_graph.push(breathPlot);
      // Now I attempt to extract volumes...
      // Our breaths mark the END of a breath..
      // so we want to draw the volumes that way.
      const volume_ht_factor = 20;
      var max_exh = unpack(breaths,'vol_e').reduce(
        function(a, b) {
          return Math.max(Math.abs(a), Math.abs(b));
        }
        ,0);
      var max_inh = unpack(breaths,'vol_i').reduce(
        function(a, b) {
          return Math.max(Math.abs(a), Math.abs(b));
        }
        ,0);
      var max_v = Math.max(max_inh,max_exh);

      // not sure why I have null work, have to understand.
      var max_work_per_liter = 0;
      var work_per_liter = [];
      for(var i = 0; i < breaths.length; i++) {
        var b = breaths[i];
        if (b.work != null) {
          var wpl = b.work / b.vol_i;
          if (wpl > max_work_per_liter)
            max_work_per_liter = wpl;
          var center = ((trans[b.trans_begin_inhale].ms + trans[b.trans_cross_zero].ms) - 2*min) / (2.0 * 1000.0);
          work_per_liter.push({ wpl: wpl, center: center });
        }
      }

      var exh_v = unpack(breaths, 'vol_e').map(e => 100 * e / max_v);
      var t_exh_v =  unpack(breaths, 'vol_e').map(e => Math.round(e*1000.0)+"ml exh");
      var inh_v = unpack(breaths, 'vol_i').map(i => 100 * i / max_v);
      var t_inh_v = unpack(breaths, 'vol_i').map(e => Math.round(e*1000.0)+"ml inh");


      var work_v = work_per_liter.map(w => (1/3) * (100 * w.wpl / max_work_per_liter));
      var t_work = work_per_liter.map(w => w.wpl.toFixed(2)+"J/L");
      var work_centers = unpack(work_per_liter, 'center');

      // now to graph properly, I must find the center of an inhalation.
      // I have packed these into the breaths...
      var inhale_centers = breaths.map(b => ((trans[b.trans_begin_inhale].ms + trans[b.trans_cross_zero].ms) - 2*min) / (2.0 * 1000.0));
      var exhale_centers = breaths.map(b => ((trans[b.trans_cross_zero].ms + trans[b.trans_end_exhale].ms) - 2*min) / (2.0 * 1000.0));

      var inhPlot = {type: "scatter", mode: "markers+text",
                     name: "Inh. ml",
                     textposition: 'bottom center',
                     x: inhale_centers,
                     y: inh_v,
                     text: t_inh_v,
                     marker: { size: 8,
                               color: 'black',
                               symbol: 'triangle-down'}
                    };
      var exhPlot = {type: "scatter", mode: "markers+text",
                     name: "Exh. ml",
                     textposition: 'top center',
                     marker : {
                       sizer: 12,
                       color: 'green',
                       symbol: 'triangle-up' },
                     x: exhale_centers,
                     y: exh_v,
                     text: t_exh_v,

                    };
      var workPlot = {type: "scatter", mode: "markers+text",
                     name: "WoB J",
                     textposition: 'bottom center',
                     x: work_centers,
                     y: work_v,
                     text: t_work,
                     marker: { size: 8,
                               color: 'blue',
                               symbol: 'triangle-right'}
                     };
      event_graph.push(workPlot);
      event_graph.push(inhPlot);
      event_graph.push(exhPlot);
    }

    // Now I will attempt to add other markers, such as Humidity, Altitude, and other events,
    // including possible warning events.

    function gen_graph_measurement(samples, name, color, textposition, tp, lc, vf, tf) {
      var selected = samples.filter(s => s.event == 'M' && s.type == tp && s.loc == lc);
      return gen_graph(selected, name, color, textposition, vf, tf);
    }

    function gen_graph_measurement_timed(samples, name, color, textposition, tp, lc, vf, tf,time_window_ms) {
      var messages = samples.filter(s => s.event == 'M' && s.type == tp && s.loc == lc);
      var separated = [];
      for(var i = 0; i < messages.length; i++) {
        var m = messages[i];
        if ((separated.length == 0) || ((separated.length > 0) && (separated[separated.length-1].ms < (m.ms - time_window_ms))))
          separated.push(m);
      }
      return gen_graph_measurement(separated, name, color, textposition, tp, lc, vf, tf);
    }

    function gen_graph(selected,name,color, textposition, vf, tf) {
      var millis = unpack(selected, 'ms');
      var zeroed = millis.map(m =>(m-min)/1000.0);
      var vs = selected.map(vf);
      var vs_t = vs.map(tf);
      var plot = {type: "scatter", mode: "markers+text",
                     name: name,
                     textposition: textposition,
                     x: zeroed,
                     y: vs,
                     text: vs_t,
                     marker: { size: 10, color: color }
                    };
      return plot;

    }
    function gen_message_events(samples,name,color, textposition) {
      var messages = samples.filter(s => s.event == 'E' && s.type == 'M');
      // Now I want to filter our all flow error messages...
      // There are two special ones, "FLOW OUT OF RANGE LOW" and
      // "FLOW OUT OF RANGE HIGH"
      // Basically, we will show only the first of these in a "run"
      // This is rather difficult; we will instead just use 200 ms
      // as a window.
      const TIME_WINDOW_MS = 400;

      var lows = [];
      var highs = [];
      var others = [];
      for(var i = 0; i < messages.length; i++) {
        var m = messages[i];
        if (m.buff == FLOW_TOO_LOW) {
          if ((lows.length == 0) || ((lows.length > 0) && (lows[lows.length-1].ms < (m.ms - TIME_WINDOW_MS))))
            lows.push(m);
        } else if (m.buff == FLOW_TOO_HIGH) {
          if ((highs.length == 0) || ((highs.length > 0) && (highs[highs.length-1].ms < (m.ms - TIME_WINDOW_MS))))
            highs.push(m);
        } else {
          others.push(m);
        }
      }
      if (lows.length > 0 || highs.length > 0) {
        set_flow_alert();
      } else {
        unset_flow_alert();
      }

      var lowsPlot = gen_graph(lows,"Low Range","blue",'top center',
                                    (s => -90.0),
                                    (v =>  "LOW"));
      var highsPlot = gen_graph(highs,"High Range","red",'bottom center',
                                    (s => 90.0),
                                    (v =>  "HIGH"));
      var othersPlot = gen_graph(others,"messages","Aqua",'top center',
                                    (s => -75.0),
                                 (v =>  v.buff));
      return [lowsPlot,highsPlot,othersPlot];
    }
    {
      var fio2AirwayPlot = gen_graph_measurement_timed(samples,
                                                 "FiO2 (%)",
                                                 "Black",'top center','O','A',
                                                 (s => s.val),
                                                 (v =>  "FiO2 (A): "+v.toFixed(1)+"%"),
                                                 // O2 reported only once every 10 seconds
                                                SAMPLES_BETWEEN_FIO2_REPORTS);
      event_graph.push(fio2AirwayPlot);
    }

    {
      var humAirwayPlot = gen_graph_measurement(samples,"Hum (%)","Aqua",'top center','H','A',
                              (s => s.val/100.0),
                              (v =>  "H2O (A): "+v.toFixed(0)+"%"));
      event_graph.push(humAirwayPlot);
    }

    {
      var humAmbientPlot = gen_graph_measurement(samples,"Hum (%)","CornflowerBlue",'bottom center','H','B',
                              (s => -s.val/100.0),
                                      (v =>  "H2O (B): "+(-v).toFixed(0)+"%"));
      event_graph.push(humAmbientPlot);
    }

    {
      var tempAirwayPlot = gen_graph_measurement(samples,"Temp A","red",'bottom center','T','A',
                              (s => s.val/100.0),
                              (v =>  "T (A): "+v.toFixed(1)+"C"));
      event_graph.push(tempAirwayPlot);
    }

    {
      var tempAmbientPlot = gen_graph_measurement(samples,"Temp B","orange",'top center','T','B',
                              (s => -s.val/100.0),
                                      (v =>  "T (B): "+(-v).toFixed(1)+"C"));
      event_graph.push(tempAmbientPlot);
    }

    // Altitude is really just a check that the system is working
     {
      var altAmbientPlot = gen_graph_measurement(samples,"Altitude","purple",'right','A','B',
                              (s => s.val/20.0),
                                      (v =>  "Alt: "+(v*20.0).toFixed(0)+"m"));
      event_graph.push(altAmbientPlot);
     }

     {
       var [l,h,o] = gen_message_events(samples,"Message","red",'right');
       event_graph.push(l);
       event_graph.push(h);
       event_graph.push(o);
     }



    // The BME680 sensor detects VOC gases. I don't think has any clinical value
    // compared

    // {
    //   var gasAirwayPlot = gen_graph_measurement(samples,"Gas A","yellow",'bottom center','G','A',
    //                           (s => s.val/1000.0),
    //                                 (v =>  "G (A): "+(v*100).toFixed(1)+"Ohms"));
    //   event_graph.push(gasAirwayPlot);
    // }

    // {
    //   var gasAmbientPlot = gen_graph_measurement(samples,"Gas B","gold",'top center','G','B',
    //                           (s => -s.val/1000.0),
    //                                   (v =>  "G (B): "+(-v*100).toFixed(1)+"Ohms"));
    //   event_graph.push(gasAmbientPlot);
    // }



    // I'm going to try putting the pressure
    // in faintly to make the graphs match

    var event_layout = {
      title: 'Events',
      showlegend: false,
      xaxis: {domain: [0.0,1.0]},
      yaxis: {
        range: [-100.0, 100.0]
      },
    };
    Plotly.newPlot('EventsGraph', event_graph,event_layout);
  }
}

function set_flow_alert() {
  $("#main_alert").show();
}
function unset_flow_alert() {
  $("#main_alert").hide();
}

function check_alarms(limits,key,limit,val,f,ms) {
  var alarms = [];
  if (limits[key][limit] && (f(val,limits[key][limit]))) {
    alarms.push({param: key,
                 limit: limit,
                 val: val,
                 ms: ms});
  }
  return alarms;
}


var LIMITS = {
  max: { h: 40,
         l: 0},
  avg: { h: 30,
         l: 0},
  min: { h: 10,
         l: -10},
  mv: { h: 10,
         l: 1},
  bpm: { h: 40,
         l: 5},
  ier: { h: 4,
         l: 0.25},
  tv: { h: 1000,
         l: 200},
  fio2: { h: 100,
         l: 18},
  taip: { h: 100, // These are in ms
         l: 0},
  trip: { h: 100, // These are in ms
          l: 0},
  wob: { h: 3,
         l: 0.2},
}

function load_ui_with_defaults(limits) {
  var Lkeys = Object.keys(limits);
  Lkeys.forEach((key,index) => {
    ["h","l"].forEach(limit => {
      $("#"+key+"_"+limit).val(limits[key][limit]);
    })
  });
}

function set_rise_time_pressures()
{
  $("#tarip_l").val(TAIP_AND_TRIP_MIN);
  $('#tarip_l').change(function () {
    TAIP_AND_TRIP_MIN = $("#tarip_l").val();
  });
  $("#tarip_h").val(TAIP_AND_TRIP_MAX);
  $('#tarip_h').change(function () {
    TAIP_AND_TRIP_MAX = $("#tarip_h").val();
  });
}

function reflectAlarmsInGUI(alarms) {
  var Lkeys = Object.keys(LIMITS);
  Lkeys.forEach((key,index) => {
    ["h","l"].forEach(limit => {
      var alarms_for_key = alarms.filter(s => s.param == key && s.limit == limit);
      if (alarms_for_key.length > 0) {
        $("#"+key).addClass("alarmred");
        $("#"+key+"_"+limit).addClass("alarmred");
      } else {
        $("#"+key).removeClass("alarmred");
        $("#"+key+"_"+limit).removeClass("alarmred");
      }
    })
  });
}

function process(samples) {
  const t = 200; // size of the window is 200ms
  const v = 50; // min volume in ml
  var [transitions,breaths] = computeMovingWindowTrace(samples,t,v);
  plot(samples,transitions,breaths);
  // How many seconds backwards should we look? Perhaps 20?
  var [bpm,tv,mv,EIratio,wob] = compute_respiration_rate(RESPIRATION_RATE_WINDOW_SECONDS,samples,transitions,breaths);

  var alarms = [];

  $("#bpm").text(bpm.toFixed(1));
  $("#tv").text(tv.toFixed(0));
  $("#mv").text(mv.toFixed(2));

  if (wob == "NA") {
    $("#wob").text("NA");
  } else {
    $("#wob").text(wob.toFixed(2));
  }
  if (EIratio == "NA") {
    $("#ier").text("NA");
  } else {
    $("#ier").text((1.0 / EIratio).toFixed(1));
  }

  var final_ms = samples[samples.length -1].ms;
  var b_ms = 0;
  if (breaths.length > 0) {
    b_ms = breaths[breaths.length -1].ms;
  } else {
    b_ms = final_ms;
  }
  function check_high_and_low(limits,key,v,ms) {
    var al1 = check_alarms(limits,key,"h",v,(a,b) =>(a > b),ms);
    var al2 = check_alarms(limits,key,"l",v,(a,b) =>(a < b),ms);

    return al1.concat(al2);
  }

  alarms = alarms.concat(check_high_and_low(LIMITS,"bpm",bpm.toFixed(1),b_ms));
  alarms = alarms.concat(check_high_and_low(LIMITS,"tv",tv.toFixed(0),b_ms));
  alarms = alarms.concat(check_high_and_low(LIMITS,"mv",mv.toFixed(2),b_ms));
  alarms = alarms.concat(check_high_and_low(LIMITS,"ier",(1.0 / EIratio).toFixed(1),b_ms));
  if (wob != "NA")
    alarms = alarms.concat(check_high_and_low(LIMITS,"wob",wob.toFixed(2),b_ms));

  // These must be moved out; in fact,
  // they must be made configurable!

  var taip = compute_mean_TRIP_or_TAIP(
    TAIP_AND_TRIP_MIN,
    TAIP_AND_TRIP_MAX,
    samples,
    true);
  if (taip != "NA") {
    $("#taip").text(taip.toFixed(1));
    alarms = alarms.concat(
      check_high_and_low(LIMITS,"taip",taip,b_ms));
  } else {
    $("#taip").text("NA");
  }

  var trip = compute_mean_TRIP_or_TAIP(
    TAIP_AND_TRIP_MIN,
    TAIP_AND_TRIP_MAX,
    samples,
    false);
  if (trip != "NA") {
    $("#trip").text(trip.toFixed(1));
    alarms = alarms.concat(
      check_high_and_low(LIMITS,"trip",trip,b_ms));
  } else {
    $("#trip").text("NA");
  }

  var [min,avg,max,palarms] = compute_pressures(RESPIRATION_RATE_WINDOW_SECONDS,samples,alarms,LIMITS);
  alarms = alarms.concat(palarms);

  $("#min").text(min.toFixed(1));
  $("#avg").text(avg.toFixed(1));
  $("#max").text(max.toFixed(1));

  var fio2 = compute_fio2_mean(RESPIRATION_RATE_WINDOW_SECONDS,samples);

   if(fio2 == null){
    $("#fio2").text("NA");
   } else {
    alarms = alarms.concat(check_high_and_low(LIMITS,"fio2",fio2.toFixed(1),b_ms));
    $("#fio2").text(fio2.toFixed(1));
   }

  reflectAlarmsInGUI(alarms);
}

// WARNING: This is a hack...if the timestamp is negative,
// we treat it as a limited (beyond range of sensor) measurement.
// Our goal is to warn about this, but for now we will just
// ignore and correct.
function sanitize_samples(samples) {
  samples.forEach(s =>
                  {
                    if (s.event == "M") {
                      if ("string" == (typeof s.ms))
                        s.ms = parseInt(s.ms);
                      if ("string" == (typeof s.val))
                        s.val = parseInt(s.val);
                      if ("string" == (typeof s.num))
                        s.num = parseInt(s.num);
                      if (s.ms < 0) {
                        s.ms = -s.ms;
                      } else if (s.event == "E") {
                      }
                    }

                  });
  return samples;
}

function retrieveAndPlot(){
  var trace_piece = (TRACE_ID) ? "/" + TRACE_ID : "";
  DSERVER_URL = $("#dserverurl").val();
  var url =  DSERVER_URL + trace_piece + "/json?n="+ NUM_TO_READ;

  $.ajax({url: url,
          success: function(cur_sam){

              // WARNING: This is a hack...if the timestamp is negative,
              // we treat it as a limited (beyond range of sensor) measurement.
              // Our goal is to warn about this, but for now we will just
            // ignore and correct.
            if (cur_sam == null) {
              console.log("No return");
              return;
            }
            if (cur_sam && cur_sam.length == 0) {
              console.log("no samples; potential misconfiguration!");
            } else {
              if (typeof(cur_sam) == "string") {
                console.log("Error!");
                stop_interval_timer();
                $("#livetoggle").prop("checked",false);
                console.log(cur_sam);
              } else {
                cur_sam = sanitize_samples(cur_sam);
	        if (INITS_ONLY) {
	          samples = cur_sam;
	          INITS_ONLY = false;
	        } else {
                  var discard = Math.max(0,
                                         samples.length + cur_sam.length - MAX_SAMPLES_TO_STORE_S);
	          samples = samples.slice(discard);
	        }

	        samples = samples.concat(cur_sam);
	        // We are not guaranteeed to get samples in order
	        // we sort them....
	        samples = samples.sort((a,b) => a.ms - b.ms);
                process(samples);
              }
            }
          },
          error: function(xhr, ajaxOptions, thrownError) {
	    console.log("Error!" + xhr.status);
	    console.log(thrownError);
            //            clearInterval(intervalID);
            stop_interval_timer();
            $("#livetoggle").prop("checked",false);
          }
         });
}





//   // A routine to calculate work per breath
// function PressureVolumeWork(breath, transitions, samples) {
//   // -1 for quadilateral approximation
//   if (breath.vol_i == 0) {
//     return null;
//   } else {
//     var beginTransition = transitions[breath.trans_begin_inhale];
//     var beginTime_ms = beginTransition.ms;
//     var endTransition = transitions[breath.trans_cross_zero];
//     var endTime_ms = endTransition.ms;
//     var flows = samples.filter(s => s.event == 'M' && s.type == 'F' &&
//                                s.ms >= beginTime_ms && s.ms <= endTime_ms);
//     var pressures = samples.filter(s => s.event == 'M' && s.type == 'D' &&
//                                    s.loc == 'A'&& s.ms >= beginTime_ms && s.ms <= endTime_ms);

//     // Note: The algorithm below relies on the fact that there is
//     // only one flow or pressure with a single ms value; and that
//     // increvementing an index necessarily incremenst the .ms value.

//     // Without two samples, we have no duration and can't define
//     // work.
//     if (pressures.length < 2 || flows.length < 2) return null;

//     var ct = Math.min(flows[0].ms,pressures[0].ms);
//     var lfp = { val : flows[0].val, ms: flows[0].ms } ; // last flow point
//     var lpp = { val : pressures[0].val, ms: pressures[0].ms }; // last pressure_point
//     var fi = increment_past(flows,flows[0].ms,0); // Index of next flow sample
//     var pi = increment_past(pressures,pressures[0].ms,0); // Index of next pressure sample
//     var w = 0; // current work

//     // compute flow at time ms give index and last point
//     // This is just a simple linear interpolation
//     function f(ms,cur,last) {
//       var ms0 = last.ms;
//       var ms1 = cur.ms;
//       return last.val + (cur.val - last.val)*(ms - ms0)/(ms1 - ms0);
//     }
//     function increment_past(array,ms,index) {
//       var begin = index;
//       while(index < array.length && array[index].ms <= ms)
//         index++;
//       if (index == begin) debugger;
//       if (index >= array.length)
//         return null;
//       else
//         return index;
//     }

//     // A fundamental invariant:
//     // pressures[pi].ms > lpp.ms
//     // flows[pi].ms > lfp.ms
//     while ((fi + pi) < (flows.length +  pressures.length)) {
//       // Invariant always increment fi or pi
//       // fi and pi point to unprocessed value
//       console.assert(pressures[pi].ms > lpp.ms);
//       console.assert(flows[fi].ms > lfp.ms);
//       var ms;
//       if (pressures[pi].ms <= flows[fi].ms) { // process pressure
//         ms = pressures[pi].ms;
//         pi = increment_past(pressures,ms,pi);
//         if (flows[fi].ms <= ms) {
//           fi = increment_past(flows,ms,fi);
//         }
//       } else {
//         ms = flows[fi].ms;
//         fi = increment_past(flows,ms,fi);
//         if (pressures[pi].ms <= ms) {
//           pi = increment_past(pressures,ms,pi);
//         }
//       }
//       if ((fi === null) || (pi === null))
//         break;
//       var dur_s = (ms - ct) / 1000;
//       console.assert(pressures[pi].ms > lpp.ms);
//       console.assert(flows[fi].ms > lfp.ms);
//       var nf = f(ms,flows[fi],lfp);
//       var np = f(ms,pressures[pi],lpp);
//       var f1 = (lfp.val + nf)/2;
//       var p1 = (lpp.val + np)/2;
//       // convert 10ths of cm H2O to pascals..
//       var p1_pa = (p1 * 98.0665) / 10;

//       // convert flows in lpm to cubic meters per seconds
//       var f1_m_cubed_per_s = f1 / (1000 * 1000 * 60);

//       // work is now in Joules!
//       w += dur_s * p1_pa * f1_m_cubed_per_s;
//       lfp = { val : f1, ms: ms };
//       lpp = { val : p1, ms: ms };
//       ct = ms;
//     }
//     return w;
//   }
// }

// // Let's first set up a perfectly square 1-second
// function generate_synthetic_trace() {
//   const SAMPLES_PER_PHASE = 1000;
//   const SAMPLES_MS_PER_SAMPLE = 1;
//   var trace = [];
//   var cur = 0;
//   // p is a probability of occuring.
//   function push_samples(start,num,d,f,pd,pf) {
//     for(var i = start; i < start+num; i++) {
//       if (Math.random() < pd) {
//         trace.push(
//           {
//             event: "M",
//             loc: "A",
//             ms: i,
//             num: 0,
//             type: "D",
//             val: d
//           });
//       }
//       if (Math.random() < pf) {
//         trace.push(
//           {
//             event: "M",
//             loc: "A",
//             ms: i,
//             num: 0,
//             type: "F",
//             val: f
//           });
//       }
//     }
//   }
//   const P1 = 1/2;
//   const P2 = 1/2;
//   push_samples(SAMPLES_PER_PHASE,SAMPLES_PER_PHASE,200,50000,P1,P2);
//   push_samples(0,SAMPLES_PER_PHASE,-200,-50000,P1,P2);
//   push_samples(SAMPLES_PER_PHASE,SAMPLES_PER_PHASE,200,50000,P1,P2);
//   push_samples(SAMPLES_PER_PHASE*2,SAMPLES_PER_PHASE,-200,-50000,P1,P2);
//   push_samples(SAMPLES_PER_PHASE*3,SAMPLES_PER_PHASE,200,50000,P1,P2);
//   return trace;
// }

// function nearp(x,y,d) {
//   return Math.abs(x - y) <= d;
// }

// function testWorkSynthetic(){ // breaths give us inspiration transition points
//   var samples = generate_synthetic_trace();

//   const JOULES_IN_BREATH = 1 * 1961.33 * 50000 / (60e+6);

//   var flows = samples.filter(s => s.event == 'M' && s.type == 'F');
//     var first_time = flows[0].ms;
//     var last_time = flows[flows.length - 1].ms;
//     var duration = last_time - first_time;
//     console.log(flows);


//   const vm = 10;
//   // There is a problem here that this does not create a transition at the beginning.
//   var transitions = compute_transitions(vm,flows);
//     var breaths = compute_breaths_based_without_negative_flow(transitions,flows);
//     console.log(breaths);
//     for(i = 0; i<breaths.length; i++) {
//       var w = PressureVolumeWork(breaths[i], transitions, samples);
//       console.assert((w == null) || (nearp(w,JOULES_IN_BREATH),0.1));
//       console.log("final (Joules) = ",w);
//     }
//   return true;
// }


//   function testWork(samples){ // breaths give us inspiration transition points
//     var flows = samples.filter(s => s.event == 'M' && s.type == 'F');
//     var first_time = flows[0].ms;
//     var last_time = flows[flows.length - 1].ms;
//     var duration = last_time - first_time;
//     console.log(flows);

//     const vm = 10;
//     var transitions = compute_transitions(vm,flows);
//     var breaths = compute_breaths_based_without_negative_flow(transitions,flows);
//     console.log(breaths);
//     for(i = 0; i<breaths.length; i++) {
//       var w = PressureVolumeWork(breaths[i], transitions, samples);
//       console.log(w);
//     }
//    }



  // // This should be in liters...
  // function integrateSamples(a,z,flows) {
  //   // -1 for quadilateral approximation
  //   var vol = 0;
  //   for(var j = a; j < z-1; j++) {
  //     // I'll use qadrilateral approximation.
  //     // We'll form each quadrilateral between two samples.
  //     var ms = flows[j+1].ms - flows[j].ms;
  //     var ht = ((flows[j+1].val + flows[j].val )/2) * CONVERT_PIRDS_TO_SLM;
  //     // Flow is actually in standard liters per minute,
  //     // so to get liters we divide by 60 to it l/s,
  //     // and and divde by 1000 to convert ms to seconds.
  //     // We could do that here, but will move constants
  //     // to end...
  //     vol += ms * ht;
  //     if (isNaN(vol)) {
  //       debugger;
  //     }
  //   }
  //   return vol/(60*1000);
  // }

  // This is based only on inhalations, and
  // is therefore functional when there is a check valve
  // in place. Such a system will rarely
  // have negative flows, and we must mark
  // the beginning of a breath from a transition to a "1"
  // state from any other state.
  // This algorithm is simple: A breath begins on a trasition
  // to 1 from a not 1 state. This algorithm is susceptible
  // to "stutter" near the boundary point, but if necessary
  // a digital filter would sove that; we have not yet found
  // that level of sophistication needed.
  // We still want to track zeros, but now must strack them
  // as a falling signal.

  // function compute_breaths_based_without_negative_flow(transitions,flows) {
  //   var beg = 0;
  //   var zero = 0;
  //   var last = 0;
  //   var voli = 0;
  //   var vole = 0;

  //   var breaths = [];
  //   var expiring = true;

  //   for(var i = 0; i < transitions.length; i++) {
  //     // We're looking for the end of the inhalation here!!
  //     if (((i -1) >= 0) && transitions[i-1].state == 1 &&
  //         (transitions[i].state == 0 || transitions[i].state == -1 )) {
  //       zero = i;
  //     }
  //     if (expiring && transitions[i].state == 1) {
  //       breaths.push({ ms: transitions[i].ms,
  //       	       sample: transitions[i].sample,
  //       	       vol_e: vole,
  //       	       vol_i: voli,
  //                      trans_begin_inhale: beg,
  //                      trans_cross_zero: zero,
  //                      trans_end_exhale: i,
  //       	     }
  //       	    );
  //       var w = PressureVolumeWork(breaths[breaths.length-1], transitions, samples);
  //       breaths[breaths.length-1].work = w;
  //       beg = i;
  //       expiring = false;
  //       vole = integrateSamples(last,transitions[i].sample,flows);

  //       last = transitions[i].sample;
  //     }
  //     if (!expiring && ((transitions[i].state == -1) || (transitions[i].state == 0)))  {
  //       expiring = true;
  //       voli = integrateSamples(last,transitions[i].sample,flows);
  //       last = transitions[i].sample;
  //     }
  //   }
  //   return breaths;
  // }


// // A simple computation of a moving window trace
// // computing [A + -B], where A is volume to left
// // of sample int time window t, and B is volume to right
// // t is in milliseconds
// function computeMovingWindowTrace(samples,t,v) {

//   var flows = samples.filter(s => s.event == 'M' && s.type == 'F');
//   var first_time = flows[0].ms;
//   var last_time = flows[flows.length - 1].ms;
//   var duration = last_time - first_time;

//   // Here is an idea...
//   // We define you to be in one of three states:
//   // Inspiring, expiring, or neither.
//   // Every transition between these states is logged.
//   // Having two inspirations between an expiration is
//   // weird but could happen.
//   // We record transitions.
//   // When the time series crossed a fixed threshold
//   // or zero, it causes a transition. If you are inspiring,
//   // you have to cross zero to transition to neither,
//   // and you start expiring when you cross the treshold.

//   // This is measured in standard liters per minute.
//   const vm = 10; // previously used 4

//   // We will model this as a list of transitions.
//   // A breath is any number of inspirations followed by
//   // any number of expirations. (I+)(E+)

//   var transitions = compute_transitions(vm,flows);

//   // Now that we have transitions, we can apply a
//   // diferrent algorithm to try to define "breaths".
//   // Because a breath is defined as an inspiration
//   // and then an expiration, we will define a breath
//   // as from the first inspiration, until there has
//   // been one expiration, until the next inspiration.
//   var breaths = [];
//   var expiring = false;

//   function compute_breaths_based_on_exhalations(transitions) {
//     var beg = 0;
//     var zero = 0;
//     var last = 0;
//     var voli = 0;
//     var vole = 0;

//     for(var i = 0; i < transitions.length; i++) {
//       // We're looking for the end of the inhalation here!!
//       if (((i -1) >= 0) && transitions[i-1].state == 1 && (transitions[i].state == 0 || transitions[i].state == -1 )) {
//         zero = i;
//       }
//       if (expiring && transitions[i].state == 1) {
//         breaths.push({ ms: transitions[i].ms,
// 		       sample: transitions[i].sample,
// 		       vol_e: vole,
// 		       vol_i: voli,
//                        trans_begin_inhale: beg,
//                        trans_cross_zero: zero,
//                        trans_end_exhale: i,
// 		     }
// 		    );
//         var w = PressureVolumeWork(breaths[0], transitions, samples);
//         breaths[0].work = w;
//         beg = i;
//         expiring = false;
//         vole = integrateSamples(last,transitions[i].sample,flows);
//         last = transitions[i].sample;
//       }
//       if (!expiring && transitions[i].state == -1) {
//         expiring = true;
//         voli = integrateSamples(last,transitions[i].sample,flows);
//         last = transitions[i].sample;
//       }
//     }
//   }

//   breaths = compute_breaths_based_without_negative_flow(transitions,flows);

//   return [transitions,breaths];
// }


$("#useofficial").click(function() {
    DSERVER_URL = VENTMON_DATA_LAKE;
    $("#dserverurl").val(DSERVER_URL);
});

$("#import").click(function() {
    var input_trace = $("#json_trace").val();
    samples = JSON.parse(input_trace);
});

$("#export").click(function() {
    $("#json_trace").val(JSON.stringify(samples,null,2));
});


  // Experimental timing against the data server

function stop_interval_timer() {
  clearInterval(intervalID);
  intervalID = null;
  $("#livetoggle").prop("checked",false);
}
function start_interval_timer() {
  if (intervalID) {
    stop_interval_timer();
  }
  intervalID = setInterval(
    function() {
      retrieveAndPlot();
    },
    DATA_RETRIEVAL_PERIOD);
  $("#livetoggle").prop("checked",true);
}
function toggle_interval_timer() {
  if (intervalID) {
    stop_interval_timer();
  } else {
    start_interval_timer();
  }
}

function setLimit(ed) {
  if(ed.which == 13) {
    var id = ed.target.id;
    var v = $("#"+id).val();
    var vf = parseFloat(v);
    var destruct = id.split("_");
    LIMITS[destruct[0]][destruct[1]] = isNaN(vf) ? null : vf;
  }
}

$(".fence").keypress(setLimit);

$("#livetoggle").change(toggle_interval_timer);

$("#startoperation").click(start_interval_timer);

$("#stopoperation").click(stop_interval_timer);

$( document ).ready(function() {

  if (window.location.protocol == "http:")
    DSERVER_URL = window.location.protocol + "//" + window.location.host;
  else
    DSERVER_URL = "http://localhost";


  $( "#dserverurl" ).val( DSERVER_URL );
  $('#dserverurl').change(function () {
    DSERVER_URL = $("#dserverurl").val();
    start_interval_timer();
  });

  $( "#traceid" ).val( TRACE_ID );
  $('#traceid').change(function () {
    TRACE_ID = $("#traceid").val();
    start_interval_timer();
  });


  $( "#samples_to_plot" ).val( MAX_SAMPLES_TO_STORE_S );
  $('#samples_to_plot').change(function () {
    MAX_SAMPLES_TO_STORE_S = $("#samples_to_plot").val();
  });

  samples = init_samples();
  process(samples);
  start_interval_timer();

  load_ui_with_defaults(LIMITS);

  set_rise_time_pressures(
    TAIP_AND_TRIP_MIN,
    TAIP_AND_TRIP_MAX,
  );

});


</script>