Rev 54 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2015 by Andreas Theofilu. All rights reserved!
*
* All rights reserved. No warranty, explicit or implicit, provided.
*
* NOTICE: All information contained herein is, and remains
* the property of Andreas Theofilu and his suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to Andreas Theofilu and its suppliers and
* may be covered by European and Foreign Patents, patents in process,
* and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Andreas Theofilu.
*
* Author: Andreas Theofilu <andreas@theosys.at>
*/
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <thread>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <cerrno>
#include <cmath>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <bcm2835.h>
#include "heating.h"
#include "html.h"
#include "config.h"
#include "helper.h"
using namespace std;
#define PIN1 RPI_V2_GPIO_P1_03
#define PIN2 RPI_V2_GPIO_P1_05
#define PIN3 RPI_V2_GPIO_P1_07
#define PIN4 RPI_V2_GPIO_P1_11
#define PIN5 RPI_V2_GPIO_P1_13
#define PIN6 RPI_V2_GPIO_P1_15
#define PIN7 RPI_V2_GPIO_P1_16
#define PIN8 RPI_V2_GPIO_P1_18
#define PIN9 RPI_V2_GPIO_P1_22
heating::heating()
{
enableDebug(Configure.debug);
glb_minimal = 16.0;
glb_night = 18.0;
rm = 0;
outSensor = 0;
rstop = false;
rrun = false;
HeatConf = nullptr;
HtmlServer = nullptr;
for (int i = 0; i < 100; i++)
s1[i] = -1;
initialized = false;
if (readConf())
{
if (buildDB())
{
initialized = true;
debug("Initialisation successfully done.");
}
else
debug("Error initializing database");
}
else
debug("Error reading config file.");
}
heating::~heating()
{
destroyHConf();
}
void heating::pthrTemps()
{
trun();
}
void heating::pthrHtml()
{
if (HtmlServer != nullptr)
return;
HtmlServer = new html();
HtmlServer->setGlobals(glb_night, glb_minimal, onoff);
applyConfig();
HtmlServer->run();
delete HtmlServer;
HtmlServer = nullptr;
}
void heating::applyConfig()
{
HCONF *akt = HeatConf;
html::HTCONF ht;
while (akt)
{
strcpy (ht.rname, akt->rname);
ht.rnum = akt->rnum;
ht.soll = akt->soll;
ht.night = akt->night;
ht.minimal = akt->minimal;
ht.start1 = akt->start;
ht.end1 = akt->end;
ht.start2 = akt->wstart;
ht.end2 = akt->wend;
HtmlServer->addConfig(&ht);
akt = akt->next;
}
}
void heating::run()
{
int i;
double tmp;
HCONF *akt;
time_t loctime = getTime();
rstop = false;
rrun = true;
i = 0;
debug ("heating::run() reached!");
// First start the runtime routine of class temperature.
// We need this to get our temperatures
thread pthr_temps(&heating::pthrTemps, this);
pthr_temps.detach();
debug ("heating::run() started thread temperature::trun() and detached");
// Then we start the HTML thread.
thread pthr_html(&heating::pthrHtml, this);
pthr_html.detach();
debug("heating::run() started thread html");
// Here we read the previous colected temperatures and evaluate them.
while (!rstop)
{
akt = HeatConf;
if (HtmlServer->hasChanged())
{
html::HTCONF *hta;
string ans;
glb_minimal = HtmlServer->getGlbAbsent();
glb_night = HtmlServer->getGlbNight();
onoff = HtmlServer->getRunMode();
ans = "CONFIG:GLOBAL NIGHT:" + doubleToString(glb_night, 1) + ";";
answer(ans);
ans = "CONFIG:GLOBAL MINIMAL:" + doubleToString(glb_minimal, 1) + ";";
answer(ans);
ans = "POWER:";
ans.append((onoff) ? "ON;" : "OFF;");
answer(ans);
hta = HtmlServer->getHConfig();
while (hta)
{
akt = HeatConf;
while (akt)
{
if (akt->rnum == hta->rnum)
{
stringstream stream;
akt->soll = hta->soll;
akt->night = hta->night;
akt->minimal = hta->minimal;
akt->start = hta->start1;
akt->end = hta->end1;
akt->wstart = hta->start2;
akt->wend = hta->end2;
stream << "CONFIG:ROOM:" << akt->rnum << ":" << string(akt->rname) << ":" << fixed <<
setprecision(1) << akt->soll << ":" << akt->night << ":" <<
akt->minimal << ":" << timeToStr(akt->start) << ":" << timeToStr(akt->end) << ":" <<
timeToStr(akt->wstart) << ":" << timeToStr(akt->wend) << ";";
answer(stream.str());
break;
}
akt = akt->next;
}
hta = hta->next;
}
akt = HeatConf;
}
while (akt)
{
/// Set the mode for every room.
if (onoff)
{
if (loctime >= akt->start && loctime <= akt->end)
akt->status = NORMAL;
else
akt->status = NIGHT;
}
else
akt->status = OFF;
tmp = getTemp(akt->tempsensor);
if (tmp < 99.0 && tmp > -99.0)
{
akt->ist = tmp;
if (evaluateTemp(akt->rnum, tmp)) // Should we start heating?
startHeat(akt->rnum); // yes
else // Or should we stop heating
stopHeat(akt->rnum); // yes
if (isHeating()) // As long as there is a room who need heating, this function returns true.
controlHeat(0, true);
else
controlHeat(0, false);
}
akt = akt->next;
}
i++;
if (i > 60)
{
i = 0;
akt = HeatConf;
while (akt)
{
answer("IST:" + itostring(akt->rnum) + ":" + doubleToString(akt->ist, 1) + ";");
akt = akt->next;
}
}
sleep(1);
}
rrun = false;
}
void heating::controlHeat (int room, bool stst)
{
HCONF *akt = HeatConf;
int pin, gpio;
if (room > 0)
{
while (akt)
{
if (akt->rnum == room)
break;
akt = akt->next;
}
if (!akt)
{
syslog(LOG_WARNING, "controlHeat: Room %d not found!", room);
return;
}
if (akt->valve != stst)
{
if (Configure.debug)
syslog(LOG_DEBUG, "Heating for room %d switched %s", room, (stst) ? "ON" : "OFF");
string h = "HEATING:";
h.append(itostring(room) + ":");
h.append((stst)? "ON;" : "OFF;");
answer(h);
}
gpio = akt->gpio;
}
else if (heatIO <= 0)
return;
else
gpio = heatIO;
switch (gpio)
{
case 1: pin = PIN1; break;
case 2: pin = PIN2; break;
case 3: pin = PIN3; break;
case 4: pin = PIN4; break;
case 5: pin = PIN5; break;
case 6: pin = PIN6; break;
case 7: pin = PIN7; break;
case 8: pin = PIN8; break;
case 9: pin = PIN9; break;
default:
return;
}
if (room && akt)
akt->valve = stst;
// Enable the GPIO pin to start heating
if (!bcmInitialized)
return;
// Set the pin to be an output
bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
// Turn it on
bcm2835_gpio_write(pin, (stst)? HIGH : LOW);
}
double heating::incSoll(int room)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return 0.0;
}
if ((akt = findRoom(room)) != nullptr)
{
if (akt->soll >= 30.0)
{
sqlite3_close(db);
return akt->soll;
}
akt->soll += 0.5;
applyConfig();
query = "update heating set soll = " + doubleToString(akt->soll, 1) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
akt->soll -= 0.5;
return akt->soll;
}
sqlite3_close(db);
stringstream stream;
stream << "SOLL:" << fixed << setprecision(1) << akt->soll << ";";
answer(stream.str());
return akt->soll;
}
return 0.0;
}
double heating::decSoll(int room)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return 0.0;
}
if ((akt = findRoom(room)) != nullptr)
{
if (akt->soll <= 10.0)
{
sqlite3_close(db);
return akt->soll;
}
akt->soll -= 0.5;
applyConfig();
query = "update heating set soll = " + doubleToString(akt->soll, 1) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
akt->soll += 0.5;
return akt->soll;
}
sqlite3_close(db);
stringstream stream;
stream << "SOLL:" << fixed << setprecision(1) << akt->soll << ";";
answer(stream.str());
return akt->soll;
}
return 0.0;
}
void heating::setSoll (int room, double val)
{
HCONF *akt = HeatConf;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
if (val < 10.0 || val > 30.0)
{
syslog(LOG_INFO, "Invalid value for maximum room temperature!");
return;
}
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return;
}
if (room > 0 && (akt = findRoom(room)) != nullptr)
{
akt->soll = val;
applyConfig();
query = "update heating set soll = " + doubleToString(val, 1) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "SOLL:" + itostring(room) + ":" + doubleToString(val, 1) + ";";
answer(query);
}
sqlite3_close(db);
}
void heating::setNight(int room, double val)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
if (val < 10.0 || val > 25.0)
{
syslog(LOG_INFO, "Invalid value for the night!");
return;
}
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return;
}
if (room > 0 && (akt = findRoom(room)) != nullptr)
{
akt->night = val;
applyConfig();
query = "update heating set night = " + doubleToString(val, 1) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "NIGHT:" + itostring(room) + ":" + doubleToString(val, 1) + ";";
answer(query);
}
else if (room == 0)
{
glb_night = val;
if (HtmlServer != nullptr)
HtmlServer->setGlobals(glb_night, glb_minimal, onoff);
query = "update glbheat set night = " + doubleToString(val, 1);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "CONFIG:GLOBAL NIGHT:" + doubleToString(val, 1) + ";";
answer(query);
}
sqlite3_close(db);
}
void heating::setMinimal(int room, double val)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
if (val < 10.0 || val > 20.0)
{
syslog(LOG_INFO, "Invalid value for the night!");
return;
}
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return;
}
if (room > 0 && (akt = findRoom(room)) != nullptr)
{
akt->minimal = val;
applyConfig();
query = "update heating set minimal = " + doubleToString(val, 1) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "MINIMAL:" + itostring(room) + ":" + doubleToString(val, 1) + ";";
answer(query);
}
else if (room == 0)
{
glb_minimal = val;
if (HtmlServer != nullptr)
HtmlServer->setGlobals(glb_night, glb_minimal, onoff);
query = "update glbheat set minimal = " + doubleToString(val, 1);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "CONFIG:GLOBAL MINIMAL:" + doubleToString(val, 1) + ";";
answer(query);
}
sqlite3_close(db);
}
bool heating::switchOnOff(bool what)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return onoff;
}
onoff = what;
if (HtmlServer != nullptr)
HtmlServer->setGlobals(glb_night, glb_minimal, onoff);
query = "update glbheat set onoff = " + (what)?"1":"0";
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return onoff;
}
sqlite3_close(db);
query = "POWER:";
query.append((onoff) ? "ON;" : "OFF;");
answer(query);
return onoff;
}
double heating::getNight (int room)
{
HCONF *akt = findRoom(room);
if (akt && akt->night > 0.0)
return akt->night;
return glb_night;
}
double heating::getMinimal (int room)
{
HCONF *akt = findRoom(room);
if (akt && akt->minimal > 0.0)
return akt->minimal;
return glb_minimal;
}
void heating::destroyHConf()
{
HCONF *p, *next;
p = HeatConf;
while (p)
{
next = p->next;
delete p;
p = next;
}
HeatConf = nullptr;
}
void heating::getConfig (int s1)
{
HCONF *akt = HeatConf;
stringstream stream;
stream << "CONFIG:GLOBAL NIGHT:" << fixed << setprecision(1) << glb_night << ";";
stream << "CONFIG:GLOBAL MINIMAL:" << fixed << setprecision(1) << glb_minimal << ";";
stream << "CONFIG:ONOFF:" << ((onoff) ? "ON;" : "OFF;");
while (akt)
{
stream << "CONFIG:ROOM:" << itostring(akt->rnum) << ":" << string(akt->rname) << ":" <<
fixed << setprecision(1) << akt->soll << ":" << akt->night << ":" <<
akt->minimal << ":" << timeToStr(akt->start) << ":" << timeToStr(akt->end) << ":" <<
timeToStr(akt->wstart) << ":" << timeToStr(akt->wend) << ";";
stream << "HEATING:" << itostring(akt->rnum) << ":";
if (akt->valve)
stream << "ON;";
else
stream << "OFF;";
akt = akt->next;
}
write(s1, stream.str().c_str(), stream.str().length());
}
heating::HCONF* heating::findRoom (int room)
{
HCONF *akt = HeatConf;
while (akt)
{
if (akt->rnum == room)
return akt;
akt = akt->next;
}
return nullptr;
}
bool heating::readConf()
{
ifstream cfile;
char line[1024], hv0[256], hv1[128];
char *p;
CNFSTAT cs;
HCONF *akt = HeatConf;
// If there's no config file, inform the user about it.
cfile.open(Configure.heatconf, ios::in);
if ((cfile.rdstate() & std::ifstream::failbit) != 0)
{
syslog(LOG_WARNING,"Error opening the config file: %s", strerror(errno));
return false;
}
syslog(LOG_INFO, "Found config file %s.", Configure.heatconf);
cs = NONE;
// Here we've found a config file. We'll read it and set the
// contents to the structure
while (cfile.getline(&line[0], sizeof(line)))
{
int len;
trim (&line[0]);
if (line[0] == '#' || !char_traits<char>::length(line))
continue;
if ((p = findc(line, char_traits<char>::length(line), ']')) != NULL)
{
char *x;
*(p+1) = 0;
if (!compcase(line, "[global]"))
{
cs = GLOBAL;
continue;
}
if ((x = findc(line, char_traits<char>::length(line), '[')) != NULL)
{
*p = 0;
akt = appendHConf();
strncpy(akt->rname, x+1, sizeof(akt->rname) - 1);
akt->soll = 20.0;
cs = ROOM;
continue;
}
else
cs = NONE;
}
if ((p = findc(line, char_traits<char>::length(line), '=')) == NULL || cs == NONE)
continue;
*p = 0;
p++;
len = char_traits<char>::length(line);
if (len > (int)sizeof(hv0))
len = (int)sizeof(hv0) - 1;
strncpy(hv0, line, len);
hv0[len] = 0;
trim (hv0);
len = char_traits<char>::length(p);
if (len > (int)sizeof(hv1))
len = (int)sizeof(hv0) - 1;
strncpy(hv1, p, len);
hv1[len] = 0;
trim (hv1);
if (!compcase(hv0, "minimal"))
{
if (cs == GLOBAL)
glb_minimal = atof (hv1);
else
akt->minimal = atof (hv1);
}
if (!compcase(hv0, "night"))
{
if (cs == GLOBAL)
glb_night = atof (hv1);
else
akt->night = atof (hv1);
}
if (!compcase(hv0, "OnOff") && cs == GLOBAL)
onoff = ToBool(hv1);
if (!compcase(hv0, "OutSensor") && cs == GLOBAL)
outSensor = atoi(hv1);
if (!compcase(hv0, "HeatIO") && cs == GLOBAL)
heatIO = atoi(hv1);
if (cs == ROOM)
{
if (!compcase(hv0, "soll"))
akt->soll = atoi(hv1);
if (!compcase(hv0, "Sensor"))
akt->tempsensor = atoi(hv1);
if (!compcase(hv0, "GPIO"))
akt->gpio = atoi(hv1);
if (!compcase(hv0, "StartTime"))
{
akt->start = strToTime(hv1);
debug("StartTime: " + itostring(akt->start) + "(" + ToString(hv1) + ")");
}
if (!compcase(hv0, "EndTime"))
{
akt->end = strToTime(hv1);
debug("EndTime: " + itostring(akt->end) + "(" + ToString(hv1) + ")");
}
if (!compcase(hv0, "WorkStartTime"))
{
akt->wstart = strToTime(hv1);
debug("WorkStartTime: " + itostring(akt->wstart) + "(" + ToString(hv1) + ")");
}
if (!compcase(hv0, "WorkEndTime"))
{
akt->wend = strToTime(hv1);
debug("WorkEndTime: " + itostring(akt->wend) + "(" + ToString(hv1) + ")");
}
}
}
akt = HeatConf;
time_t loctime = getTime();
while (akt)
{
if (onoff)
akt->status = OFF;
else if (loctime > akt->start && loctime < akt->end)
akt->status = NORMAL;
else
akt->status = NIGHT;
akt = akt->next;
}
cfile.close ();
if (heatIO <= 0)
syslog (LOG_DAEMON, "Missing the GPIO number to start and stop the heating! This heat control will not work!");
return true;
}
bool heating::buildDB()
{
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *zErrMsg = nullptr;
string query;
if (access(Configure.home, R_OK | W_OK)) // Try to create a new database if it doesn't exist
{
HCONF *akt;
debug("Creating SQL3 database.");
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return false;
}
query = "create table \"main\".\"heating\" (";
query.append("\"id\" INTEGER PRIMARY KEY NOT NULL,");
query.append("\"name\" TEXT NOT NULL,");
query.append("\"soll\" REAL,");
query.append("\"night\" REAL,");
query.append("\"minimal\" REAL,");
query.append("\"start\" INTEGER,");
query.append("\"end\" INTEGER,");
query.append("\"wstart\" INTEGER,");
query.append("\"wend\" INTEGER");
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
else
syslog(LOG_INFO, "Database \"heating\" was successfully created.");
query = "create table \"main\".\"kitable\" (";
query.append("\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
query.append("\"rnum\" INTEGER NOT NULL,");
query.append("\"diffout\" INTEGER,");
query.append("\"diffin\" INTEGER,");
query.append("\"heat\" INTEGER)");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
else
syslog(LOG_INFO, "Database \"kitable\" was successfully created.");
query = "create table \"main\".\"glbheat\" (";
query.append("\"night\" REAL,");
query.append("\"minimal\" REAL,");
query.append("\"onoff\" INTEGER");
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
else
syslog(LOG_INFO, "Database \"glbheat\" was successfully created.");
// Insert the values from the config file into the table
debug("Inserting configuration into SQL3 database");
akt = HeatConf;
while (akt)
{
query = "insert into heating (id, name, soll, night, minimal, start, end, wstart, wend) values (";
query.append(itostring(akt->rnum));
query.append(",\""+string(akt->rname)+"\"");
query.append(","+doubleToString(akt->soll, 1));
query.append(","+doubleToString(akt->night, 1));
query.append(","+doubleToString(akt->minimal, 1));
query.append(","+itostring(akt->start));
query.append(","+itostring(akt->end));
query.append(","+itostring(akt->wstart));
query.append(","+itostring(akt->wend));
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
akt = akt->next;
}
query = "insert into glbheat (night, minimal, onoff) values (";
query.append(doubleToString(glb_night, 1));
query.append(","+doubleToString(glb_minimal, 1));
query.append(","+itostring(onoff));
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
sqlite3_close(db);
}
else
{
HCONF *akt;
debug("Reading configuration from SQL3 database");
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return false;
}
akt = HeatConf;
time_t loctime = getTime();
while (akt)
{
debug("Queriing "+itostring(akt->rnum)+", "+ToString(akt->rname));
query = "select id, name, soll, night, minimal, start, end from heating where id = "+itostring(akt->rnum);
if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
if ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
strncpy(akt->rname, (const char *)sqlite3_column_text(res, 1), sizeof(akt->rname) - 1);
akt->soll = sqlite3_column_double(res, 2);
akt->night = sqlite3_column_double(res, 3);
akt->minimal = sqlite3_column_double(res, 4);
akt->start = (time_t)sqlite3_column_int(res, 5);
akt->end = (time_t)sqlite3_column_int(res, 6);
}
sqlite3_finalize(res);
query = "select rnum, diffout, diffin, heat from kitable where rnum = " + itostring(akt->rnum);
if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
while ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
HTABLE *tb = appendHTable(akt);
tb->tempDifferOut = sqlite3_column_double(res, 1);
tb->tempDifferIn = sqlite3_column_double(res, 2);
tb->heat = sqlite3_column_int(res, 3);
debug("KITABLE: read "+itostring(sqlite3_column_int(res, 0)));
}
sqlite3_finalize(res);
if (onoff)
akt->status = OFF;
else if (loctime > akt->start && loctime < akt->end)
akt->status = NORMAL;
else
akt->status = NIGHT;
akt = akt->next;
}
debug("Reading global parameters from SQL3 database");
query = "select night, minimal, onoff from glbheat";
if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
if ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
glb_night = sqlite3_column_double(res, 0);
glb_minimal = sqlite3_column_double(res, 1);
onoff = (time_t)sqlite3_column_int(res, 2);
}
sqlite3_finalize(res);
sqlite3_close(db);
}
debug("All database operations done");
return true;
}
void heating::setHandle (int fd)
{
// First look into table to make sure the handle isn't already in the table
for (int i = 0; i < 100; i++)
{
if (s1[i] == fd)
return;
}
// Because we're here, the handle is not in the table. So insert it now.
for (int i = 0; i < 100; i++)
{
if (s1[i] < 0)
{
s1[i] = fd;
break;
}
}
}
void heating::removeHandle (int fd)
{
for (int i = 0; i < 100; i++)
{
if (s1[i] == fd)
{
s1[i] = -1;
break;
}
}
}
void heating::answer (string msg)
{
for (int i = 0; i < 100; i++)
{
if (s1[i] > 0)
write(s1[i], msg.c_str(), strlen(msg.c_str()));
}
}
time_t heating::strToTime (char* str)
{
int hour, min;
if (strchr(str, ':') != NULL)
{
sscanf(str, "%d:%d", &hour, &min);
return (time_t)hour * 3600 + (time_t)min * 60;
}
return 0;
}
string heating::timeToStr(time_t t)
{
int hour, min;
char hv0[48];
hour = (int)(t / 3600);
min = (int)((t - (time_t)hour * 3600) / 60);
sprintf(hv0, "%02d:%02d", hour, min);
string zeit(hv0);
return zeit;
}
time_t heating::getTime()
{
tm *ltm;
time_t t = time(NULL);
time_t out;
ltm = localtime(&t);
out = (time_t)(ltm->tm_hour * 3600 + ltm->tm_min * 60 + ltm->tm_sec);
return out;
}
heating::HCONF* heating::appendHConf()
{
HCONF *akt, *p;
akt = new HCONF;
memset(akt, 0, sizeof(HCONF));
akt->soll = 0.0;
akt->night = 0.0;
akt->minimal = 0.0;
akt->ist = 0.0;
if (HeatConf == nullptr)
HeatConf = akt;
else
{
p = HeatConf;
while (p->next)
p = p->next;
p->next = akt;
}
rm++;
akt->rnum = rm;
return akt;
}
/*
* This function checks if there is a room where heating is requested.
* If only one room requests heating, it returns true. This means, that
* the therme, the heating, should start.
* Otherwise it should stop heating.
* This will work only, if there's a GPIO pin defined for the heating!
* Beside this the function checks whether the defined GPIO pin is
* unique. If not, it always returns false.
*/
bool heating::isHeating()
{
HCONF *akt = HeatConf;
if (heatIO <= 0)
return false;
// Make sure the defined GPIO pin for the heating is unique.
while (akt)
{
if (akt->gpio == heatIO)
return false;
akt = akt->next;
}
// Find out if there is a room who requested heating.
akt = HeatConf;
while (akt)
{
if (akt->valve)
return true;
akt = akt->next;
}
return false;
}
heating::HTABLE* heating::appendHTable(HCONF *ht)
{
HTABLE *akt, *p;
akt = new HTABLE;
memset(akt, 0, sizeof(HTABLE));
if (ht->hTable == nullptr)
ht->hTable = akt;
else
{
p = ht->hTable;
while (p->next)
p = p->next;
p->next = akt;
}
return akt;
}
bool heating::evaluateTemp (int room, double temp)
{
HCONF *akt;
double soll, diffOut, diffIn;
time_t loctime;
akt = HeatConf;
while (akt)
{
if (akt->rnum == room)
break;
akt = akt->next;
}
if (!akt)
{
syslog(LOG_WARNING, "evaluateTemp: Room %s not found!", room);
return false;
}
loctime = getTime();
switch(akt->status)
{
case NORMAL:
if (akt->wstart && akt->wstart < akt->wend && loctime >= akt->wstart && loctime <= akt->wend)
soll = akt->night;
else if ((loctime >= akt->start && loctime <= akt->wstart) ||
(loctime >= akt->wend && loctime <= akt->end))
soll = akt->soll;
diffOut = trunc(soll - getOutside());
diffIn = trunc(soll - temp);
break;
case NIGHT:
if (akt->night)
soll = akt->night;
else
soll = glb_night;
diffOut = trunc(soll - getOutside());
diffIn = trunc(soll - temp);
break;
case OFF:
if (akt->minimal)
soll = akt->minimal;
else
soll = glb_minimal;
diffOut = trunc(soll - getOutside());
diffIn = trunc(soll - temp);
break;
}
if (!akt->hTable) // If we've no heating table, we've to evaluate the temperature difference
{
if (!akt->valve && temp <= (soll - 0.5))
{
HTABLE *tb;
tb = appendHTable(akt);
tb->tempDifferOut = trunc(soll - getOutside());
tb->tempDifferIn = trunc(soll - temp);
tb->start = getTime();
return true;
}
if (akt->valve && temp < soll)
return true;
else
return false;
}
else if (akt->valve && temp < soll)
return true;
else if (akt->valve && temp >= soll)
{
HTABLE *tb = akt->hTable;
while (tb)
{
if (tb->start)
break;
if (tb->next)
tb = tb->next;
else
break;
}
if (tb && tb->start)
{
if (loctime > tb->start)
tb->heat = loctime - tb->start;
else
tb->heat = (86400 + loctime) - tb->start;
tb->start = 0;
}
return false;
}
else if (!akt->valve && temp <= (soll - 0.5)) // If temperature is beyond soll, start immediately
{
HTABLE *tb = akt->hTable;
// Find out whether we've already a correct time in the table or not.
while (tb)
{
if (tb->tempDifferOut == diffOut && tb->tempDifferIn == diffIn)
break;
tb = tb->next;
}
if (!tb)
{
tb = appendHTable(akt);
tb->tempDifferOut = diffOut;
tb->tempDifferIn = diffIn;
tb->start = loctime;
}
return true;
}
else
{
HTABLE *tb = akt->hTable;
time_t t = getTime();
// Check the current time and look in learned table whether we should
// start heating or not.
if (tb) // Only if we already have a table!
{ // Yes there is one, so search through it.
bool strt = false;
if (diffIn <= 0.0) // Is it warmer inside than it should be?
return false; // yes, then turn heating off
if (akt->status == OFF && diffIn >= 0.5) // Are we in off mode and is the temperature lower than it should be?
return true; // yes, then start heating
else if (akt->status == OFF) // otherwise ...
return false; // no heating
while (tb) // Search through learned table
{
if (!tb->start && tb->tempDifferOut == diffOut && tb->tempDifferIn == diffIn)
break;
tb = tb->next;
}
if (tb) // Have we found a known time to heat?
{ // Yes, then look if it's time to heat
if (akt->status == NORMAL)
{
if ((t + tb->heat) >= akt->start && (t + tb->heat) <= akt->end)
return true;
}
else if (akt->status == NIGHT)
{
if ((t + tb->heat) >= akt->end || t < (akt->end - tb->heat))
return true;
}
}
else // None of the saved temperture differences match.
{ // So test if it's time to add a new one
if (akt->status == NORMAL)
{
if ((t + 600) >= akt->start && (t + 600) <= akt->end)
strt = true;
}
else if (akt->status == NIGHT)
{
if ((t + 600) >= akt->end || t < (akt->end - 600))
strt = true;
}
if (strt)
{
bool add = true;
// Make sure there is no open start
tb = akt->hTable;
while (tb)
{
if (tb->start)
{
add = false;
break;
}
tb = tb->next;
}
if (add)
tb = appendHTable(akt);
tb->tempDifferOut = diffOut;
tb->tempDifferIn = diffIn;
tb->start = t;
return true;
}
}
}
}
return false;
}
time_t heating::calcTime(double soll, double ist)
{
double pwm, len;
double correct= 100;
double parallel = 110;
pwm = (log((soll - ist) + correct * 0.001) + parallel * 0.01) * 100.0;
len = (10 * 60) * pwm / 100.0;
return time_t(len * 10.0);
}