its going on.. (unfortunatly with some smaller bugs)

This commit is contained in:
Steffen Vogel 2010-10-01 11:23:31 +02:00
parent 261fa6f768
commit 95fb4f0b71
22 changed files with 268 additions and 148 deletions

View file

@ -44,7 +44,7 @@ class AggregatorController extends EntityController {
return $aggregator;
}
else {
throw new \Exception($identifier . ' is not a group uuid');
throw new \Exception($identifier . ' is not a group');
}
}

View file

@ -44,7 +44,7 @@ class ChannelController extends EntityController {
return $channel;
}
else {
throw new \Exception($identifier . ' is not a channel uuid');
throw new \Exception($identifier . ' is not a channel');
}
}

View file

@ -23,8 +23,8 @@
namespace Volkszaehler\View;
use Doctrine\ORM\Query\AST\Functions;
use Volkszaehler\Interpreter;
use Volkszaehler\View\HTTP;
use Volkszaehler\Util;
use Volkszaehler\Model;
@ -67,7 +67,7 @@ class JSON extends View {
$json = $this->json->encode((Util\Debug::isActivated()) ? JSON_PRETTY : 0);
if ($this->padding) {
$json = 'if (self.' . $this->padding . ') { ' . $this->padding . '(' . $json . '); }';
$json = 'if (' . $this->padding . ') { ' . $this->padding . '(' . $json . '); }';
}
$this->response->setHeader('Content-type', 'application/json');
@ -79,18 +79,13 @@ class JSON extends View {
*
* @param Model\Channel $channel
*/
protected function addChannel(Model\Channel $channel) {
$this->json['channel'] = self::convertEntity($channel);
}
/**
* Add aggregator to output queue
*
* @param Model\Aggregator $aggregator
* @param boolean $recursive
*/
protected function addAggregator(Model\Aggregator $aggregator, $recursive = FALSE) {
$this->json['group'] = self::convertAggregator($aggregator, $recursive);
protected function addEntity(Model\Entity $entity) {
if ($entity instanceof Model\Aggregator) {
$this->json['entity'] = self::convertAggregator($entity);
}
else {
$this->json['entity'] = self::convertEntity($entity);
}
}
/**
@ -198,12 +193,11 @@ class JSON extends View {
$jsonAggregator = self::convertEntity($aggregator);
foreach ($aggregator->getChildren() as $entity) {
if ($entity instanceof Model\Channel) {
$jsonAggregator['channels'][] = self::convertEntity($entity);
$jsonAggregator['children'][] = self::convertEntity($entity);
}
elseif ($entity instanceof Model\Aggregator) {
$jsonAggregator['groups'][] = self::convertAggregator($entity);
$jsonAggregator['children'][] = self::convertAggregator($entity);
}
}

View file

@ -110,11 +110,8 @@ abstract class View {
if ($data instanceof Interpreter\InterpreterInterface) {
$this->addData($data);
}
elseif ($data instanceof Model\Channel) {
$this->addChannel($data);
}
elseif ($data instanceof Model\Aggregator) {
$this->addAggregator($data);
elseif ($data instanceof Model\Entity) {
$this->addEntity($data);
}
elseif ($data instanceof \Exception) {
$this->addException($data);
@ -131,8 +128,7 @@ abstract class View {
protected abstract function render();
protected abstract function addData(Interpreter\InterpreterInterface $data);
protected abstract function addChannel(Model\Channel $channel);
protected abstract function addAggregator(Model\Aggregator $aggregator);
protected abstract function addEntity(Model\Entity $entity);
protected abstract function addException(\Exception $exception);
protected abstract function addDebug(Util\Debug $debug);
}

BIN
frontend/images/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

BIN
frontend/images/cookie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
frontend/images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

BIN
frontend/images/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

BIN
frontend/images/server.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

BIN
frontend/images/shading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

View file

@ -18,6 +18,7 @@
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.barRenderer.min.js"></script>
<script type="text/javascript" src="javascripts/jqplot/plugins/jqplot.highlighter.min.js"></script>
<script type="text/javascript" src="javascripts/json2.js"></script>
<script type="text/javascript" src="javascripts/functions.js"></script>
<script type="text/javascript" src="javascripts/script.js"></script>
@ -29,38 +30,50 @@
<body>
<div id="vz">
<div id="content">
<div id="plot"></div>
<table id="move">
<tr>
<td><input type="button" onclick="moveWindow('back')" value="&laquo;" /></td>
<td><input type="button" onclick="moveWindow('last')" value="&raquo;&raquo;" /></td>
<td><input type="button" onclick="moveWindow('forward')" value="&raquo;" /></td>
<td><input type="image" src="images/control_rewind_blue.png" onclick="moveWindow('back')" value="&laquo;" /></td>
<td>
<input type="image" src="images/control_end_blue.png" onclick="moveWindow('last')" value="&raquo;&raquo;" />
<input type="image" src="images/arrow_refresh.png" onclick="loadData()" alt="refresh" />
</td>
<td><input type="image" src="images/control_fastforward_blue.png" onclick="moveWindow('forward')" value="&raquo;" /></td>
</tr>
</table>
<div id="accordion">
<h3>Kanäle</h3>
<table id="entities">
<div id="entities">
<table>
<thead>
<tr>
<th>UUID</th>
<th>Title</th>
<th>Titel</th>
<th>Typ</th>
<th>Operationen</th>
<th>Anzeige</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div id="addEntity"><input type="text" size="36" maxlength="36" name="uuid" value="UUID" /> <button><img src="images/add.png" alt="add" />Kanal hinzuf&uuml;gen</button></div>
</div>
<h3>Optionen</h3>
<div id="options">
<table>
<tr><td><label for="refresh">Automatisch aktualisieren</label></td><td><input type="checkbox" name="refresh" /><div id="refreshInterval"></div></td></tr>
<tr><td><label for="refresh"><img src="images/arrow_refresh.png" alt="refresh" /> Automatisch aktualisieren</label></td><td><input type="checkbox" name="refresh" /></td></tr>
<tr><td><label for="trendline"><img src="images/chart_line.png" alt="trendline" /> Trendline anzeigen</label></td><td><input type="checkbox" name="trendline" /></td></tr>
<tr><td><label for="backend"><img src="images/server.png" alt="backendUrl" /> Backend-Adresse</label></td><td><input type="text" name="backendUrl" /></td></tr>
<tr><td><label for="tuples"><img src="images/shading.png" alt="tuples" /> Tuples (Auflösung)</label></td><td><input type="text" name="tuples" /></td></tr>
</table>
</div>
</div>
</div>
<div id="footer">
&copy; 2010 - <a href="http://volkszaehler.org/">volkszaehler.org</a> - the open smartmeter platform
</div>
</body>
</html>

View file

@ -32,9 +32,9 @@
/**
* Refresh plot with new data
*/
function refresh() {
if ($('[name=refresh]').attr('checked')) {
getData();
function refreshWindow() {
if ($('input[name=refresh]').attr('checked')) {
loadData();
}
}
@ -53,24 +53,25 @@ function moveWindow(mode) {
myWindowEnd += delta;
}
getData();
loadData();
}
function getData() {
// load json data with given time window
$.getJSON(backendUrl + '/data/' + myUUID + '.json', { from: myWindowStart, to: myWindowEnd, tuples: 500 }, function(data){
json = data;
showChart();
//load json data with given time window
function loadData() {
eachRecursive(entities, function(entity, parent) {
if (entity.active && entity.type != 'group') {
$.getJSON(backendUrl + '/data/' + entity.uuid + '.json', { from: myWindowStart, to: myWindowEnd, tuples: tuples }, ajaxWait(function(json) {
entity.data = json.data[0]; // TODO filter for correct uuid
}, showChart, 'data'));
}
});
return false;
}
function showChart() {
var jqData = new Array();
$.each(json.data, function(index, value) {
jqData.push(value.tuples);
eachRecursive(entities, function(entity, parent) {
jqData.push(entity.data.tuples);
});
// TODO read docs
@ -86,11 +87,13 @@ function showChart() {
*/
/**
* Get all entity infomration from backend
* Get all entity information from backend
*/
function loadEntities() {
function loadEntities(uuids) {
$.each(uuids, function(index, value) {
$.getJSON(backendUrl + '/entity/' + value + '.json', ajaxWait(showEntities, 'enities'));
$.getJSON(backendUrl + '/entity/' + value + '.json', ajaxWait(function(json) {
entities.push(json.entity);
}, showEntities, 'information'));
});
}
@ -98,72 +101,101 @@ function loadEntities() {
* Create nested entity list
* @param data
*/
function showEntities(data) {
function showEntities() {
$('#entities tbody').empty();
$.each(data, function(index, value) {
var entity = (value.group) ? value.group : value.channel;
var i = 0;
eachRecursive(entities, function(entity, parent) {
entity.active = true; // TODO active by default or via backend property?
entity.color = colors[i++%colors.length];
showEntity(entity);
$('#entities tbody').append(
$('<tr>')
.addClass((parent) ? 'child-of-entity-' + parent.uuid : '')
.attr('id', 'entity-' + entity.uuid)
.append(
$('<td>').append(
$('<span>')
.addClass((entity.type == 'group') ? 'group' : 'channel')
.attr('title', entity.uuid)
.text(entity.title)
)
)
.append($('<td>').text(entity.type))
.append($('<td>') // operations
.append($('<input>')
.attr('type', 'image')
.attr('src', 'images/delete.png')
.attr('alt', 'delete')
.bind('click', entity, function(event) { alert('delete: ' + event.data.uuid); })
)
)
.append($('<td>')
.append($('<div>')
.css('background-color', entity.color)
.addClass('indicator')
.append($('<input>')
.attr('type', 'checkbox')
.attr('checked', entity.active)
.bind('change', entity, function(event) {
event.data.active = $(this).attr('checked');
loadData();
})
)
)
)
);
});
$('#entities').treeTable();
}
/**
* Create nested entity list (recursive)
* @param entity
* @param parent
*/
function showEntity(entity, parent) {
$('#entities tbody').append(
$('<tr>')
.attr('class', (parent) ? 'child-of-entity-' + parent.uuid : '')
.attr('id', 'entity-' + entity.uuid)
.append($('<td>').text(entity.uuid))
.append($('<td>').text(entity.title))
.append($('<td>').text(entity.type))
);
var entities = new Array();
if (entity.channels) {
$.merge(entities, entity.channels);
}
if (entity.groups) {
$.merge(entities, entity.groups);
}
// http://ludo.cubicphuse.nl/jquery-plugins/treeTable/doc/index.html
$('#entities table').treeTable();
$.each(entities, function(index, value) {
showEntity(value, entity);
});
// load data and show plot
loadData();
}
/*
* General helper functions
*/
function ajaxWait(callback, identifier) {
if (!identifier) {
var identifier = 0;
}
if (!ajaxWait.counter || !ajaxWait.data) {
ajaxWait.counter = new Array();
ajaxWait.data = new Array();
}
if (!ajaxWait.counter[identifier] || !ajaxWait.data[identifier]) {
ajaxWait.counter[identifier] = 0;
ajaxWait.data[identifier] = new Array;
}
function ajaxWait(callback, finished, identifier) {
if (!ajaxWait.counter) { ajaxWait.counter = new Array(); }
if (!ajaxWait.counter[identifier]) { ajaxWait.counter[identifier] = 0; }
ajaxWait.counter[identifier]++;
return function (data, textStatus) {
ajaxWait.data[identifier].push(data);
callback(data, textStatus);
if (!--ajaxWait.counter[identifier]) {
callback(ajaxWait.data[identifier]);
finished();
}
};
}
}
function eachRecursive(array, callback, parent) {
$.each(array, function(index, value) {
callback(value, parent);
if (value.children) { // has children?
eachRecursive(value.children, callback, value); // call recursive
}
});
}
Array.prototype.contains = function(needle) {
for (var i=0; i<this.length; i++) {
if (this[i] == needle) {
return true;
}
}
return false;
};
Array.prototype.diff = function(compare) {
return this.filter(function(elem) {
return !compare.contains(elem);
});
};

View file

@ -0,0 +1,38 @@
/*
* JSON parser and stringifier
*
* http://www.JSON.org/json2.js (2010-08-25)
*
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*
* See http://www.JSON.org/js.html
*/
if(!this.JSON){this.JSON={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}}());

View file

@ -26,11 +26,12 @@
*/
/*
* Constants
* Constants & settings
*/
const backendUrl = '../backend/index.php';
const jqOptions = {
title: 'volkszaehler.org',
var backendUrl = '../backend/index.php';
var tuples = 300;
var colors = ['#83CAFF', '#7E0021', '#579D1C', '#FFD320', '#FF420E', '#004586', '#0084D1', '#C5000B', '#FF950E', '#4B1F6F', '#AECF00', '#314004'];
var jqOptions = {
series: [],
cursor: {
zoom: true,
@ -43,7 +44,11 @@ const jqOptions = {
showLine: false,
markerOptions: {
style: 'dash',
shadow: false,
size: 2
},
trendline: {
shadow: false
}
},
axes: {
@ -73,59 +78,50 @@ const jqOptions = {
}
};
// uuids
var myUUID = '';
// storing entities
var entities = new Array;
var uuids = new Array;
if ($.getCookie('uuids')) {
var uuids = $.parseJSON($.getCookie('uuids'));
}
if($.getUrlVar('uuid')) {
myUUID = $.getUrlVar('uuid');
uuids.push($.getUrlVar('uuid'));
}
if (uuids.length == 0) {
alert('Error: No UUIDs given!')
}
// storing json data
var json;
// windowEnd parameter for json server
var myWindowEnd = new Date().getTime();
// windowStart parameter for json server
// windowStart parameter for json server (last 24 hours)
var myWindowStart = myWindowEnd - 24*60*60*1000;
// executed on document loaded complete
// this is where it all starts...
$(document).ready(function() {
// resize chart area for low resolution displays
// works fine with HTC hero
// perhaps you have to reload after display rotation
if($(window).width() < 800) {
$('#chart').animate({
width: $(window).width() - 40,
height: $(window).height() - 3,
}, 0);
// parse uuids from cookie and url
uuids = getUUIDs();
var uuid;
if($.getUrlVar('uuid') && !uuids.contains($.getUrlVar('uuid'))) {
uuids.push($.getUrlVar('uuid'));
$.setCookie('uuids', JSON.stringify(uuids));
}
// load all entity information
loadEntities();
// start auto refresh timer
window.setInterval(refresh, 5000);
window.setInterval(refreshWindow, 5000);
// initialization of user interface
$('#accordion h3').click(function() {
$(this).next().toggle('fast');
return false;
}).next().hide();
$('#refreshInterval').slider();
// add new entity to list
$('#addEntity button').click(function() {
uuids.push($(this).prev().val());
loadEntities(uuids);
});
// load data and show plot
getData();
// options
$('input[name=trendline]').change(function() {
jqOptions.seriesDefaults.trendline.show = $(this).attr('checked');
});
$('input[name=backendUrl]').val(backendUrl);
$('input[name=tuples]').val(tuples);
// load all entity information
loadEntities(uuids);
});

View file

@ -1,12 +1,12 @@
body {
margin: 0;
padding: 0;
background-color: #9ACC67;
background-color: #769AFF;
font-family: sans-serif;
}
#vz {
margin: 150px auto;
#content {
margin: 20px auto;
width: 800px;
padding: 10px;
-moz-border-radius: 10px;
@ -14,31 +14,82 @@ body {
border: 1px solid black;
}
#footer {
text-align: center;
font-size: 0.7em;
color: white;
margin: 10px;
}
#move {
width: 100%;
text-align: center;
margin: 5px;
}
#accordion h3 {
#accordion {
background-color: lightgrey;
padding: 4px;
margin: 2px;
-moz-border-radius: 4px;
}
#accordion h3 {
background-color: grey;
padding: 4px;
margin: 2px 0px;
color: white;
cursor: pointer;
font-size: 0.9em;
-moz-border-radius: 4px;
}
#accordion div {
padding: 5px;
}
/* treeTable for entities */
#entities {
#entities table {
border-collapse: collapse;
width: 100%;
}
#entities thead tr th {
padding: 0.3em 1.67em;
padding: 0.1em 1.67em;
border-bottom: 2px solid grey;
text-align: left;
font-size: 0.9em;
}
#entities tbody tr td {
padding: 0.3em 1.5em;
white-space: nowrap;
padding: 0.1em 1.5em;
border-bottom: 1px solid #A7A7A7;
}
#entities tbody span {
background-position: left center;
background-repeat: no-repeat;
padding: 0.2em 0 0.2em 1.5em;
}
#entities tbody span.channel {
background-image: url("../images/chart_curve.png");
}
#entities tbody span.group {
background-image: url("../images/folder.png");
}
#addEntity {
margin-top: 10px;
}
.indicator {
height: 12px;
}
.inactive {
filter: alpha(opacity=30);
-moz-opacity: 0.3;
-khtml-opacity: 0.3;
opacity: 0.3;
}