Explorar o código

OP task #507 | add alarms settings

Avetisyan Karen %!s(int64=6) %!d(string=hai) anos
pai
achega
345b487118

BIN=BIN
web_interface/dist/wui-6/main.css


BIN=BIN
web_interface/dist/wui-6/main.js


BIN=BIN
web_interface/dist/wui-6/settings.html


BIN=BIN
web_interface/dist/wui-8/main.js


BIN=BIN
web_interface/dist/wui-8/settings.html


BIN=BIN
web_interface/dist/wui/main.css


BIN=BIN
web_interface/dist/wui/main.js


BIN=BIN
web_interface/dist/wui/settings.html


+ 86 - 0
web_interface/src/wui-6/main.css

@@ -2710,3 +2710,89 @@ h4.span_4_of_4 {
   width: 100% !important;
   margin-bottom: 10px;
 }
+
+.spinBox{
+  width: 100%;
+  position      : relative;
+  display       : inline-block;
+  padding-bottom: 5px;
+  -webkit-appearance: none;
+}
+
+.spinBox input{
+  width: 100%;
+  display       : block;
+  /*border        : 0.0625em solid rgb(224,224,224);*/
+  border-right  : none;
+  text-align    : center;
+  -webkit-appearance: none;
+  padding: 8px 7px;
+  font-size: 14px;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.spinBox input:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.spinBox input[disabled]{
+  background : rgb(240,240,240);
+}
+
+.spinBoxUp,
+.spinBoxDown{
+  position         : absolute;
+  display          : block;
+  width            : 2em;
+  height           : 2em;
+  border           : 0.0625em solid rgb(224,224,224);
+  background       : rgb(248,248,248);
+  cursor           : pointer;
+  font-weight: bold;
+}
+
+.spinBox input[disabled] + span,
+.spinBox input[disabled] + span + span{
+  background : rgb(240,240,240);
+  cursor     : auto;
+}
+
+.spinBoxUp span,
+.spinBoxDown span{
+  display : block;
+  width   : 1em;
+  height  : 1em;
+}
+
+.spinBoxUp{
+  text-align: center;
+  line-height: 2em;
+  vertical-align:middle;
+  border-top-right-radius: 5px;
+  border-bottom-right-radius: 5px;
+  right: 0;
+  top: 0;
+}
+.spinBoxDown{
+  vertical-align:middle;
+  line-height: 2em;
+  border-top-left-radius: 5px;
+  border-bottom-left-radius: 5px;
+  text-align: center;
+  top: 0;
+  left: 0;
+}
+.spinboxMark{
+  position: absolute;
+  text-align: center;
+  font-style: italic;
+  padding: 0.3em;
+  height  : 1.59em;
+}

+ 371 - 2
web_interface/src/wui-6/main.js

@@ -778,7 +778,15 @@ function checkOther(ele) {
 }
 
 // ################################################################################
-
+function fillTempRange(id, min, max) {
+  var obj = $(id);
+  for (var i = min; i <= max; i++) {
+    var opt = document.createElement('option');
+    opt.value = i;
+    opt.text = i;
+    obj.appendChild(opt);
+  }
+}
 function settingsGET(){
   getJSON('settings.cgi', function(data) {
     //SNMP params
@@ -798,7 +806,27 @@ function settingsGET(){
     $('gw').value       = data.gw;
     $('mask').value     = data.mask;
     $('dhcp').checked   = data.dhcp;
-
+    // Alarms
+    fillTempRange('temp_high', 40, 80);
+    fillTempRange('temp_low', -40, 0);
+    fillTempRange('loadvolt_high', 60, 90);
+    fillTempRange('mainvolt_low', 110, 180);
+    fillTempRange('mainvolt_high', 120, 130);
+    $('temp_high').value = data.temp_high;
+    $('temp_low').value = data.temp_low;
+    temp_hist     = new SpinBox('temp_hist', {
+      'name':'temp_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': data.temp_hist
+    });
+    $('loadvolt_high').value = data.loadvolt_high;
+    loadvolt_hist = new SpinBox('loadvolt_hist', {
+      'name':'loadvolt_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': data.loadvolt_hist
+    });
+    $('mainvolt_low').value = data.mainvolt_low;
+    $('mainvolt_high').value = data.mainvolt_high;
+    mainvolt_hist = new SpinBox('mainvolt_hist', {
+      'name':'mainvolt_hist','minimum':10,'maximum':30,'step':1, 'value': data.mainvolt_hist
+    });
+    //
     $('tn_enabled').checked   = data.tn_enabled;
     $('tn_port').value        = data.tn_port;
     // DateTime
@@ -911,7 +939,348 @@ function infoGet() {
 }
 
 // ################################################################################
+/*
+
+SpinBox.js
+
+Implements a spin box interface for a text field
+
+Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
+the terms of the CC0 1.0 Universal legal code:
+
+http://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+*/
+
+/* Creates a spin box. The parameter is:
+ *
+ * container - either the DOM node to contain the spin box or the ID of the node
+ * options   - an object containing various parameters; this optional parameter
+ *             defaults to the empty object
+ *
+ * The options object may contain the following keys:
+ *
+ * className - a class name to apply to the container; the class name with the
+ *             suffixes 'Up' and 'Down' will be applied to the up and down
+ *             buttons. The default value is 'spinBox'.
+ * value     - the initial value for the spin box. The default is 0 (subject to
+ *             restrictions on the minimum and maximum value) if the input
+ *             element did not already exist, or the existing value if the input
+ *             element did already exist.
+ * step      - the value by which to increment or decrement the value. The
+ *             default value is 1.
+ * minimum   - the minimum allowed value. The default is not to have a minimum
+ *             value.
+ * maximum   - the maximum allowed value. The default is not to have a maximum
+ *             value.
+ * decimals  - the number of decimal places allowed. The default is 0.
+ * name      - input name attribute.
+ *
+ * Note that the minimum, maximum, and decimal places restrictions are enforced
+ * for values set by the spin box, but a value outside of these restrictions may
+ * be typed by the user.
+ */
+function SpinBox(container, options) {
+
+  // fetch the DOM node if a string was supplied
+  if (typeof container == 'string') {
+    container = document.getElementById(container);
+    container.innerHTML = '';
+  }
+
+  // store the options and set the default values
+  this.options = (options ? options : {});
+  if (!('className' in this.options)) this.options.className = 'spinBox';
+  if (!('step'      in this.options)) this.options.step      = 1;
+  if (!('decimals'  in this.options)) this.options.decimals  = 0;
+
+  // check whether the input field should be created
+  var inputs = container.getElementsByTagName('input');
+  if (inputs.length === 0) {
+
+    // create the input node
+    this.input = document.createElement('input');
+    this.input.setAttribute('minimum', this.options.minimum);
+    this.input.setAttribute('maximum', this.options.maximum);
+    this.setValue('value' in this.options ? this.options.value : 0);
+    this.setName('name'   in this.options ? this.options.name : '');
+    container.appendChild(this.input);
+
+  } else {
+
+    // store a reference to the input node
+    this.input = inputs[0];
+    this.setValue(this.options.value ? this.options.value : this.input.value);
+
+  }
+
+  // create the up button
+  var upButton = document.createElement('span');
+  upButton.appendChild(document.createElement('span'));
+  upButton.innerHTML = '+';
+  container.appendChild(upButton);
+
+  // create the down button
+  var downButton = document.createElement('span');
+  downButton.appendChild(document.createElement('span'));
+  downButton.innerHTML = '-';
+  container.appendChild(downButton);
+
+  // apply the classes
+  container.className += ' ' + this.options.className;
+  upButton.className = this.options.className + 'Up';
+  downButton.className = this.options.className + 'Down';
+
+  // add the listeners
+  this.addEventListener(this.input, 'mousewheel',     this.handleMouseWheel, [], true);
+  this.addEventListener(this.input, 'DOMMouseScroll', this.handleMouseWheel, [], true);
+  this.addEventListener(this.input, 'keydown',        this.handleKeyDown, [], true);
+  this.addEventListener(this.input, 'keypress',       this.handleKeyPress, [], true);
+  this.addEventListener(this.input, 'keyup',          this.stop);
+  this.addEventListener(upButton,   'mousedown',      this.start, [true]);
+  this.addEventListener(upButton,   'mouseup',        this.stop);
+  this.addEventListener(upButton,   'mouseout',       this.stop);
+  this.addEventListener(downButton, 'mousedown',      this.start, [false]);
+  this.addEventListener(downButton, 'mouseup',        this.stop);
+  this.addEventListener(downButton, 'mouseout',       this.stop);
+
+}
+
+/* Returns the current value. This will be a number, or the value NaN if the
+ * current contents of the input field do not start with a valid number.
+ */
+SpinBox.prototype.getValue = function () {
+
+  // parse and return the value
+  return parseFloat(this.input.value);
+
+};
 
+SpinBox.prototype.setName = function (name) {
+
+  // asign input name
+  this.input.name = name;
+};
+/* Sets the value. Restrictions on the minimum and maximum value are enforced.
+ * The parameter is:
+ *
+ * value - the value
+ */
+SpinBox.prototype.setValue = function (value) {
+
+  // ensure the value is within the permitted range
+  if ('minimum' in this.options) value = Math.max(this.options.minimum, value);
+  if ('maximum' in this.options) value = Math.min(this.options.maximum, value);
+
+  // store the sign
+  var sign = (value < 0 ? '-' : '');
+  value = Math.abs(value);
+
+  // determine the multiplier for rounding
+  var multiplier = Math.pow(10, this.options.decimals);
+
+  // split the value in to integer and fractional parts
+  value = Math.round(value * multiplier);
+  var integer = (value - value % multiplier) / multiplier;
+  var fractional = '' + value % multiplier;
+
+  // add leading zeros to the fractional part
+  while (fractional.length < this.options.decimals) {
+    fractional = '0' + fractional;
+  }
+
+  // set the value
+  this.input.value =
+      sign + integer + (this.options.decimals > 0 ? '.' + fractional : '');
+
+  // check whether the browser can dispatch events
+  if ('dispatchEvent' in this.input){
+
+    // create the event
+    try{
+      var event = new Event('change', {bubbles : true, cancelable : true});
+    }catch (e){
+      var event = document.createEvent('Event');
+      event.initEvent('change', true, true);
+    }
+
+    // dispatch the event
+    this.input.dispatchEvent(event);
+
+  }
+
+};
+
+/* Adds an event listener to a node. The event listener is bound to the current
+ * value of 'this'. The parameters are:
+ *
+ * node         - the node
+ * event        - the event name
+ * listener     - the listener functions
+ * parameters   - an array of additional parameters to pass to the listener;
+ *                these are placed after the event parameter
+ * allowDefault - true if the default action should not be prevented
+ */
+SpinBox.prototype.addEventListener = function (
+node, event, listener, parameters, allowDefault) {
+
+  // store a reference to the 'this' object
+  var thisObject = this;
+
+  // create the bound listener
+  function boundListener(e) {
+
+    // get the event if it is not supplied
+    if (!e) e = window.event;
+
+    // call the listener
+    listener.apply(thisObject, [e].concat(parameters));
+
+    // prevent the default action if necessary
+    if (!allowDefault) {
+      if (e.preventDefault) {
+        e.preventDefault();
+      } else {
+        e.returnValue = false;
+      }
+    }
+
+  }
+
+  // add the event listener
+  if (node.addEventListener) {
+    node.addEventListener(event, boundListener, false);
+  } else {
+    node.attachEvent('on' + event, boundListener);
+  }
+
+};
+
+/* Handles a mouse wheel event by updating the value if the field is active. The
+ * parameter is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleMouseWheel = function (e) {
+
+  // check whether the field is active
+  if (document.activeElement == this.input) {
+
+    // update the value
+    if (e.wheelDelta) {
+      this.start(e, e.wheelDelta > 1);
+    } else if (e.detail) {
+      this.start(e, e.detail < 1);
+    }
+    this.stop();
+
+    // prevent the default action
+    if (e.preventDefault) {
+      e.preventDefault();
+    } else {
+      e.returnValue = false;
+    }
+
+  }
+
+};
+
+/* Handles a key down event by starting updating if appropriate. The parameter
+ * is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleKeyDown = function (e) {
+
+  // if the up or down keys were pressed, start updating
+  if (e.keyCode == 38) this.start(e, true);
+  if (e.keyCode == 40) this.start(e, false);
+
+};
+
+/* Handles a key press event by filtering out invalid characters. The parameter
+ * is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleKeyPress = function (e) {
+
+  // determine the character code
+  var charCode = ('charCode' in e ? e.charCode : e.keyCode);
+
+  // allow special key presses
+  if (charCode === 0 || e.altKey || e.ctrlKey || e.metaKey) return;
+
+  // allow a minus sign if the value can be negative
+  if (charCode == 45 && (!('minimum' in this.options) || this.options.minimum < 0)) {
+    return;
+  }
+
+  // allow a decimal point if the value may contain decimals
+  if (charCode == 46 && this.options.decimals > 0) return;
+
+  // allow digits
+  if (charCode >= 48 && charCode <= 57) return;
+
+  // prevent the default action
+  if (e.preventDefault) {
+    e.preventDefault();
+  } else {
+    e.returnValue = false;
+  }
+
+};
+
+/* Starts updating the value. The parameters are:
+ *
+ * e  - the event object
+ * up - true to increment the value, false to decrement the value
+ */
+SpinBox.prototype.start = function (e, up) {
+
+  // if the field is disabled or we are already updating, return immediately
+  if (this.input.disabled || 'timeout' in this) return;
+
+  // set the update step
+  this.updateStep = (up ? this.options.step : -this.options.step);
+
+  // initialise the timeout delay
+  this.timeoutDelay = 500;
+
+  // update the value
+  this.update();
+
+};
+
+// Stops update the value.
+SpinBox.prototype.stop = function () {
+
+  // clear the timeout if it exists
+  if ('timeout' in this) {
+    window.clearTimeout(this.timeout);
+    delete this.timeout;
+  }
+
+};
+
+// Updates the value.
+SpinBox.prototype.update = function () {
+
+  // determine the current value
+  var value = parseFloat(this.input.value);
+  if (isNaN(value)) value = 0;
+
+  // update the value
+  this.setValue(value + this.updateStep);
+
+  // reduce the delay
+  this.timeoutDelay = Math.max(20, Math.floor(this.timeoutDelay * 0.9));
+
+  // call this function again
+  var thisObject = this;
+  this.timeout = window.setTimeout(function () { thisObject.update(); }, this.timeoutDelay);
+
+};
 
 /*
   This code is from Dynamic Web Coding at dyn-web.com

+ 8 - 0
web_interface/src/wui-6/settings.cgi

@@ -16,6 +16,14 @@ JSON = {
   "batcap":7,
   "batcharge_volt":2.275,
   "tempcomp":False,
+  "temp_high": 45,
+  "temp_low": -10,
+  "temp_hist": 0.5,
+  "loadvolt_high": 65,
+  "loadvolt_hist": 0.5,
+  "mainvolt_low": 112,
+  "mainvolt_high": 120,
+  "mainvolt_hist": 12,
   "tempcomp_k_buf":3,
   "tempcomp_k_cycle":4,
   "batsym":False,

+ 66 - 1
web_interface/src/wui-6/settings.html

@@ -52,6 +52,7 @@
     <li><a href="#snmpt" class="activeTab">SNMP</a></li>
     <li><a href="#inout">Сухие контакты</a></li>
     <li><a href="#netw">Сетевые параметры</a></li>
+    <li><a href="#alarms">Аварии</a></li>
     <li><a href="#service">Сервис</a></li>
     <li><a href="#ntpt">Время</a></li>
   </ul>
@@ -206,6 +207,68 @@
     </div>
   </div>
 </div>
+<div id="alarms" class="tabpane">
+  <div class="panel-heading">Аварии</div>
+  <div class="panel-body section group">
+    <div class="col span_1_of_2">
+      <h4 class="col span_3_of_3">Температура</h4>
+      <div class="col span_1_of_3">
+        <label for="temp_low" class="col span_2_of_2">Нижняя граница</label>
+        <div class="col span_2_of_2">
+          <select class="form-control" name="temp_low" id="temp_low"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="temp_high" class="col span_2_of_2">Верхняя граница</label>
+        <div class="col span_2_of_2">
+          <select class="form-control" name="temp_high" id="temp_high"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="temp_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="temp_hist"></span>
+        </div>
+      </div>
+    </div>
+    <div class="col span_1_of_2">
+      <h4 class="col span_2_of_2">Нагрузка</h4>
+      <div class="col span_1_of_2">
+        <label for="loadvolt_high" class="col span_2_of_2">Верхняя граница</label>
+        <div class="col span_2_of_2">
+          <select name="loadvolt_high" id="loadvolt_high" class="form-control"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_2">
+        <label for="loadvolt_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="loadvolt_hist"></span>
+        </div>
+      </div>
+    </div>
+    <div class="col span_1_of_2">
+      <h4 class="col span_2_of_2">Выходное напряжение</h4>
+      <div class="col span_1_of_3">
+        <label for="mainvolt_low" class="col span_2_of_2">Нижняя граница</label>
+        <div class="col span_2_of_2">
+          <select  class="form-control" name="mainvolt_low" id="mainvolt_low"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+      <label for="mainvolt_high" class="col span_2_of_2">Верхняя граница</label>
+      <div class="col span_2_of_2">
+        <select  class="form-control" name="mainvolt_high" id="mainvolt_high"></select>
+      </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="mainvolt_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="mainvolt_hist"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
 <div id="service" class="tabpane">
   <div class="panel-heading">Сервис</div>
   <div class="panel-body section group">
@@ -327,7 +390,9 @@ DYN_WEB.Tabs.setup({
   useCookies: true // optional
 });
 var ntpservipValue;
-
+var temp_hist     = new SpinBox('temp_hist', {'name':'temp_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': 0.5});
+var loadvolt_hist = new SpinBox('loadvolt_hist', {'name':'loadvolt_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': 0.5});
+var mainvolt_hist = new SpinBox('mainvolt_hist', {'name':'mainvolt_hist','minimum':10,'maximum':30,'step':1});
 settingsGET();
 // $('dev-update').onclick = function(){
 //   $('count-wrap').style.display = 'block';

+ 30 - 1
web_interface/src/wui-8/main.js

@@ -794,10 +794,19 @@ function checkOther(ele) {
 }
 
 // ################################################################################
-
+function fillTempRange(id, min, max) {
+  var obj = $(id);
+  for (var i = min; i <= max; i++) {
+    var opt = document.createElement('option');
+    opt.value = i;
+    opt.text = i;
+    obj.appendChild(opt);
+  }
+}
 function settingsGET(){
   getJSON('settings.cgi', function(data) {
     //SNMP params
+    //
     $('managerIP').value = data.managerIP;
     $('read_community').value = data.read_community;
     $('write_community').value = data.write_community;
@@ -810,6 +819,26 @@ function settingsGET(){
     $('gw').value       = data.gw;
     $('mask').value     = data.mask;
     $('dhcp').checked   = data.dhcp;
+    // Alarms
+    fillTempRange('temp_high', 40, 80);
+    fillTempRange('temp_low', -40, 0);
+    fillTempRange('loadvolt_high', 60, 90);
+    fillTempRange('mainvolt_low', 110, 180);
+    fillTempRange('mainvolt_high', 120, 130);
+    $('temp_high').value = data.temp_high;
+    $('temp_low').value = data.temp_low;
+    temp_hist     = new SpinBox('temp_hist', {
+      'name':'temp_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': data.temp_hist
+    });
+    $('loadvolt_high').value = data.loadvolt_high;
+    loadvolt_hist = new SpinBox('loadvolt_hist', {
+      'name':'loadvolt_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': data.loadvolt_hist
+    });
+    $('mainvolt_low').value = data.mainvolt_low;
+    $('mainvolt_high').value = data.mainvolt_high;
+    mainvolt_hist = new SpinBox('mainvolt_hist', {
+      'name':'mainvolt_hist','minimum':10,'maximum':30,'step':1, 'value': data.mainvolt_hist
+    });
     // Whitelist
     $('wtl_ip1').value = data.wtl_ip1;
     $('wtl_ip2').value = data.wtl_ip2;

+ 8 - 0
web_interface/src/wui-8/settings.cgi

@@ -24,6 +24,14 @@ JSON = {
   "tempcomp_k_buf":3,
   "tempcomp_k_cycle":4,
   "batsym":False,
+  "temp_high": 45,
+  "temp_low": -10,
+  "temp_hist": 0.5,
+  "loadvolt_high": 65,
+  "loadvolt_hist": 0.5,
+  "mainvolt_low": 112,
+  "mainvolt_high": 120,
+  "mainvolt_hist": 12,
   "ups_cell_min": 1.5,
   "ups_cell_max": 2.3,
   "di1":1,

+ 68 - 2
web_interface/src/wui-8/settings.html

@@ -52,6 +52,7 @@
     <li><a href="#snmpt" class="activeTab">SNMP</a></li>
     <li><a href="#ups">Аккумуляторы</a></li>
     <li><a href="#netw">Сетевые параметры</a></li>
+    <li><a href="#alarms">Аварии</a></li>
     <li><a href="#whitelist">Белый список</a></li>
     <li><a href="#service">Сервис</a></li>
     <li><a href="#ntpt">Время</a></li>
@@ -173,6 +174,68 @@
     </div>
   </div>
 </div>
+<div id="alarms" class="tabpane">
+  <div class="panel-heading">Аварии</div>
+  <div class="panel-body section group">
+    <div class="col span_1_of_2">
+      <h4 class="col span_3_of_3">Температура</h4>
+      <div class="col span_1_of_3">
+        <label for="temp_low" class="col span_2_of_2">Нижняя граница</label>
+        <div class="col span_2_of_2">
+          <select class="form-control" name="temp_low" id="temp_low"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="temp_high" class="col span_2_of_2">Верхняя граница</label>
+        <div class="col span_2_of_2">
+          <select class="form-control" name="temp_high" id="temp_high"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="temp_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="temp_hist"></span>
+        </div>
+      </div>
+    </div>
+    <div class="col span_1_of_2">
+      <h4 class="col span_2_of_2">Нагрузка</h4>
+      <div class="col span_1_of_2">
+        <label for="loadvolt_high" class="col span_2_of_2">Верхняя граница</label>
+        <div class="col span_2_of_2">
+          <select name="loadvolt_high" id="loadvolt_high" class="form-control"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_2">
+        <label for="loadvolt_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="loadvolt_hist"></span>
+        </div>
+      </div>
+    </div>
+    <div class="col span_1_of_2">
+      <h4 class="col span_2_of_2">Выходное напряжение</h4>
+      <div class="col span_1_of_3">
+        <label for="mainvolt_low" class="col span_2_of_2">Нижняя граница</label>
+        <div class="col span_2_of_2">
+          <select  class="form-control" name="mainvolt_low" id="mainvolt_low"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+      <label for="mainvolt_high" class="col span_2_of_2">Верхняя граница</label>
+      <div class="col span_2_of_2">
+        <select  class="form-control" name="mainvolt_high" id="mainvolt_high"></select>
+      </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="mainvolt_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="mainvolt_hist"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
 <div id="service" class="tabpane">
   <div class="panel-heading">Сервис</div>
   <div class="panel-body section group">
@@ -295,8 +358,11 @@ DYN_WEB.Tabs.setup({
 });
 var ntpservipValue;
 
-var ups_cell_min = new SpinBox('ups_cell_min', {'name':'ups_cell_min','minimum':0,'maximum':3,'step':0.01});
-var ups_cell_max = new SpinBox('ups_cell_max', {'name':'ups_cell_max','minimum':0,'maximum':3,'step':0.01});
+var ups_cell_min  = new SpinBox('ups_cell_min', {'name':'ups_cell_min','minimum':0,'maximum':3,'step':0.01});
+var ups_cell_max  = new SpinBox('ups_cell_max', {'name':'ups_cell_max','minimum':0,'maximum':3,'step':0.01});
+var temp_hist     = new SpinBox('temp_hist', {'name':'temp_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': 0.5});
+var loadvolt_hist = new SpinBox('loadvolt_hist', {'name':'loadvolt_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': 0.5});
+var mainvolt_hist = new SpinBox('mainvolt_hist', {'name':'mainvolt_hist','minimum':10,'maximum':30,'step':1});
 
 settingsGET();
 // $('dev-update').onclick = function(){

+ 86 - 0
web_interface/src/wui/main.css

@@ -2710,3 +2710,89 @@ h4.span_4_of_4 {
   width: 100% !important;
   margin-bottom: 10px;
 }
+
+.spinBox{
+  width: 100%;
+  position      : relative;
+  display       : inline-block;
+  padding-bottom: 5px;
+  -webkit-appearance: none;
+}
+
+.spinBox input{
+  width: 100%;
+  display       : block;
+  /*border        : 0.0625em solid rgb(224,224,224);*/
+  border-right  : none;
+  text-align    : center;
+  -webkit-appearance: none;
+  padding: 8px 7px;
+  font-size: 14px;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.spinBox input:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.spinBox input[disabled]{
+  background : rgb(240,240,240);
+}
+
+.spinBoxUp,
+.spinBoxDown{
+  position         : absolute;
+  display          : block;
+  width            : 2em;
+  height           : 2em;
+  border           : 0.0625em solid rgb(224,224,224);
+  background       : rgb(248,248,248);
+  cursor           : pointer;
+  font-weight: bold;
+}
+
+.spinBox input[disabled] + span,
+.spinBox input[disabled] + span + span{
+  background : rgb(240,240,240);
+  cursor     : auto;
+}
+
+.spinBoxUp span,
+.spinBoxDown span{
+  display : block;
+  width   : 1em;
+  height  : 1em;
+}
+
+.spinBoxUp{
+  text-align: center;
+  line-height: 2em;
+  vertical-align:middle;
+  border-top-right-radius: 5px;
+  border-bottom-right-radius: 5px;
+  right: 0;
+  top: 0;
+}
+.spinBoxDown{
+  vertical-align:middle;
+  line-height: 2em;
+  border-top-left-radius: 5px;
+  border-bottom-left-radius: 5px;
+  text-align: center;
+  top: 0;
+  left: 0;
+}
+.spinboxMark{
+  position: absolute;
+  text-align: center;
+  font-style: italic;
+  padding: 0.3em;
+  height  : 1.59em;
+}

+ 363 - 1
web_interface/src/wui/main.js

@@ -794,7 +794,15 @@ function checkOther(ele) {
 }
 
 // ################################################################################
-
+function fillTempRange(id, min, max) {
+  var obj = $(id);
+  for (var i = min; i <= max; i++) {
+    var opt = document.createElement('option');
+    opt.value = i;
+    opt.text = i;
+    obj.appendChild(opt);
+  }
+}
 function settingsGET(){
   getJSON('settings.cgi', function(data) {
     //SNMP params
@@ -813,6 +821,19 @@ function settingsGET(){
     $('gw').value       = data.gw;
     $('mask').value     = data.mask;
     $('dhcp').checked   = data.dhcp;
+    // Alarms
+    fillTempRange('temp_high', 40, 80);
+    fillTempRange('temp_low', -40, 0);
+    fillTempRange('loadvolt_high', 60, 90);
+    $('temp_high').value = data.temp_high;
+    $('temp_low').value = data.temp_low;
+    temp_hist     = new SpinBox('temp_hist', {
+      'name':'temp_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': data.temp_hist
+    });
+    $('loadvolt_high').value = data.loadvolt_high;
+    loadvolt_hist = new SpinBox('loadvolt_hist', {
+      'name':'loadvolt_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': data.loadvolt_hist
+    });
     // RADIUS params
     $('rs_server').value    = data.rs_server;
     $('rs_port').value      = data.rs_port;
@@ -932,7 +953,348 @@ function infoGet() {
 }
 
 // ################################################################################
+/*
+
+SpinBox.js
+
+Implements a spin box interface for a text field
+
+Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
+the terms of the CC0 1.0 Universal legal code:
+
+http://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+*/
+
+/* Creates a spin box. The parameter is:
+ *
+ * container - either the DOM node to contain the spin box or the ID of the node
+ * options   - an object containing various parameters; this optional parameter
+ *             defaults to the empty object
+ *
+ * The options object may contain the following keys:
+ *
+ * className - a class name to apply to the container; the class name with the
+ *             suffixes 'Up' and 'Down' will be applied to the up and down
+ *             buttons. The default value is 'spinBox'.
+ * value     - the initial value for the spin box. The default is 0 (subject to
+ *             restrictions on the minimum and maximum value) if the input
+ *             element did not already exist, or the existing value if the input
+ *             element did already exist.
+ * step      - the value by which to increment or decrement the value. The
+ *             default value is 1.
+ * minimum   - the minimum allowed value. The default is not to have a minimum
+ *             value.
+ * maximum   - the maximum allowed value. The default is not to have a maximum
+ *             value.
+ * decimals  - the number of decimal places allowed. The default is 0.
+ * name      - input name attribute.
+ *
+ * Note that the minimum, maximum, and decimal places restrictions are enforced
+ * for values set by the spin box, but a value outside of these restrictions may
+ * be typed by the user.
+ */
+function SpinBox(container, options) {
+
+  // fetch the DOM node if a string was supplied
+  if (typeof container == 'string') {
+    container = document.getElementById(container);
+    container.innerHTML = '';
+  }
+
+  // store the options and set the default values
+  this.options = (options ? options : {});
+  if (!('className' in this.options)) this.options.className = 'spinBox';
+  if (!('step'      in this.options)) this.options.step      = 1;
+  if (!('decimals'  in this.options)) this.options.decimals  = 0;
+
+  // check whether the input field should be created
+  var inputs = container.getElementsByTagName('input');
+  if (inputs.length === 0) {
+
+    // create the input node
+    this.input = document.createElement('input');
+    this.input.setAttribute('minimum', this.options.minimum);
+    this.input.setAttribute('maximum', this.options.maximum);
+    this.setValue('value' in this.options ? this.options.value : 0);
+    this.setName('name'   in this.options ? this.options.name : '');
+    container.appendChild(this.input);
+
+  } else {
+
+    // store a reference to the input node
+    this.input = inputs[0];
+    this.setValue(this.options.value ? this.options.value : this.input.value);
+
+  }
+
+  // create the up button
+  var upButton = document.createElement('span');
+  upButton.appendChild(document.createElement('span'));
+  upButton.innerHTML = '+';
+  container.appendChild(upButton);
+
+  // create the down button
+  var downButton = document.createElement('span');
+  downButton.appendChild(document.createElement('span'));
+  downButton.innerHTML = '-';
+  container.appendChild(downButton);
+
+  // apply the classes
+  container.className += ' ' + this.options.className;
+  upButton.className = this.options.className + 'Up';
+  downButton.className = this.options.className + 'Down';
+
+  // add the listeners
+  this.addEventListener(this.input, 'mousewheel',     this.handleMouseWheel, [], true);
+  this.addEventListener(this.input, 'DOMMouseScroll', this.handleMouseWheel, [], true);
+  this.addEventListener(this.input, 'keydown',        this.handleKeyDown, [], true);
+  this.addEventListener(this.input, 'keypress',       this.handleKeyPress, [], true);
+  this.addEventListener(this.input, 'keyup',          this.stop);
+  this.addEventListener(upButton,   'mousedown',      this.start, [true]);
+  this.addEventListener(upButton,   'mouseup',        this.stop);
+  this.addEventListener(upButton,   'mouseout',       this.stop);
+  this.addEventListener(downButton, 'mousedown',      this.start, [false]);
+  this.addEventListener(downButton, 'mouseup',        this.stop);
+  this.addEventListener(downButton, 'mouseout',       this.stop);
+
+}
+
+/* Returns the current value. This will be a number, or the value NaN if the
+ * current contents of the input field do not start with a valid number.
+ */
+SpinBox.prototype.getValue = function () {
+
+  // parse and return the value
+  return parseFloat(this.input.value);
+
+};
+
+SpinBox.prototype.setName = function (name) {
+
+  // asign input name
+  this.input.name = name;
+};
+/* Sets the value. Restrictions on the minimum and maximum value are enforced.
+ * The parameter is:
+ *
+ * value - the value
+ */
+SpinBox.prototype.setValue = function (value) {
+
+  // ensure the value is within the permitted range
+  if ('minimum' in this.options) value = Math.max(this.options.minimum, value);
+  if ('maximum' in this.options) value = Math.min(this.options.maximum, value);
+
+  // store the sign
+  var sign = (value < 0 ? '-' : '');
+  value = Math.abs(value);
+
+  // determine the multiplier for rounding
+  var multiplier = Math.pow(10, this.options.decimals);
+
+  // split the value in to integer and fractional parts
+  value = Math.round(value * multiplier);
+  var integer = (value - value % multiplier) / multiplier;
+  var fractional = '' + value % multiplier;
+
+  // add leading zeros to the fractional part
+  while (fractional.length < this.options.decimals) {
+    fractional = '0' + fractional;
+  }
+
+  // set the value
+  this.input.value =
+      sign + integer + (this.options.decimals > 0 ? '.' + fractional : '');
+
+  // check whether the browser can dispatch events
+  if ('dispatchEvent' in this.input){
+
+    // create the event
+    try{
+      var event = new Event('change', {bubbles : true, cancelable : true});
+    }catch (e){
+      var event = document.createEvent('Event');
+      event.initEvent('change', true, true);
+    }
+
+    // dispatch the event
+    this.input.dispatchEvent(event);
+
+  }
+
+};
+
+/* Adds an event listener to a node. The event listener is bound to the current
+ * value of 'this'. The parameters are:
+ *
+ * node         - the node
+ * event        - the event name
+ * listener     - the listener functions
+ * parameters   - an array of additional parameters to pass to the listener;
+ *                these are placed after the event parameter
+ * allowDefault - true if the default action should not be prevented
+ */
+SpinBox.prototype.addEventListener = function (
+node, event, listener, parameters, allowDefault) {
+
+  // store a reference to the 'this' object
+  var thisObject = this;
+
+  // create the bound listener
+  function boundListener(e) {
+
+    // get the event if it is not supplied
+    if (!e) e = window.event;
+
+    // call the listener
+    listener.apply(thisObject, [e].concat(parameters));
+
+    // prevent the default action if necessary
+    if (!allowDefault) {
+      if (e.preventDefault) {
+        e.preventDefault();
+      } else {
+        e.returnValue = false;
+      }
+    }
+
+  }
+
+  // add the event listener
+  if (node.addEventListener) {
+    node.addEventListener(event, boundListener, false);
+  } else {
+    node.attachEvent('on' + event, boundListener);
+  }
+
+};
+
+/* Handles a mouse wheel event by updating the value if the field is active. The
+ * parameter is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleMouseWheel = function (e) {
+
+  // check whether the field is active
+  if (document.activeElement == this.input) {
+
+    // update the value
+    if (e.wheelDelta) {
+      this.start(e, e.wheelDelta > 1);
+    } else if (e.detail) {
+      this.start(e, e.detail < 1);
+    }
+    this.stop();
+
+    // prevent the default action
+    if (e.preventDefault) {
+      e.preventDefault();
+    } else {
+      e.returnValue = false;
+    }
+
+  }
+
+};
+
+/* Handles a key down event by starting updating if appropriate. The parameter
+ * is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleKeyDown = function (e) {
+
+  // if the up or down keys were pressed, start updating
+  if (e.keyCode == 38) this.start(e, true);
+  if (e.keyCode == 40) this.start(e, false);
+
+};
+
+/* Handles a key press event by filtering out invalid characters. The parameter
+ * is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleKeyPress = function (e) {
+
+  // determine the character code
+  var charCode = ('charCode' in e ? e.charCode : e.keyCode);
 
+  // allow special key presses
+  if (charCode === 0 || e.altKey || e.ctrlKey || e.metaKey) return;
+
+  // allow a minus sign if the value can be negative
+  if (charCode == 45 && (!('minimum' in this.options) || this.options.minimum < 0)) {
+    return;
+  }
+
+  // allow a decimal point if the value may contain decimals
+  if (charCode == 46 && this.options.decimals > 0) return;
+
+  // allow digits
+  if (charCode >= 48 && charCode <= 57) return;
+
+  // prevent the default action
+  if (e.preventDefault) {
+    e.preventDefault();
+  } else {
+    e.returnValue = false;
+  }
+
+};
+
+/* Starts updating the value. The parameters are:
+ *
+ * e  - the event object
+ * up - true to increment the value, false to decrement the value
+ */
+SpinBox.prototype.start = function (e, up) {
+
+  // if the field is disabled or we are already updating, return immediately
+  if (this.input.disabled || 'timeout' in this) return;
+
+  // set the update step
+  this.updateStep = (up ? this.options.step : -this.options.step);
+
+  // initialise the timeout delay
+  this.timeoutDelay = 500;
+
+  // update the value
+  this.update();
+
+};
+
+// Stops update the value.
+SpinBox.prototype.stop = function () {
+
+  // clear the timeout if it exists
+  if ('timeout' in this) {
+    window.clearTimeout(this.timeout);
+    delete this.timeout;
+  }
+
+};
+
+// Updates the value.
+SpinBox.prototype.update = function () {
+
+  // determine the current value
+  var value = parseFloat(this.input.value);
+  if (isNaN(value)) value = 0;
+
+  // update the value
+  this.setValue(value + this.updateStep);
+
+  // reduce the delay
+  this.timeoutDelay = Math.max(20, Math.floor(this.timeoutDelay * 0.9));
+
+  // call this function again
+  var thisObject = this;
+  this.timeout = window.setTimeout(function () { thisObject.update(); }, this.timeoutDelay);
+
+};
 
 /*
   This code is from Dynamic Web Coding at dyn-web.com

+ 8 - 0
web_interface/src/wui/settings.cgi

@@ -16,6 +16,14 @@ JSON = {
   "batcap":7,
   "batcharge_volt":2.275,
   "tempcomp":False,
+  "temp_high": 45,
+  "temp_low": -10,
+  "temp_hist": 0.5,
+  "loadvolt_high": 65,
+  "loadvolt_hist": 0.5,
+  "mainvolt_low": 112,
+  "mainvolt_high": 120,
+  "mainvolt_hist": 12,
   "tempcomp_k_buf":3,
   "tempcomp_k_cycle":4,
   "batsym":False,

+ 44 - 1
web_interface/src/wui/settings.html

@@ -66,6 +66,7 @@
     <li><a href="#snmpt" class="activeTab">SNMP</a></li>
     <li><a href="#inout">Сухие контакты</a></li>
     <li><a href="#netw">Сетевые параметры</a></li>
+    <li><a href="#alarms">Аварии</a></li>
     <li><a href="#service">Сервис</a></li>
     <li><a href="#raduis">Безопасность</a></li>
     <li><a href="#ntpt">Время</a></li>
@@ -192,6 +193,47 @@
     </div>
   </div>
 </div>
+<div id="alarms" class="tabpane">
+  <div class="panel-heading">Аварии</div>
+  <div class="panel-body section group">
+    <div class="col span_1_of_2">
+      <h4 class="col span_3_of_3">Температура</h4>
+      <div class="col span_1_of_3">
+        <label for="temp_low" class="col span_2_of_2">Нижняя граница</label>
+        <div class="col span_2_of_2">
+          <select class="form-control" name="temp_low" id="temp_low"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="temp_high" class="col span_2_of_2">Верхняя граница</label>
+        <div class="col span_2_of_2">
+          <select class="form-control" name="temp_high" id="temp_high"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_3">
+        <label for="temp_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="temp_hist"></span>
+        </div>
+      </div>
+    </div>
+    <div class="col span_1_of_2">
+      <h4 class="col span_2_of_2">Нагрузка</h4>
+      <div class="col span_1_of_2">
+        <label for="loadvolt_high" class="col span_2_of_2">Верхняя граница</label>
+        <div class="col span_2_of_2">
+          <select name="loadvolt_high" id="loadvolt_high" class="form-control"></select>
+        </div>
+      </div>
+      <div class="col span_1_of_2">
+        <label for="loadvolt_hist" class="col span_2_of_2">Гистерезис</label>
+        <div class="col span_2_of_2">
+          <span id="loadvolt_hist"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
 <div id="service" class="tabpane">
   <div class="panel-heading">Сервис</div>
   <div class="panel-body section group">
@@ -359,7 +401,8 @@ DYN_WEB.Tabs.setup({
   useCookies: true // optional
 });
 var ntpservipValue;
-
+var temp_hist     = new SpinBox('temp_hist', {'name':'temp_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': 0.5});
+var loadvolt_hist = new SpinBox('loadvolt_hist', {'name':'loadvolt_hist','minimum':0.5,'maximum':2.0,'step':0.1,'decimals':1, 'value': 0.5});
 settingsGET();
 // $('dev-update').onclick = function(){
 //   $('count-wrap').style.display = 'block';