Files
VE.Direct-Text-Reader/worker.php
T

179 lines
5.7 KiB
PHP

<?php
/*
* VE.Direct-Text-Reader
* worker.php
* Command line tool to fetch text protocol data from
* VE.Direct interfaces and store it into a database
* for further processing
*
* Licensed under the GPL 3.0
* https://git.schauaus.at/VE-Tools/VE.Direct-Text-Reader
*/
$startAt = date('Y-m-d H:i:s');
$scriptPath = dirname(__FILE__);
$config = json_decode( file_get_contents($scriptPath . '/config.json') );
$loglevel = 0;
switch ($config->loglevel) {
case 'debug':
$loglevel = 4;
break;
case 'info':
$loglevel = 3;
break;
case 'warn':
$loglevel = 2;
break;
case 'error':
$loglevel = 1;
break;
}
if ($loglevel > 0) {
$logfile = (!str_starts_with($config->logfile, '/')) ? fopen($scriptPath . '/' . $config->logfile, 'a') : fopen($config->logfile, 'a');
writeLog("===================================================\r\n", 1);
writeLog("worker startet at $startAt\r\n", 1);
writeLog("loglevel: $loglevel (" . $config->loglevel . ")\r\n\r\n", 1);
}
//create database if it does not exist yet
if(!is_file((!str_starts_with($config->database, '/')) ? $scriptPath. '/' . $config->database : $config->database)) {
writeLog('Database not found! I will create it! ', 3);
if( !initializeDB() ) {
writeLog("failed! Exit!\r\n\r\n");
cleanUp('with errors ');
}
else writeLog("succeess!\r\n\r\n", 3);
}
$data = readData();
if( !$data )
cleanUp('with errors ');
if( !processData($data, $startAt) )
cleanUp('with errors ');
cleanUp('successfully ');
/*
* cleanup
*/
function cleanUp($status) {
global $loglevel;
if( $loglevel > 0) {
global $logfile;
writeLog('worker finished ' . $status . 'at ' . date('Y-m-d H:i:s') . "\r\n", 1);
writeLog("===================================================\r\n\r\n", 1);
fclose($logfile);
}
exit();
}
/*
* global function to write new data to logifle
*/
function writeLog($strData, $minLogLevel) {
global $config, $logfile, $loglevel;
if($loglevel >= $minLogLevel)
fwrite($logfile, $strData);
}
/*
* read data stream from VE Direct USB serial interface
*/
function readData() {
global $config;
writeLog("Open VE Direct interface:\r\n" . $config->device . "\r\n", 3);
$VEinterface = dio_open($config->device, O_RDONLY | O_NONBLOCK);
if($VEinterface === false) {
writeLog("unable to open VE Direct interface!\r\n", 1);
return false;
}
writeLog("success!\r\n\r\n", 3);
//set up the interface to properly communicate with the interface
dio_tcsetattr( $VEinterface, array(
'baud' => 19200,
'bits' => 8,
'stop' => 1,
'parity' => 0,
'flow_control' => 0,
'is_canonical' => 0)
);
$bytesToRead = 1024; // read 1KB by default
if(isset($config->bytesToRead))
$bytesToRead = $config->bytesToRead;
writeLog("Reading $bytesToRead Byte from VE.Direct interface\r\n", 3);
$dataStream = "";
$startTime = time();
while(strlen($dataStream) < $bytesToRead && time() < $startTime + $config->timeout)
$dataStream .= dio_read($VEinterface, $bytesToRead);
dio_close($VEinterface);
writeLog($dataStream . "\r\n\r\n", 4);
//split data stream into array and remove checksums
$dataArray = explode("\n", $dataStream);
$collect = false;
$returnData = array();
for($i=0; $i < count($dataArray); $i++) {
if(!$collect && str_starts_with($dataArray[$i], 'PID'))
$collect = true;
else if($collect && str_starts_with($dataArray[$i], 'PID'))
break;
if($collect) {
$dataLine = trim($dataArray[$i]);
if( strlen($dataLine) == 0 || str_starts_with($dataLine, 'Checksum') || str_starts_with($dataLine, ':') ) //skip empty lines and checksums
continue;
$dataFields = explode("\t", $dataLine);
$returnData[$dataFields[0]] = $dataFields[1];
}
}
ksort($returnData);
writeLog("Collected data:\r\n" . print_r($returnData, true) . "\r\n", 3);
return $returnData;
}
function processData($data, $collectionDateTime) {
global $config, $scriptPath;
$db = (!str_starts_with($config->database, '/')) ? new SQLITE3($scriptPath . '/' . $config->database) : new SQLITE3($config->database);
$dbTableName = $data['PID'] . '-' . $data['SER#'];
$sql = "SELECT name FROM sqlite_schema WHERE type='table' AND name='$dbTableName';";
$tableNames = $db->query($sql);
if( !$tableNames->fetchArray(SQLITE3_NUM) ) {
writeLog('New Victron Energy Device found. Crating a new database table... ', 3);
if( $db->exec("CREATE TABLE '$dbTableName' (date TEXT, time TEXT, data TEXT);") )
writeLog("success!\r\n", 3);
else {
writeLog("failed! Cancel!\r\n", 3);
return false;
}
}
$dateTime = explode(" ", $collectionDateTime);
unset($data['PID']);
unset($data['SER#']);
$dataJson = json_encode($data);
writeLog('Add dataset to database... ', 3);
$sql = "INSERT INTO '$dbTableName' (date, time, data) VALUES('$dateTime[0]', '$dateTime[1]', '$dataJson')";
if( !$db->exec($sql) ) {
writeLog("failed!\r\n\r\n", 3);
return false;
}
writeLog("success!\r\n\r\n", 3);
return true;
}
function initializeDB() {
global $config, $scriptPath;
$dateTime = date('Y-m-d H:m:s');
$db = (!str_starts_with($config->database, '/')) ? new SQLITE3($scriptPath . '/' . $config->database) : new SQLITE3($config->database);
if( !$db->exec('CREATE TABLE meta (data TEXT);') ) return false;
if( !$db->exec("INSERT INTO meta (data) VALUES ('{\"creation\": \"$dateTime\"');") ) return false;
return true;
}
?>