556c51440f
with fully functional worker and basic documentation
178 lines
5.6 KiB
PHP
178 lines
5.6 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 = "";
|
|
while(strlen($dataStream) < $bytesToRead)
|
|
$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;
|
|
}
|
|
?>
|