improved interpreters
added jpgraph output for debugging adding import script for f10's data (http://github.com/f10/volkszaehler.org) updated 1wire logging script
This commit is contained in:
parent
ea89630f73
commit
5923ae8940
11 changed files with 335 additions and 38 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ share/sql/demo/pulses.dummy.copy
|
|||
.project
|
||||
.buildpath
|
||||
.settings/
|
||||
backend/lib/vendor/*
|
||||
|
|
|
@ -28,11 +28,20 @@ class Data extends Controller {
|
|||
|
||||
$q = $this->em->createQuery('SELECT c FROM Volkszaehler\Model\Channel\Channel c WHERE c.id IN (' . implode(', ', $ids) . ')');
|
||||
$channels = $q->execute();
|
||||
|
||||
|
||||
$from = ($this->view->request->getParameter('from')) ? (int) $this->view->request->getParameter('from') : NULL;
|
||||
$to = ($this->view->request->getParameter('to')) ? (int) $this->view->request->getParameter('to') : NULL;
|
||||
$groupBy = ($this->view->request->getParameter('groupBy')) ? $this->view->request->getParameter('groupBy') : NULL; // get all readings by default
|
||||
|
||||
|
||||
switch ($this->view->request->getParameter('type')) {
|
||||
case 'power':
|
||||
|
||||
case 'consumption':
|
||||
case 'stats':
|
||||
}
|
||||
|
||||
|
||||
foreach ($channels as $channel) {
|
||||
$interpreter = $channel->getInterpreter($this->em);
|
||||
$this->view->add($interpreter->getValues($from, $to, $groupBy));
|
||||
|
|
|
@ -76,14 +76,22 @@ abstract class Interpreter implements InterpreterInterface {
|
|||
|
||||
$sql = 'SELECT';
|
||||
$sql .= ($sqlGroupBy === false) ? ' timestamp, value' : ' MAX(timestamp) AS timestamp, SUM(value) AS value, COUNT(timestamp) AS count';
|
||||
$sql .= ' FROM data WHERE channel_id = ' . (int) $this->channel->getId(); // TODO add time filter
|
||||
$sql .= ' FROM data WHERE channel_id = ' . (int) $this->channel->getId();
|
||||
|
||||
if (!is_null($from)) {
|
||||
$sql .= ' && timestamp > ' . $from;
|
||||
}
|
||||
|
||||
if (!is_null($to)) {
|
||||
$sql .= ' && timestamp < ' . $to;
|
||||
}
|
||||
|
||||
if ($sqlGroupBy !== false) {
|
||||
$sql .= ' GROUP BY ' . $sqlGroupBy;
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY timestamp DESC';
|
||||
|
||||
|
||||
$rsm = new \Doctrine\ORM\Query\ResultsetMapping;
|
||||
$rsm->addScalarResult('timestamp', 'timestamp');
|
||||
$rsm->addScalarResult('value', 'value');
|
||||
|
@ -108,7 +116,7 @@ abstract class Interpreter implements InterpreterInterface {
|
|||
$packages = array();
|
||||
$reading = reset($result);
|
||||
for ($i = 1; $i <= $packageCount; $i++) {
|
||||
$package = array('timestamp' => $reading['timestamp'], // last timestamp in package
|
||||
$package = array('timestamp' => (int) $reading['timestamp'], // last timestamp in package
|
||||
'value' => (float) $reading['value'], // sum of values
|
||||
'count' => ($sqlGroupBy === false) ? 1 : $reading['count']); // total count of values or pulses in the package
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Volkszaehler\Interpreter;
|
|||
|
||||
class Sensor extends Interpreter {
|
||||
|
||||
public function getData($from = NULL, $to = NULL, $groupBy = NULL) {
|
||||
public function getValues($from = NULL, $to = NULL, $groupBy = NULL) {
|
||||
$data = parent::getData($from, $to, $groupBy);
|
||||
|
||||
array_walk($data, function(&$reading) {
|
||||
|
|
|
@ -36,7 +36,10 @@ class Data {
|
|||
*/
|
||||
private $timestamp;
|
||||
|
||||
/** @Column(type="decimal") */
|
||||
/**
|
||||
* @Column(type="decimal", precision="10", scale="5")
|
||||
* @todo change to float after DCC-67 has been closed
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
|
|
100
backend/lib/View/JpGraph.php
Normal file
100
backend/lib/View/JpGraph.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright (c) 2010 by Justin Otherguy <justin@justinotherguy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License (either version 2 or
|
||||
* version 3) as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* For more information on the GPL, please go to:
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
namespace Volkszaehler\View;
|
||||
|
||||
require_once \Volkszaehler\BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph.php';
|
||||
require_once \Volkszaehler\BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph_scatter.php';
|
||||
require_once \Volkszaehler\BACKEND_DIR . '/lib/vendor/JpGraph/jpgraph_date.php';
|
||||
|
||||
|
||||
class JpGraph extends View {
|
||||
protected $width = 800;
|
||||
protected $height = 600;
|
||||
|
||||
protected $plotCount = 0;
|
||||
|
||||
protected static $colors = array('chartreuse', 'chocolate1', 'cyan', 'blue', 'lightcyan4', 'gold');
|
||||
|
||||
protected $graph;
|
||||
|
||||
public function __construct(Http\Request $request, Http\Response $response, $format) {
|
||||
parent::__construct($request, $response);
|
||||
|
||||
$this->graph = new \Graph($this->width,$this->height);
|
||||
|
||||
// Specify what scale we want to use,
|
||||
$this->graph->SetScale('datlin');
|
||||
|
||||
$this->graph->SetMarginColor('white');
|
||||
$this->graph->SetMargin(90,10,18,90);
|
||||
|
||||
$this->graph->SetTickDensity(TICKD_DENSE, TICKD_SPARSE);
|
||||
$this->graph->xaxis->SetFont(FF_ARIAL);
|
||||
$this->graph->yaxis->SetFont(FF_ARIAL);
|
||||
|
||||
$this->graph->xaxis->SetLabelAngle(45);
|
||||
$this->graph->xaxis->SetLabelFormatCallback(function($label) { return date('j.n.y G:i', $label); });
|
||||
|
||||
//$this->graph->img->SetAntiAliasing();
|
||||
}
|
||||
|
||||
public function add($data) {
|
||||
$xData = $yData = array();
|
||||
foreach ($data as $reading) {
|
||||
$xData[] = $reading['timestamp']/1000;
|
||||
$yData[] = $reading['value'];
|
||||
}
|
||||
|
||||
// Create the linear plot
|
||||
$plot = new \ScatterPlot($yData, $xData);
|
||||
|
||||
$plot->mark->SetColor(self::$colors[$this->plotCount]);
|
||||
$plot->mark->SetFillColor(self::$colors[$this->plotCount]);
|
||||
|
||||
$plot->mark->SetType(MARK_DIAMOND);
|
||||
$plot->mark->SetWidth(1);
|
||||
$plot->SetLinkPoints(true, self::$colors[$this->plotCount]);
|
||||
|
||||
$this->plotCount++;
|
||||
|
||||
|
||||
// Add the plot to the graph
|
||||
$this->graph->Add($plot);
|
||||
}
|
||||
|
||||
public function addException(\Exception $e) { echo $e; }
|
||||
public function addDebug() {}
|
||||
|
||||
public static function factory(Http\Request $request, Http\Response $response) {
|
||||
|
||||
}
|
||||
|
||||
public function render() {
|
||||
// Display the graph
|
||||
$this->graph->Stroke();
|
||||
|
||||
$this->response->send();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -34,27 +34,78 @@ class Json extends View {
|
|||
|
||||
$this->json['source'] = 'volkszaehler.org';
|
||||
$this->json['version'] = \Volkszaehler\VERSION;
|
||||
|
||||
|
||||
$this->response->setHeader('Content-type', 'application/json');
|
||||
}
|
||||
|
||||
public function render() {
|
||||
parent::render();
|
||||
|
||||
echo json_encode($this->json);
|
||||
|
||||
echo self::format(json_encode($this->json));
|
||||
}
|
||||
|
||||
protected function addDebug() {
|
||||
|
||||
protected static function format($json) {
|
||||
$tab = "\t";
|
||||
$formatted = '';
|
||||
$indentLevel = 0;
|
||||
$inString = false;
|
||||
|
||||
$len = strlen($json);
|
||||
for($c = 0; $c < $len; $c++) {
|
||||
$char = $json[$c];
|
||||
switch($char) {
|
||||
case '{':
|
||||
case '[':
|
||||
$formatted .= $char;
|
||||
if (!$inString && (ord($json[$c+1]) != ord($char)+2)) {
|
||||
$indentLevel++;
|
||||
$formatted .= "\n" . str_repeat($tab, $indentLevel);
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (!$inString && (ord($json[$c-1]) != ord($char)-2)) {
|
||||
$indentLevel--;
|
||||
$formatted .= "\n" . str_repeat($tab, $indentLevel);
|
||||
}
|
||||
$formatted .= $char;
|
||||
break;
|
||||
case ',':
|
||||
$formatted .= $char;
|
||||
if (!$inString) {
|
||||
$formatted .= "\n" . str_repeat($tab, $indentLevel);
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
$formatted .= $char;
|
||||
if (!$inString) {
|
||||
$formatted .= ' ';
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
if ($c > 0 && $json[$c-1] != '\\') {
|
||||
$inString = !$inString;
|
||||
}
|
||||
default:
|
||||
$formatted .= $char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
public function addDebug() {
|
||||
$config = Util\Registry::get('config');
|
||||
|
||||
|
||||
$this->json['debug'] = array('time' => $this->getTime(),
|
||||
'database' => array('driver' => $config['db']['driver'],
|
||||
'queries' => Util\Debug::getSQLLogger()->queries)
|
||||
);
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
protected function addException(\Exception $exception) {
|
||||
public function addException(\Exception $exception) {
|
||||
$this->json['exception'] = array('type' => get_class($exception),
|
||||
'message' => $exception->getMessage(),
|
||||
'code' => $exception->getCode(),
|
||||
|
|
|
@ -21,7 +21,12 @@
|
|||
|
||||
namespace Volkszaehler\View;
|
||||
|
||||
abstract class View {
|
||||
interface ViewInterface {
|
||||
public function addException(\Exception $e);
|
||||
public function addDebug();
|
||||
}
|
||||
|
||||
abstract class View implements ViewInterface {
|
||||
public $request;
|
||||
protected $response;
|
||||
|
||||
|
@ -43,15 +48,23 @@ abstract class View {
|
|||
* creates new view instance depending on the requested format
|
||||
*/
|
||||
public static function factory(Http\Request $request, Http\Response $response) {
|
||||
$format = ucfirst(strtolower($request->getParameter('format')));
|
||||
$controller = ucfirst(strtolower($request->getParameter('controller')));
|
||||
$format = strtolower($request->getParameter('format'));
|
||||
$controller = strtolower($request->getParameter('controller'));
|
||||
|
||||
$viewClassName = 'Volkszaehler\View\\' . $format . '\\' . $controller;
|
||||
if (!(\Volkszaehler\Util\ClassLoader::classExists($viewClassName)) || !is_subclass_of($viewClassName, '\Volkszaehler\View\View')) {
|
||||
throw new \InvalidArgumentException('\'' . $viewClassName . '\' is not a valid View');
|
||||
if (in_array($format, array('png', 'jpg'))) {
|
||||
$view = new JpGraph($request, $response, $format);
|
||||
}
|
||||
else {
|
||||
$viewClassName = 'Volkszaehler\View\\' . ucfirst($format) . '\\' . ucfirst($controller);
|
||||
|
||||
if (!(\Volkszaehler\Util\ClassLoader::classExists($viewClassName)) || !is_subclass_of($viewClassName, '\Volkszaehler\View\View')) {
|
||||
throw new \InvalidArgumentException('\'' . $viewClassName . '\' is not a valid View');
|
||||
}
|
||||
|
||||
$view = new $viewClassName($request, $response);
|
||||
}
|
||||
|
||||
return new $viewClassName($request, $response);
|
||||
return $view;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
72
share/tools/import_flo.php
Normal file
72
share/tools/import_flo.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/*
|
||||
* Copyright (c) 2010 by Justin Otherguy <justin@justinotherguy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License (either version 2 or
|
||||
* version 3) as published by the Free Software Foundation.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* For more information on the GPL, please go to:
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
/*
|
||||
* simple script to import demo pulses
|
||||
*/
|
||||
|
||||
$sql = '';
|
||||
$pulses = array();
|
||||
|
||||
// initialize db connection
|
||||
mysql_connect('localhost', 'vz', 'demo');
|
||||
mysql_select_db('volkszaehler_doctrine');
|
||||
|
||||
// dump => db channel id mapping
|
||||
$mapping[4] = 22;
|
||||
$mapping[5] = 23;
|
||||
$mapping[9] = 24;
|
||||
$mapping[10] = 25;
|
||||
|
||||
$fd = fopen('/home/steffen/Desktop/testdaten_nicht_veroeffentlichen.sql', 'r');
|
||||
if ($fd) {
|
||||
while (!feof($fd)) {
|
||||
$line = fgets($fd);
|
||||
|
||||
// $matches index 1 2 3 4 5 6 7 8
|
||||
if (preg_match('/^\((\d), \'(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\', (\d)/', $line, $matches)) {
|
||||
|
||||
$ts = mktime($matches[5], $matches[6], $matches[7], $matches[3], $matches[4], $matches[2]) * 1000;
|
||||
$value = $matches[8];
|
||||
$channel = $mapping[$matches[1]];
|
||||
|
||||
if ($ts > 0) {
|
||||
$pulses[] = '(' . $channel . ', ' . $ts . ', ' . $value . ')';
|
||||
}
|
||||
|
||||
if (count($pulses) % 1000 == 0) {
|
||||
$sql = 'INSERT INTO data (channel_id, timestamp, value) VALUES ' . implode(', ', $pulses);
|
||||
if (!mysql_query($sql)){
|
||||
echo mysql_error();
|
||||
}
|
||||
|
||||
echo 'Rows inserted: ' . mysql_affected_rows() . '<br />';
|
||||
|
||||
flush();
|
||||
$pulses = array();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fclose($fd);
|
||||
}
|
||||
|
||||
?>
|
|
@ -23,9 +23,7 @@
|
|||
* simple script to import demo pulses
|
||||
*/
|
||||
|
||||
include '../../backend/init.php';
|
||||
|
||||
$dbh = Database::getConnection();
|
||||
// TODO adapt to doctrine dal or use native mysql
|
||||
|
||||
$sql = '';
|
||||
|
||||
|
@ -42,11 +40,14 @@ if ($fd) {
|
|||
|
||||
fclose($fd);
|
||||
|
||||
$sql = 'INSERT INTO data (meter_id, timestamp, value) VALUES ' . implode(', ', $pulses);
|
||||
$sql = 'INSERT INTO data (channel_id, timestamp, value) VALUES ' . implode(', ', $pulses);
|
||||
$dbh->execute($sql);
|
||||
|
||||
echo 'Imported rows: ' . $dbh->affectedRows();
|
||||
}
|
||||
else {
|
||||
throw new Exception('cant open dump');
|
||||
}
|
||||
|
||||
function parsePgSqlTimestamp($timestamp) {
|
||||
$unix = strtotime($timestamp);
|
|
@ -29,23 +29,45 @@
|
|||
# configuration
|
||||
#
|
||||
# backend url
|
||||
URL="http://localhost/workspace/volkszaehler.org/content/backend.php"
|
||||
# URL_PARAMS="&debug=1"
|
||||
URL="http://localhost/workspace/volkszaehler.org/backend/index.php"
|
||||
|
||||
# 1wire sensor id => volkszaehler.org ucid
|
||||
declare -A MAPPING
|
||||
MAPPING["1012E6D300080077"]="93f85330-9037-11df-86d3-379c018a387b"
|
||||
|
||||
# the digitemp binary, choose the right one for your adaptor
|
||||
DIGITEMP="digitemp_DS9097"
|
||||
DIGITEMP_OPTS="-a"
|
||||
# DIGITEMP_OPTS="-t 0"
|
||||
|
||||
# the digitemp configuration (holds your sensor ids)
|
||||
DIGITEMP_CONF="/home/steffen/.digitemprc"
|
||||
|
||||
# the port of your digitemp adaptor
|
||||
DIGITEMP_PORT="/dev/ttyUSB0"
|
||||
|
||||
# additional options for digitemp
|
||||
# specify single or all sensors here for example
|
||||
DIGITEMP_OPTS="-t 0"
|
||||
#DIGITEMP_OPTS="-a"
|
||||
|
||||
# additional options for curl
|
||||
# specify credentials, proxy etc here
|
||||
CURL_OPTS=""
|
||||
|
||||
# enable this for a more verbose output
|
||||
#DEBUG=true
|
||||
|
||||
# ========================= do not change anything under this line
|
||||
|
||||
# special ucid prefix for 1wire sensors
|
||||
UCID_PREFIX="07506920-6e7a-11df-"
|
||||
# building digitemp options
|
||||
DIGITEMP_OPTS="-c ${DIGITEMP_CONF} ${DIGITEMP_OPTS} -s ${DIGITEMP_PORT} -q -o %s;%R;%N;%C"
|
||||
|
||||
if [ $DEBUG ]; then
|
||||
echo "enabling debugging output"
|
||||
echo -e "running digitemp:\t${DIGITEMP} ${DIGITEMP_OPTS}"
|
||||
fi
|
||||
|
||||
# execute digitemp
|
||||
LINES=$(${DIGITEMP} -c ${DIGITEMP_CONF} ${DIGITEMP_OPTS} -q -o "%N;%R;%C")
|
||||
LINES=$(${DIGITEMP} ${DIGITEMP_OPTS})
|
||||
|
||||
# save old internal field seperator
|
||||
OLD_IFS=${IFS}
|
||||
|
@ -55,9 +77,26 @@ for LINE in $LINES
|
|||
do
|
||||
IFS=";"
|
||||
COLUMNS=( $LINE )
|
||||
UCID="${UCID_PREFIX}${COLUMNS[1]:0:4}-${COLUMNS[1]:5}"
|
||||
curl ${CURL_OPTS} "${URL}?controller=data&action=add&ucid=${UCID}&value=${COLUMNS[2]}×tamp=$(( ${COLUMNS[0]} * 1000 ))${URL_PARAMS}"
|
||||
done
|
||||
IFS=${OLD_IFS}
|
||||
|
||||
# reset old ifs
|
||||
IFS=${OLD_IFS}
|
||||
if [ -z ${MAPPING[${COLUMNS[1]}]} ]; then
|
||||
echo "sensor ${COLUMNS[1]} is not mapped to an ucid" >&2
|
||||
echo "please add it to the script. Example:" >&2
|
||||
echo >&2
|
||||
echo -e "MAPPING[\"${COLUMNS[1]}\"]=\"9aa643b0-9025-11df-9b68-8528e3b655ed\"" >&2
|
||||
elif [ ${COLUMNS[3]:0:2} == "85" ]; then
|
||||
echo "check your wiring; we received an invalid reading!" 1>2&
|
||||
else
|
||||
UCID=${MAPPING[${COLUMNS[1]}]}
|
||||
REQUEST_URL="${URL}?format=json&controller=data&action=add&ucid=${UCID}&value=${COLUMNS[3]}×tamp=$(( ${COLUMNS[2]} * 1000 ))${URL_PARAMS}${DEBUG:+&debug=1}"
|
||||
|
||||
if [ $DEBUG ]; then
|
||||
echo -e "logging sensor:\t\t${UCID}"
|
||||
echo -e "with value:\t\t${COLUMNS[3]}"
|
||||
echo -e "at\t\t\t$(date -d @${COLUMNS[2]})"
|
||||
echo "|"
|
||||
fi
|
||||
|
||||
curl ${CURL_OPTS} ${DEBUG:-"-s"} ${DEBUG:-"-o /dev/null"} ${DEBUG:+"--verbose"} "${REQUEST_URL}" 2>&1 | sed 's/^/|\t/'
|
||||
fi
|
||||
done
|
||||
|
|
Loading…
Add table
Reference in a new issue