378 lines
10 KiB
JavaScript
378 lines
10 KiB
JavaScript
/**
|
|
* Entity handling, parsing & validation
|
|
*
|
|
* @author Justin Otherguy <justin@justinotherguy.org>
|
|
* @author Steffen Vogel <info@steffenvogel.de>
|
|
* @copyright Copyright (c) 2011, The volkszaehler.org project
|
|
* @package default
|
|
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
|
*/
|
|
/*
|
|
* This file is part of volkzaehler.org
|
|
*
|
|
* volkzaehler.org is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License, or any later version.
|
|
*
|
|
* volkzaehler.org 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 General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* Entity constructor
|
|
* @todo add validation
|
|
*/
|
|
var Entity = function(json) {
|
|
this.parseJSON(json);
|
|
|
|
if (this.active === undefined) {
|
|
this.active = true; // activate by default
|
|
}
|
|
|
|
|
|
};
|
|
|
|
Entity.prototype.parseJSON = function(json) {
|
|
$.extend(true, this, json);
|
|
|
|
if (this.children) {
|
|
for (var i = 0; i < this.children.length; i++) {
|
|
this.children[i] = new Entity(this.children[i]);
|
|
}
|
|
|
|
this.children.sort(Entity.compare);
|
|
}
|
|
|
|
if (this.type !== undefined) {
|
|
this.definition = vz.capabilities.definitions.get('entities', this.type);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Query middleware for details
|
|
*/
|
|
Entity.prototype.loadDetails = function() {
|
|
return vz.load({
|
|
url: this.middleware,
|
|
controller: 'entity',
|
|
identifier: this.uuid,
|
|
context: this,
|
|
success: function(json) {
|
|
this.parseJSON(json.entity);
|
|
}
|
|
});
|
|
};
|
|
|
|
Entity.prototype.loadData = function() {
|
|
return vz.load({
|
|
controller: 'data',
|
|
url: this.middleware,
|
|
identifier: this.uuid,
|
|
context: this,
|
|
data: {
|
|
from: Math.floor(vz.options.plot.xaxis.min),
|
|
to: Math.ceil(vz.options.plot.xaxis.max),
|
|
tuples: vz.options.tuples
|
|
},
|
|
success: function(json) {
|
|
this.data = json.data;
|
|
|
|
if (this.data.count > 0) {
|
|
if (this.data.min[1] < vz.options.plot.yaxis.min) { // allow negative values for temperature sensors
|
|
vz.options.plot.yaxis.min = null;
|
|
}
|
|
}
|
|
|
|
this.updateDOMRow();
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Show and edit entity details
|
|
*/
|
|
Entity.prototype.showDetails = function() {
|
|
$('<div>')
|
|
.addClass('details')
|
|
.append(this.getDOMDetails())
|
|
.dialog({
|
|
title: 'Details für ' + this.title,
|
|
width: 480,
|
|
resizable: false
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Show from for new Channel
|
|
*
|
|
* @todo implement/test
|
|
*/
|
|
Entity.prototype.getDOMDetails = function(edit) {
|
|
var table = $('<table><thead><tr><th>Eigenschaft</th><th>Wert</th></tr></thead></table>');
|
|
var data = $('<tbody>');
|
|
|
|
// general properties
|
|
var general = ['uuid', 'middleware', 'type', 'color', 'cookie'];
|
|
var sections = ['required', 'optional'];
|
|
|
|
general.each(function(index, property) {
|
|
switch(property) {
|
|
case 'type':
|
|
var title = 'Typ';
|
|
var value = this.definition.translation[vz.options.language];
|
|
break;
|
|
|
|
case 'middleware':
|
|
var title = 'Middleware';
|
|
var value = '<a href="' + this.middleware + '/capabilities.json">' + this.middleware + '</a>';
|
|
break;
|
|
|
|
case 'uuid':
|
|
var title = 'UUID';
|
|
var value = '<a href="' + this.middleware + '/entity/' + this.uuid + '.json">' + this.uuid + '</a>';
|
|
break;
|
|
|
|
case 'color':
|
|
var title = 'Farbe';
|
|
var value = '<span style="background-color: ' + this.color + '">' + this.color + '</span>';
|
|
break;
|
|
|
|
case 'cookie':
|
|
var title = 'Cookie';
|
|
value = '<img src="images/' + ((this.cookie) ? 'tick' : 'cross') + '.png" alt="' + ((value) ? 'ja' : 'nein') + '" />';
|
|
break;
|
|
case 'active':
|
|
var title = 'Aktiv';
|
|
var value = '<img src="images/' + ((this.active) ? 'tick' : 'cross') + '.png" alt="' + ((this.active) ? 'ja' : 'nein') + '" />';
|
|
break;
|
|
}
|
|
|
|
data.append($('<tr>')
|
|
.addClass('property')
|
|
.addClass('general')
|
|
.append($('<td>')
|
|
.addClass('key')
|
|
.text(title)
|
|
)
|
|
.append($('<td>')
|
|
.addClass('value')
|
|
.append(value)
|
|
)
|
|
);
|
|
}, this);
|
|
|
|
sections.each(function(index, section) {
|
|
this.definition[section].each(function(index, property) {
|
|
if (this.hasOwnProperty(property)) {
|
|
var definition = vz.capabilities.definitions.get('properties', property);
|
|
var title = definition.translation[vz.options.language];
|
|
var value = this[property];
|
|
|
|
if (definition.type == 'boolean') {
|
|
value = '<img src="images/' + ((value) ? 'tick' : 'cross') + '.png" alt="' + ((value) ? 'ja' : 'nein') + '" />';
|
|
}
|
|
|
|
if (property == 'cost') {
|
|
value = (value * 1000 * 100) + ' ct/k' + this.definition.unit + 'h'; // ct per kWh
|
|
}
|
|
|
|
data.append($('<tr>')
|
|
.addClass('property')
|
|
.addClass(section)
|
|
.append($('<td>')
|
|
.addClass('key')
|
|
.text(title)
|
|
)
|
|
.append($('<td>')
|
|
.addClass('value')
|
|
.append(value)
|
|
)
|
|
);
|
|
}
|
|
}, this);
|
|
}, this);
|
|
return table.append(data);
|
|
};
|
|
|
|
Entity.prototype.getDOMRow = function(parent) {
|
|
var row = $('<tr>')
|
|
.addClass((parent) ? 'child-of-entity-' + parent.uuid : '')
|
|
.addClass((this.definition.model == 'Volkszaehler\\Model\\Aggregator') ? 'aggregator' : 'channel')
|
|
.addClass('entity')
|
|
.attr('id', 'entity-' + this.uuid)
|
|
.append($('<td>')
|
|
.addClass('visibility')
|
|
.css('background-color', this.color)
|
|
.append($('<input>')
|
|
.attr('type', 'checkbox')
|
|
.attr('checked', this.active)
|
|
.bind('change', this, function(event) {
|
|
var state = $(this).attr('checked');
|
|
|
|
event.data.each(function(entity, parent) {
|
|
$('#entity-' + entity.uuid + ((parent) ? '.child-of-entity-' + parent.uuid : '') + ' input[type=checkbox]')
|
|
.attr('checked', state);
|
|
entity.active = state;
|
|
});
|
|
|
|
vz.wui.drawPlot();
|
|
})
|
|
)
|
|
)
|
|
.append($('<td>').addClass('expander'))
|
|
.append($('<td>')
|
|
.append($('<span>')
|
|
.text(this.title)
|
|
.addClass('indicator')
|
|
.css('background-image', 'url(images/types/' + this.definition.icon + ')')
|
|
)
|
|
)
|
|
.append($('<td>').text(this.definition.translation[vz.options.language])) // channel type
|
|
.append($('<td>').addClass('min')) // min
|
|
.append($('<td>').addClass('max')) // max
|
|
.append($('<td>').addClass('average')) // avg
|
|
.append($('<td>').addClass('last')) // last value
|
|
.append($('<td>').addClass('consumption')) // consumption
|
|
.append($('<td>').addClass('cost')) // costs
|
|
.append($('<td>') // operations
|
|
.addClass('ops')
|
|
.append($('<input>')
|
|
.attr('type', 'image')
|
|
.attr('src', 'images/information.png')
|
|
.attr('alt', 'details')
|
|
.bind('click', this, function(event) {
|
|
event.data.showDetails();
|
|
})
|
|
)
|
|
)
|
|
.data('entity', this);
|
|
|
|
if (this.cookie) {
|
|
$('td.ops', row).prepend($('<input>')
|
|
.attr('type', 'image')
|
|
.attr('src', 'images/delete.png')
|
|
.attr('alt', 'delete')
|
|
.bind('click', this, function(event) {
|
|
vz.entities.remove(event.data);
|
|
vz.entities.saveCookie();
|
|
vz.entities.showTable();
|
|
vz.wui.drawPlot();
|
|
})
|
|
);
|
|
}
|
|
|
|
return row;
|
|
};
|
|
|
|
Entity.prototype.updateDOMRow = function() {
|
|
var row = $('#entity-' + this.uuid);
|
|
|
|
var delta = this.data.to - this.data.from;
|
|
var year = 365*24*60*60*1000;
|
|
|
|
if (this.data.count > 0) { // update statistics if data available
|
|
$('.min', row)
|
|
.text(vz.wui.formatNumber(this.data.min[1], true) + this.definition.unit)
|
|
.attr('title', $.plot.formatDate(new Date(this.data.min[0]), '%d. %b %y %h:%M:%S', vz.options.plot.xaxis.monthNames));
|
|
$('.max', row)
|
|
.text(vz.wui.formatNumber(this.data.max[1], true) + this.definition.unit)
|
|
.attr('title', $.plot.formatDate(new Date(this.data.max[0]), '%d. %b %y %h:%M:%S', vz.options.plot.xaxis.monthNames));
|
|
$('.average', row)
|
|
.text(vz.wui.formatNumber(this.data.average, true) + this.definition.unit);
|
|
$('.last', row)
|
|
.text(vz.wui.formatNumber(this.data.tuples.last()[1], true) + this.definition.unit);
|
|
|
|
if (this.definition.interpreter == 'Volkszaehler\\Interpreter\\MeterInterpreter') { // sensors have no consumption
|
|
$('.consumption', row)
|
|
.text(vz.wui.formatNumber(this.data.consumption, true) + this.definition.unit + 'h')
|
|
.attr('title', vz.wui.formatNumber(this.data.consumption * (year/delta), true) + this.definition.unit + 'h' + '/Jahr');
|
|
}
|
|
|
|
if (this.cost) {
|
|
$('.cost', row)
|
|
.text(vz.wui.formatNumber(this.cost * this.data.consumption) + ' €')
|
|
.attr('title', vz.wui.formatNumber(this.cost * this.data.consumption * (year/delta)) + ' €/Jahr');
|
|
}
|
|
}
|
|
else { // no data available, clear table
|
|
$('.min', row).text('').attr('title', '');
|
|
$('.max', row).text('').attr('title', '');
|
|
$('.average', row).text('');
|
|
$('.last', row).text('');
|
|
$('.consumption', row).text('');
|
|
$('.cost', row).text('');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add entity as child
|
|
*/
|
|
Entity.prototype.addChild = function(child) {
|
|
if (this.definition.model != 'Volkszaehler\\Model\\Aggregator') {
|
|
throw new Exception('EntityException', 'Entity is not an Aggregator');
|
|
}
|
|
|
|
return vz.load({
|
|
controller: 'group',
|
|
identifier: this.uuid,
|
|
url: this.middleware,
|
|
data: {
|
|
uuid: child.uuid
|
|
},
|
|
type: 'post'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove entity from children
|
|
*/
|
|
Entity.prototype.removeChild = function(child) {
|
|
return vz.load({
|
|
controller: 'group',
|
|
identifier: this.uuid,
|
|
url: this.middleware,
|
|
data: {
|
|
uuid: child.uuid,
|
|
operation: 'delete'
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Calls the callback function for the entity and all nested children
|
|
*
|
|
* @param cb callback function
|
|
*/
|
|
Entity.prototype.each = function(cb, recursive) {
|
|
if (this.children) {
|
|
for (var i = 0; i < this.children.length; i++) {
|
|
cb(this.children[i], this);
|
|
|
|
if (recursive) {
|
|
this.children[i].each(cb, true); // call recursive
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Compares two entities for sorting
|
|
*
|
|
* @static
|
|
* @todo Channels before Aggregators
|
|
*/
|
|
Entity.compare = function(a, b) {
|
|
if (a.definition.model == 'Volkszaehler\\Model\\Channel' && // Channels before Aggregators
|
|
b.definition.model == 'Volkszaehler\\Model\\Aggregator')
|
|
{
|
|
return 1;
|
|
}
|
|
else {
|
|
return ((a.title < b.title) ? -1 : ((a.title > b.title) ? 1 : 0));
|
|
}
|
|
}
|