Subversion Repositories heating

Rev

Rev 44 | Rev 47 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 andreas 1
/*
2
 * Copyright (C) 2015 by Andreas Theofilu. All rights reserved!
3
 *
4
 * All rights reserved. No warranty, explicit or implicit, provided.
5
 *
6
 * NOTICE:  All information contained herein is, and remains
7
 * the property of Andreas Theofilu and his suppliers, if any.
8
 * The intellectual and technical concepts contained
9
 * herein are proprietary to Andreas Theofilu and its suppliers and
10
 * may be covered by European and Foreign Patents, patents in process,
11
 * and are protected by trade secret or copyright law.
12
 * Dissemination of this information or reproduction of this material
13
 * is strictly forbidden unless prior written permission is obtained
14
 * from Andreas Theofilu.
15
 * 
16
 * Author: Andreas Theofilu <andreas@theosys.at>
17
 */
18
#include <iostream>
19
#include <fstream>
20
#include <string>
4 andreas 21
#include <iomanip>
8 andreas 22
#include <thread>
3 andreas 23
#include <cstring>
24
#include <cctype>
25
#include <cstddef>
26
#include <cstdio>
27
#include <cstdlib>
4 andreas 28
#include <ctime>
3 andreas 29
#include <fstream>
30
#include <cerrno>
8 andreas 31
#include <cmath>
3 andreas 32
#include <unistd.h>
33
#include <syslog.h>
34
#include <sys/stat.h>
35
#include <sys/types.h>
36
#include <fcntl.h>
4 andreas 37
#include <sqlite3.h>
9 andreas 38
#include <bcm2835.h>
3 andreas 39
#include "heating.h"
17 andreas 40
#include "html.h"
3 andreas 41
#include "config.h"
42
#include "helper.h"
43
 
44
using namespace std;
45
 
9 andreas 46
#define PIN1	RPI_V2_GPIO_P1_03
47
#define PIN2	RPI_V2_GPIO_P1_05
48
#define PIN3	RPI_V2_GPIO_P1_07
49
#define PIN4	RPI_V2_GPIO_P1_11
50
#define PIN5	RPI_V2_GPIO_P1_13
51
#define PIN6	RPI_V2_GPIO_P1_15
52
#define PIN7	RPI_V2_GPIO_P1_16
53
#define PIN8	RPI_V2_GPIO_P1_18
54
#define PIN9	RPI_V2_GPIO_P1_22
8 andreas 55
 
3 andreas 56
heating::heating()
57
{
58
	enableDebug(Configure.debug);
59
	glb_minimal = 16.0;
4 andreas 60
	glb_night = 18.0;
3 andreas 61
	rm = 0;
12 andreas 62
	outSensor = 0;
5 andreas 63
	rstop = false;
64
	rrun = false;
3 andreas 65
	HeatConf = nullptr;
46 andreas 66
	HtmlServer = nullptr;
5 andreas 67
 
68
	for (int i = 0; i < 100; i++)
69
		s1[i] = -1;
70
 
4 andreas 71
	initialized = false;
72
 
73
	if (readConf())
74
	{
75
		if (buildDB())
18 andreas 76
		{
4 andreas 77
			initialized = true;
18 andreas 78
			debug("Initialisation successfully done.");
79
		}
80
		else
81
			debug("Error initializing database");
4 andreas 82
	}
18 andreas 83
	else
84
		debug("Error reading config file.");
3 andreas 85
}
86
 
87
heating::~heating()
88
{
89
	destroyHConf();
90
}
91
 
9 andreas 92
void heating::pthrTemps()
8 andreas 93
{
94
	trun();
95
}
96
 
17 andreas 97
void heating::pthrHtml()
98
{
99
HCONF *akt = HeatConf;
100
html::HTCONF ht;
101
 
46 andreas 102
	if (HtmlServer != nullptr)
103
		return;
104
 
17 andreas 105
	HtmlServer = new html();
106
	HtmlServer->setGlobals(glb_night, glb_minimal, onoff);
107
 
108
	while (akt)
109
	{
110
		strcpy (ht.rname, akt->rname);
111
		ht.rnum = akt->rnum;
112
		ht.soll = akt->soll;
113
		ht.night = akt->night;
114
		ht.minimal = akt->minimal;
35 andreas 115
		ht.start1 = akt->start;
116
		ht.end1 = akt->end;
117
		ht.start2 = akt->wstart;
118
		ht.end2 = akt->wend;
119
 
17 andreas 120
		HtmlServer->addConfig(&ht);
121
		akt = akt->next;
122
	}
123
 
124
	HtmlServer->run();
125
	delete HtmlServer;
46 andreas 126
	HtmlServer = nullptr;
17 andreas 127
}
128
 
4 andreas 129
void heating::run()
130
{
5 andreas 131
int i;
132
double tmp;
133
HCONF *akt;
4 andreas 134
 
5 andreas 135
	rstop = false;
136
	rrun = true;
137
	i = 0;
138
 
12 andreas 139
	debug ("heating::run() reached!");
8 andreas 140
	// First start the runtime routine of class temperature.
9 andreas 141
	// We need this to get our temperatures
142
	thread pthr_temps(&heating::pthrTemps, this);
143
	pthr_temps.detach();
12 andreas 144
	debug ("heating::run() started thread temperature::trun() and detached");
17 andreas 145
	// Then we start the HTML thread.
146
	thread pthr_html(&heating::pthrHtml, this);
147
	pthr_html.detach();
148
	debug("heating::run() started thread html");
9 andreas 149
	// Here we read the previous colected temperatures and evaluate them.
5 andreas 150
	while (!rstop)
151
	{
152
		akt = HeatConf;
9 andreas 153
 
46 andreas 154
		if (HtmlServer->hasChanged())
155
		{
156
			html::HTCONF *hta;
157
 
158
			glb_minimal = HtmlServer->getGlbAbsent();
159
			glb_night = HtmlServer->getGlbNight();
160
			onoff = HtmlServer->getRunMode();
161
			hta = HtmlServer->getHConfig();
162
 
163
			while (hta)
164
			{
165
				akt = HeatConf;
166
 
167
				while (akt)
168
				{
169
					if (akt->rnum == hta->rnum)
170
					{
171
						akt->soll = hta->soll;
172
						akt->night = hta->night;
173
						akt->minimal = hta->minimal;
174
						akt->start = hta->start1;
175
						akt->end = hta->end1;
176
						akt->wstart = hta->start2;
177
						akt->wend = hta->end2;
178
						break;
179
					}
180
 
181
					akt = akt->next;
182
				}
183
 
184
				hta = hta->next;
185
			}
186
 
187
			akt = HeatConf;
188
		}
189
 
5 andreas 190
		while (akt)
191
		{
192
			tmp = getTemp(akt->tempsensor);
193
 
194
			if (tmp < 99.0 && tmp > -99.0)
195
				akt->ist = tmp;
196
 
9 andreas 197
			if (evaluateTemp(akt->rnum, tmp))			// Should we start heating?
198
				startHeat(akt->rnum);					// yes
199
			else										// Or should we stop heating
200
				stopHeat(akt->rnum);					// yes
201
 
5 andreas 202
			akt = akt->next;
203
		}
204
 
205
		i++;
206
 
207
		if (i > 60)
208
		{
209
			i = 0;
210
			akt = HeatConf;
211
 
212
			while (akt)
213
			{
214
				answer("IST:" + itostring(akt->rnum) + ":" + doubleToString(akt->ist, 2) + ";");
215
				akt = akt->next;
216
			}
217
		}
218
 
219
		sleep(1);
220
	}
221
 
222
	rrun = false;
4 andreas 223
}
224
 
12 andreas 225
void heating::controlHeat (int room, bool stst)
9 andreas 226
{
227
HCONF *akt = HeatConf;
228
int pin;
229
 
230
	while (akt)
231
	{
232
		if (akt->rnum == room)
233
			break;
234
 
235
		akt = akt->next;
236
	}
237
 
238
	if (!akt || akt->valve)
239
		return;
240
 
241
	switch (akt->gpio)
242
	{
243
		case 1: pin = PIN1; break;
244
		case 2: pin = PIN2; break;
245
		case 3: pin = PIN3; break;
246
		case 4: pin = PIN4; break;
247
		case 5: pin = PIN5; break;
248
		case 6: pin = PIN6; break;
249
		case 7: pin = PIN7; break;
250
		case 8: pin = PIN8; break;
251
		case 9: pin = PIN9; break;
252
		default:
253
			return;
254
	}
255
 
256
	// Enable the GPIO pin to start heating
257
	if (!bcm2835_init())
258
		return;
259
 
260
	// Set the pin to be an output
261
	bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
262
	// Turn it on
12 andreas 263
	bcm2835_gpio_write(pin, (stst)? HIGH : LOW);
264
	akt->valve = stst;
9 andreas 265
	bcm2835_close();
266
}
267
 
4 andreas 268
double heating::incSoll(int room)
269
{
270
HCONF *akt;
271
char *zErrMsg = nullptr;
272
int rc;
273
sqlite3 *db;
274
sqlite3_stmt *res;
275
string query;
276
 
277
	rc = sqlite3_open(Configure.home, &db);
278
 
279
	if (rc)
280
	{
281
		syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
282
		return 0.0;
283
	}
284
 
285
	if ((akt = findRoom(room)) != nullptr)
286
	{
287
		if (akt->soll >= 30.0)
288
		{
289
			sqlite3_close(db);
290
			return akt->soll;
291
		}
292
 
293
		akt->soll += 0.5;
5 andreas 294
		query = "update heating set soll = " + doubleToString(akt->soll, 2) + " where id = " + itostring(room);
4 andreas 295
 
5 andreas 296
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 297
		{
298
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
299
			sqlite3_free(zErrMsg);
300
			sqlite3_close(db);
301
			akt->soll -= 0.5;
302
			return akt->soll;
303
		}
304
 
305
		sqlite3_close(db);
5 andreas 306
		stringstream stream;
307
		stream << "SOLL:" << setprecision(2) << akt->soll << ";";
308
		answer(stream.str());
4 andreas 309
		return akt->soll;
310
	}
311
 
312
	return 0.0;
313
}
314
 
315
double heating::decSoll(int room)
316
{
317
HCONF *akt;
318
char *zErrMsg = nullptr;
319
int rc;
320
sqlite3 *db;
321
sqlite3_stmt *res;
322
string query;
323
 
324
	rc = sqlite3_open(Configure.home, &db);
325
 
326
	if (rc)
327
	{
328
		syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
329
		return 0.0;
330
	}
331
 
332
	if ((akt = findRoom(room)) != nullptr)
333
	{
334
		if (akt->soll <= 10.0)
335
		{
336
			sqlite3_close(db);
337
			return akt->soll;
338
		}
339
 
340
		akt->soll -= 0.5;
5 andreas 341
		query = "update heating set soll = " + doubleToString(akt->soll, 2) + " where id = " + itostring(room);
4 andreas 342
 
5 andreas 343
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 344
		{
345
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
346
			sqlite3_free(zErrMsg);
347
			sqlite3_close(db);
348
			akt->soll += 0.5;
349
			return akt->soll;
350
		}
351
 
352
		sqlite3_close(db);
5 andreas 353
		stringstream stream;
354
		stream << "SOLL:" << setprecision(2) << akt->soll << ";";
355
		answer(stream.str());
4 andreas 356
		return akt->soll;
357
	}
358
 
359
	return 0.0;
360
}
361
 
9 andreas 362
void heating::setSoll (int room, double val)
363
{
364
HCONF *akt = HeatConf;
365
char *zErrMsg = nullptr;
366
int rc;
367
sqlite3 *db;
368
sqlite3_stmt *res;
369
string query;
370
 
371
	if (val < 10.0 || val > 30.0)
372
	{
373
		syslog(LOG_INFO, "Invalid value for maximum room temperature!");
374
		return;
375
	}
376
 
377
	rc = sqlite3_open(Configure.home, &db);
378
 
379
	if (rc)
380
	{
381
		syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
382
		return;
383
	}
384
 
385
	if (room > 0 && (akt = findRoom(room)) != nullptr)
386
	{
387
		akt->soll = val;
388
		query = "update heating set soll = " + doubleToString(val, 2) + " where id = " + itostring(room);
389
 
390
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
391
		{
392
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
393
			sqlite3_free(zErrMsg);
394
			sqlite3_close(db);
395
			return;
396
		}
397
 
398
		query = "SOLL:" + itostring(room) + ":" + doubleToString(val, 2) + ";";
399
		answer(query);
400
	}
401
 
402
	sqlite3_close(db);
403
}
404
 
4 andreas 405
void heating::setNight(int room, double val)
406
{
407
HCONF *akt;
408
char *zErrMsg = nullptr;
409
int rc;
410
sqlite3 *db;
411
sqlite3_stmt *res;
412
string query;
413
 
414
	if (val < 10.0 || val > 25.0)
415
	{
416
		syslog(LOG_INFO, "Invalid value for the night!");
417
		return;
418
	}
419
 
420
	rc = sqlite3_open(Configure.home, &db);
421
 
422
	if (rc)
423
	{
424
		syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
425
		return;
426
	}
427
 
428
	if (room > 0 && (akt = findRoom(room)) != nullptr)
429
	{
430
		akt->night = val;
5 andreas 431
		query = "update heating set night = " + doubleToString(val, 2) + " where id = " + itostring(room);
432
 
433
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 434
		{
435
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
436
			sqlite3_free(zErrMsg);
437
			sqlite3_close(db);
438
			return;
439
		}
440
 
5 andreas 441
		query = "NIGHT:" + itostring(room) + ":" + doubleToString(val, 2) + ";";
442
		answer(query);
4 andreas 443
	}
444
	else if (room == 0)
445
	{
446
		glb_night = val;
5 andreas 447
		query = "update glbheat set night = " + doubleToString(val, 2);
4 andreas 448
 
5 andreas 449
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 450
		{
451
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
452
			sqlite3_free(zErrMsg);
453
			sqlite3_close(db);
454
			return;
455
		}
5 andreas 456
 
457
		query = "CONFIG:GLOBAL NIGHT:" + doubleToString(val, 2) + ";";
458
		answer(query);
4 andreas 459
	}
460
 
461
	sqlite3_close(db);
462
}
463
 
464
void heating::setMinimal(int room, double val)
465
{
466
HCONF *akt;
467
char *zErrMsg = nullptr;
468
int rc;
469
sqlite3 *db;
470
sqlite3_stmt *res;
471
string query;
472
 
473
	if (val < 10.0 || val > 20.0)
474
	{
475
		syslog(LOG_INFO, "Invalid value for the night!");
476
		return;
477
	}
478
 
479
	rc = sqlite3_open(Configure.home, &db);
480
 
481
	if (rc)
482
	{
483
		syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
484
		return;
485
	}
486
 
487
	if (room > 0 && (akt = findRoom(room)) != nullptr)
488
	{
489
		akt->minimal = val;
5 andreas 490
		query = "update heating set minimal = " + doubleToString(val, 2) + " where id = " + itostring(room);
4 andreas 491
 
5 andreas 492
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 493
		{
494
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
495
			sqlite3_free(zErrMsg);
496
			sqlite3_close(db);
497
			return;
498
		}
5 andreas 499
 
500
		query = "MINIMAL:" + itostring(room) + ":" + doubleToString(val, 2) + ";";
501
		answer(query);
4 andreas 502
	}
503
	else if (room == 0)
504
	{
505
		glb_minimal = val;
5 andreas 506
		query = "update glbheat set minimal = " + doubleToString(val, 2);
4 andreas 507
 
5 andreas 508
		if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 509
		{
510
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
511
			sqlite3_free(zErrMsg);
512
			sqlite3_close(db);
513
			return;
514
		}
5 andreas 515
 
516
		query = "CONFIG:GLOBAL MINIMAL:" + doubleToString(val, 2) + ";";
517
		answer(query);
4 andreas 518
	}
519
 
520
	sqlite3_close(db);
521
}
522
 
523
bool heating::switchOnOff(bool what)
524
{
525
HCONF *akt;
526
char *zErrMsg = nullptr;
527
int rc;
528
sqlite3 *db;
529
sqlite3_stmt *res;
530
string query;
531
 
532
	rc = sqlite3_open(Configure.home, &db);
533
 
534
	if (rc)
535
	{
536
		syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
5 andreas 537
		return onoff;
4 andreas 538
	}
539
 
540
	onoff = what;
5 andreas 541
	query = "update glbheat set onoff = " + (what)?"1":"0";
4 andreas 542
 
5 andreas 543
	if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
4 andreas 544
	{
545
		syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
546
		sqlite3_free(zErrMsg);
547
		sqlite3_close(db);
5 andreas 548
		return onoff;
4 andreas 549
	}
550
 
551
	sqlite3_close(db);
5 andreas 552
	query = "POWER:" + (onoff) ? "ON;" : "OFF;";
553
	answer(query);
554
	return onoff;
4 andreas 555
}
556
 
557
double heating::getNight (int room)
558
{
559
HCONF *akt = findRoom(room);
560
 
561
	if (akt && akt->night > 0.0)
562
		return akt->night;
563
 
564
	return glb_night;
565
}
566
 
567
double heating::getMinimal (int room)
568
{
569
HCONF *akt = findRoom(room);
570
 
571
	if (akt && akt->minimal > 0.0)
572
		return akt->minimal;
573
 
574
	return glb_minimal;
575
}
576
 
3 andreas 577
void heating::destroyHConf()
578
{
579
HCONF *p, *next;
580
 
581
	p = HeatConf;
582
 
583
	while (p)
584
	{
585
		next = p->next;
586
		delete p;
587
		p = next;
588
	}
589
 
590
	HeatConf = nullptr;
591
}
592
 
5 andreas 593
void heating::getConfig (int s1)
594
{
595
HCONF *akt = HeatConf;
596
stringstream stream;
597
 
598
	stream << "CONFIG:GLOBAL NIGHT:" << setprecision(2) << glb_night << ";";
599
	write(s1, stream.str().c_str(), stream.str().length());
600
	stream << "CONFIG:GLOBAL MINIMAL:" << setprecision(2) << glb_minimal << ";";
601
	write(s1, stream.str().c_str(), stream.str().length());
602
	stream << "CONFIG:ONOFF:" << (onoff) ? "1;" : "0;";
603
	write(s1, stream.str().c_str(), stream.str().length());
604
 
605
	while (akt)
606
	{
607
		stream << "CONFIG:ROOM:" << akt->rnum << ":" << string(akt->rname) << ":" <<
608
			setprecision(2) << akt->soll << ":" << akt->night << ":" <<
42 andreas 609
			akt->minimal << ":" << timeToStr(akt->start) << ":" << timeToStr(akt->end) << ":" <<
610
			akt->wstart << ":" << akt->wend << ";";
5 andreas 611
		write(s1, stream.str().c_str(), stream.str().length());
612
 
613
		akt = akt->next;
614
	}
615
}
616
 
4 andreas 617
heating::HCONF* heating::findRoom (int room)
618
{
619
HCONF *akt = HeatConf;
620
 
621
	while (akt)
622
	{
623
		if (akt->rnum == room)
624
			return akt;
625
 
626
		akt = akt->next;
627
	}
628
 
629
	return nullptr;
630
}
631
 
3 andreas 632
bool heating::readConf()
633
{
634
ifstream cfile;
635
char line[1024], hv0[256], hv1[128];
636
char *p;
4 andreas 637
CNFSTAT cs;
638
HCONF *akt = HeatConf;
3 andreas 639
 
640
	// If there's no config file, inform the user about it.
641
	cfile.open(Configure.heatconf, ios::in);
642
 
643
	if ((cfile.rdstate() & std::ifstream::failbit) != 0)
644
	{
645
		syslog(LOG_WARNING,"Error opening the config file: %s", strerror(errno));
646
		return false;
647
	}
648
 
649
	syslog(LOG_INFO, "Found config file %s.", Configure.heatconf);
4 andreas 650
	cs = NONE;
3 andreas 651
	// Here we've found a config file. We'll read it and set the
652
	// contents to the structure
653
	while (cfile.getline(&line[0], sizeof(line)))
654
	{
655
		int len;
656
 
657
		trim (&line[0]);
658
 
659
		if (line[0] == '#' || !char_traits<char>::length(line))
660
			continue;
661
 
4 andreas 662
		if ((p = findc(line, char_traits<char>::length(line), ']')) != NULL)
663
		{
664
			char *x;
665
 
666
			*(p+1) = 0;
667
 
668
			if (!compcase(line, "[global]"))
669
			{
670
				cs = GLOBAL;
671
				continue;
672
			}
673
 
674
			if ((x = findc(line, char_traits<char>::length(line), '[')) != NULL)
675
			{
676
				*p = 0;
677
				akt = appendHConf();
678
				strncpy(akt->rname, x+1, sizeof(akt->rname) - 1);
679
				akt->soll = 20.0;
680
				cs = ROOM;
681
				continue;
682
			}
683
			else
684
				cs = NONE;
685
		}
686
 
687
		if ((p = findc(line, char_traits<char>::length(line), '=')) == NULL || cs == NONE)
3 andreas 688
			continue;
689
 
690
		*p = 0;
691
		p++;
692
		len = char_traits<char>::length(line);
693
 
694
		if (len > (int)sizeof(hv0))
695
			len = (int)sizeof(hv0) - 1;
696
 
697
		strncpy(hv0, line, len);
698
		hv0[len] = 0;
699
		trim (hv0);
700
		len = char_traits<char>::length(p);
701
 
702
		if (len > (int)sizeof(hv1))
703
			len = (int)sizeof(hv0) - 1;
704
 
705
		strncpy(hv1, p, len);
706
		hv1[len] = 0;
707
		trim (hv1);
708
 
4 andreas 709
		if (!compcase(hv0, "minimal"))
710
		{
711
			if (cs == GLOBAL)
712
				glb_minimal = atof (hv1);
713
			else
714
				akt->minimal = atof (hv1);
715
		}
3 andreas 716
 
4 andreas 717
		if (!compcase(hv0, "night"))
718
		{
719
			if (cs == GLOBAL)
720
				glb_night = atof (hv1);
721
			else
722
				akt->night = atof (hv1);
723
		}
724
 
725
		if (!compcase(hv0, "OnOff") && cs == GLOBAL)
726
			onoff = ToBool(hv1);
727
 
12 andreas 728
		if (!compcase(hv0, "OutSensor") && cs == GLOBAL)
729
			outSensor = atoi(hv1);
730
 
4 andreas 731
		if (cs == ROOM)
732
		{
733
			if (!compcase(hv0, "soll"))
734
				akt->soll = atoi(hv1);
735
 
736
			if (!compcase(hv0, "Sensor"))
737
				akt->tempsensor = atoi(hv1);
738
 
739
			if (!compcase(hv0, "GPIO"))
740
				akt->gpio = atoi(hv1);
741
 
742
			if (!compcase(hv0, "StartTime"))
13 andreas 743
			{
4 andreas 744
				akt->start = strToTime(hv1);
14 andreas 745
				debug("StartTime: " + itostring(akt->start) + "(" + ToString(hv1) + ")");
13 andreas 746
			}
4 andreas 747
 
748
			if (!compcase(hv0, "EndTime"))
13 andreas 749
			{
4 andreas 750
				akt->end = strToTime(hv1);
14 andreas 751
				debug("EndTime: " + itostring(akt->end) + "(" + ToString(hv1) + ")");
13 andreas 752
			}
42 andreas 753
 
754
			if (!compcase(hv0, "WorkStartTime"))
755
			{
756
				akt->wstart = strToTime(hv1);
757
				debug("WorkStartTime: " + itostring(akt->wstart) + "(" + ToString(hv1) + ")");
758
			}
759
 
760
			if (!compcase(hv0, "WorkEndTime"))
761
			{
762
				akt->wend = strToTime(hv1);
763
				debug("WorkEndTime: " + itostring(akt->wend) + "(" + ToString(hv1) + ")");
764
			}
4 andreas 765
		}
766
	}
767
 
768
	cfile.close ();
769
	return true;
770
}
771
 
772
 
773
bool heating::buildDB()
774
{
775
sqlite3 *db;
776
sqlite3_stmt *res;
777
int rc;
778
char *zErrMsg = nullptr;
779
string query;
780
 
781
	if (access(Configure.home, R_OK | W_OK))		// Try to create a new database if it doesn't exist
782
	{
783
		HCONF *akt;
784
 
18 andreas 785
		debug("Creating SQL3 database.");
4 andreas 786
		rc = sqlite3_open(Configure.home, &db);
787
 
788
		if (rc)
789
		{
790
			syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
791
			return false;
792
		}
793
 
794
		query = "create table \"main\".\"heating\" (";
795
		query.append("\"id\" INTEGER PRIMARY KEY NOT NULL,");
796
		query.append("\"name\" TEXT NOT NULL,");
797
		query.append("\"soll\" REAL,");
798
		query.append("\"night\" REAL,");
799
		query.append("\"minimal\" REAL,");
800
		query.append("\"start\" INTEGER,");
42 andreas 801
		query.append("\"end\" INTEGER,");
802
		query.append("\"wstart\" INTEGER,");
803
		query.append("\"wend\" INTEGER");
4 andreas 804
		query.append(")");
5 andreas 805
		rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
4 andreas 806
 
807
		if (rc != SQLITE_OK)
808
		{
809
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
810
			sqlite3_free(zErrMsg);
811
			sqlite3_close(db);
812
			return false;
813
		}
814
		else
815
			syslog(LOG_INFO, "Database \"heating\" was successfully created.");
816
 
10 andreas 817
		query = "create table \"main\".\"kitable\" (";
818
		query.append("\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
12 andreas 819
		query.append("\"rnum\" INTEGER NOT NULL,");
10 andreas 820
		query.append("\"diffout\" INTEGER,");
821
		query.append("\"diffin\" INTEGER,");
822
		query.append("\"heat\" INTEGER)");
823
		rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
824
 
825
		if (rc != SQLITE_OK)
826
		{
827
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
828
			sqlite3_free(zErrMsg);
829
			sqlite3_close(db);
830
			return false;
831
		}
832
		else
833
			syslog(LOG_INFO, "Database \"kitable\" was successfully created.");
834
 
835
		query = "create table \"main\".\"glbheat\" (";
836
		query.append("\"night\" REAL,");
837
		query.append("\"minimal\" REAL,");
838
		query.append("\"onoff\" INTEGER");
839
		query.append(")");
840
		rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
40 andreas 841
 
10 andreas 842
		if (rc != SQLITE_OK)
843
		{
844
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
845
			sqlite3_free(zErrMsg);
846
			sqlite3_close(db);
847
			return false;
848
		}
849
		else
850
			syslog(LOG_INFO, "Database \"glbheat\" was successfully created.");
40 andreas 851
 
4 andreas 852
		// Insert the values from the config file into the table
18 andreas 853
		debug("Inserting configuration into SQL3 database");
4 andreas 854
		akt = HeatConf;
40 andreas 855
 
4 andreas 856
		while (akt)
857
		{
42 andreas 858
			query = "insert into heating (id, name, soll, night, minimal, start, end, wstart, wend) values (";
4 andreas 859
			query.append(itostring(akt->rnum));
10 andreas 860
			query.append(",\""+string(akt->rname)+"\"");
5 andreas 861
			query.append(","+doubleToString(akt->soll, 2));
862
			query.append(","+doubleToString(akt->night, 2));
863
			query.append(","+doubleToString(akt->minimal, 2));
4 andreas 864
			query.append(","+itostring(akt->start));
865
			query.append(","+itostring(akt->end));
42 andreas 866
			query.append(","+itostring(akt->wstart));
867
			query.append(","+itostring(akt->wend));
4 andreas 868
			query.append(")");
5 andreas 869
			rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
4 andreas 870
 
871
			if (rc != SQLITE_OK)
872
			{
873
				syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
874
				sqlite3_free(zErrMsg);
875
				sqlite3_close(db);
876
				return false;
877
			}
878
 
879
			akt = akt->next;
15 andreas 880
		}
4 andreas 881
 
15 andreas 882
		query = "insert into glbheat (night, minimal, onoff) values (";
883
		query.append(doubleToString(glb_night, 2));
884
		query.append(","+doubleToString(glb_minimal, 2));
885
		query.append(","+itostring(onoff));
886
		query.append(")");
887
		rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
888
 
889
		if (rc != SQLITE_OK)
890
		{
891
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
892
			sqlite3_free(zErrMsg);
893
			sqlite3_close(db);
894
			return false;
4 andreas 895
		}
896
 
897
		sqlite3_close(db);
898
	}
899
	else
900
	{
901
		HCONF *akt;
15 andreas 902
 
18 andreas 903
		debug("Reading configuration from SQL3 database");
4 andreas 904
		rc = sqlite3_open(Configure.home, &db);
15 andreas 905
 
4 andreas 906
		if (rc)
907
		{
908
			syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
909
			return false;
910
		}
911
 
912
		akt = HeatConf;
40 andreas 913
 
4 andreas 914
		while (akt)
3 andreas 915
		{
18 andreas 916
			debug("Queriing "+itostring(akt->rnum)+", "+ToString(akt->rname));
4 andreas 917
			query = "select id, name, soll, night, minimal, start, end from heating where id = "+itostring(akt->rnum);
918
 
5 andreas 919
			if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
4 andreas 920
			{
921
				syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
922
				sqlite3_close(db);
923
				return false;
924
			}
925
 
926
			if ((rc = sqlite3_step(res)) == SQLITE_ROW)
927
			{
928
				strncpy(akt->rname, (const char *)sqlite3_column_text(res, 1), sizeof(akt->rname) - 1);
929
				akt->soll = sqlite3_column_double(res, 2);
930
				akt->night = sqlite3_column_double(res, 3);
931
				akt->minimal = sqlite3_column_double(res, 4);
932
				akt->start = (time_t)sqlite3_column_int(res, 5);
933
				akt->end = (time_t)sqlite3_column_int(res, 6);
934
			}
935
 
44 andreas 936
			sqlite3_finalize(res);
12 andreas 937
			query = "select rnum, diffout, diffin, heat from kitable where rnum = " + itostring(akt->rnum);
938
 
939
			if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
940
			{
941
				syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
942
				sqlite3_close(db);
943
				return false;
944
			}
945
 
946
			while ((rc = sqlite3_step(res)) == SQLITE_ROW)
947
			{
948
				HTABLE *tb = appendHTable(akt);
15 andreas 949
				tb->tempDifferOut = sqlite3_column_double(res, 1);
950
				tb->tempDifferIn = sqlite3_column_double(res, 2);
12 andreas 951
				tb->heat = sqlite3_column_int(res, 3);
18 andreas 952
				debug("KITABLE: read "+itostring(sqlite3_column_int(res, 0)));
12 andreas 953
			}
954
 
44 andreas 955
			sqlite3_finalize(res);
4 andreas 956
			akt = akt->next;
3 andreas 957
		}
4 andreas 958
 
18 andreas 959
		debug("Reading global parameters from SQL3 database");
4 andreas 960
		query = "select night, minimal, onoff from glbheat";
961
 
5 andreas 962
		if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
4 andreas 963
		{
964
			syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
965
			sqlite3_close(db);
966
			return false;
967
		}
968
 
969
		if ((rc = sqlite3_step(res)) == SQLITE_ROW)
970
		{
971
			glb_night = sqlite3_column_double(res, 0);
972
			glb_minimal = sqlite3_column_double(res, 1);
973
			onoff = (time_t)sqlite3_column_int(res, 2);
974
		}
975
 
44 andreas 976
		sqlite3_finalize(res);
4 andreas 977
		sqlite3_close(db);
3 andreas 978
	}
4 andreas 979
 
18 andreas 980
	debug("All database operations done");
3 andreas 981
	return true;
982
}
983
 
5 andreas 984
void heating::setHandle (int fd)
985
{
986
	// First look into table to make sure the handle isn't already in the table
987
	for (int i = 0; i < 100; i++)
988
	{
989
		if (s1[i] == fd)
990
			return;
991
	}
992
 
993
	// Because we're here, the handle is not in the table. So insert it now.
994
	for (int i = 0; i < 100; i++)
995
	{
996
		if (s1[i] < 0)
997
		{
998
			s1[i] = fd;
999
			break;
1000
		}
1001
	}
1002
}
1003
 
1004
void heating::removeHandle (int fd)
1005
{
1006
	for (int i = 0; i < 100; i++)
1007
	{
1008
		if (s1[i] == fd)
1009
		{
1010
			s1[i] = -1;
1011
			break;
1012
		}
1013
	}
1014
}
1015
 
1016
void heating::answer (string msg)
1017
{
1018
	for (int i = 0; i < 100; i++)
1019
	{
1020
		if (s1[i] > 0)
1021
			write(s1[i], msg.c_str(), strlen(msg.c_str()));
1022
	}
1023
}
1024
 
4 andreas 1025
time_t heating::strToTime (char* str)
1026
{
14 andreas 1027
int hour, min;
4 andreas 1028
 
7 andreas 1029
	if (strchr(str, ':') != NULL)
1030
	{
14 andreas 1031
		sscanf(str, "%d:%d", &hour, &min);
1032
		return (time_t)hour * 3600 + (time_t)min * 60;
7 andreas 1033
	}
1034
 
1035
	return 0;
4 andreas 1036
}
1037
 
5 andreas 1038
string heating::timeToStr(time_t t)
1039
{
14 andreas 1040
int hour, min;
38 andreas 1041
char hv0[48];
5 andreas 1042
 
14 andreas 1043
	hour = (int)(t / 3600);
39 andreas 1044
	min = (int)((t - (time_t)hour * 3600) / 60);
38 andreas 1045
	sprintf(hv0, "%02d:%02d", hour, min);
1046
	string zeit(hv0);
5 andreas 1047
	return zeit;
1048
}
1049
 
8 andreas 1050
time_t heating::getTime()
1051
{
1052
tm *ltm, ptm;
1053
time_t t = time(NULL);
1054
 
1055
	ltm = localtime(&t);
1056
	memset (&ptm, 0, sizeof(struct tm));
1057
	ptm.tm_hour = ltm->tm_hour;
1058
	ptm.tm_min = ltm->tm_min;
1059
	ptm.tm_isdst = ltm->tm_isdst;
1060
	return mktime(&ptm);
1061
}
1062
 
3 andreas 1063
heating::HCONF* heating::appendHConf()
1064
{
1065
HCONF *akt, *p;
1066
 
1067
	akt = new HCONF;
1068
	memset(akt, 0, sizeof(HCONF));
1069
 
1070
	if (HeatConf == nullptr)
1071
		HeatConf = akt;
1072
	else
1073
	{
1074
		p = HeatConf;
1075
 
1076
		while (p->next)
1077
			p = p->next;
1078
 
1079
		p->next = akt;
1080
	}
1081
 
1082
	rm++;
1083
	akt->rnum = rm;
1084
	return akt;
1085
}
8 andreas 1086
 
1087
heating::HTABLE* heating::appendHTable(HCONF *ht)
1088
{
1089
HTABLE *akt, *p;
1090
 
1091
	akt = new HTABLE;
1092
	memset(akt, 0, sizeof(HTABLE));
1093
 
1094
	if (ht->hTable == nullptr)
1095
		ht->hTable = akt;
1096
	else
1097
	{
1098
		p = ht->hTable;
1099
 
1100
		while (p->next)
1101
			p = p->next;
1102
 
1103
		p->next = akt;
1104
	}
1105
 
1106
	return akt;
1107
}
1108
 
1109
bool heating::evaluateTemp (int room, double temp)
1110
{
1111
HCONF *akt;
9 andreas 1112
double soll, diffOut, diffIn;
8 andreas 1113
 
1114
	akt = HeatConf;
1115
 
1116
	while (akt)
1117
	{
1118
		if (akt->rnum == room)
1119
			break;
1120
 
1121
		akt = akt->next;
1122
	}
1123
 
1124
	if (!akt)
1125
		return false;
1126
 
9 andreas 1127
	switch(akt->status)
1128
	{
1129
		case NORMAL:
1130
			soll = akt->soll;
1131
			diffOut = trunc(soll - getOutside());
1132
			diffIn = trunc(soll - temp);
1133
		break;
1134
 
1135
		case NIGHT:
1136
			if (akt->night)
1137
				soll = akt->night;
1138
			else
1139
				soll = glb_night;
1140
 
1141
			diffOut = trunc(soll - getOutside());
1142
			diffIn = trunc(soll - temp);
1143
		break;
1144
 
1145
		case OFF:
1146
			if (akt->minimal)
1147
				soll = akt->minimal;
1148
			else
1149
				soll = glb_minimal;
1150
 
1151
			diffOut = trunc(soll - getOutside());
1152
			diffIn = trunc(soll - temp);
1153
		break;
1154
	}
1155
 
8 andreas 1156
	if (!akt->hTable)			// If we've no heating table, we've to evaluate the temperature difference
1157
	{
9 andreas 1158
		if (!akt->valve && temp <= (soll - 0.5))
8 andreas 1159
		{
1160
			HTABLE *tb;
1161
			tb = appendHTable(akt);
9 andreas 1162
			tb->tempDifferOut = trunc(soll - getOutside());
1163
			tb->tempDifferIn = trunc(soll - temp);
8 andreas 1164
			tb->start = getTime();
1165
			return true;
1166
		}
1167
 
9 andreas 1168
		if (akt->valve && temp < soll)
8 andreas 1169
			return true;
1170
		else
1171
			return false;
1172
	}
9 andreas 1173
	else if (akt->valve && temp < soll)
8 andreas 1174
		return true;
9 andreas 1175
	else if (akt->valve && temp >= soll)
8 andreas 1176
	{
1177
		HTABLE *tb = akt->hTable;
1178
 
9 andreas 1179
		while (tb)
1180
		{
1181
			if (tb->start)
1182
				break;
8 andreas 1183
 
9 andreas 1184
			if (tb->next)
1185
				tb = tb->next;
1186
			else
1187
				break;
1188
		}
1189
 
1190
		if (tb && tb->start)
8 andreas 1191
		{
1192
			time_t t = getTime();
1193
 
1194
			if (t > tb->start)
1195
				tb->heat = t - tb->start;
1196
			else
1197
				tb->heat = (86400 + t) - tb->start;
1198
 
1199
			tb->start = 0;
1200
		}
1201
 
1202
		return false;
1203
	}
9 andreas 1204
	else if (!akt->valve && temp <= (soll - 0.5))		// If temperature is beyond soll, start immediately
1205
		return true;
1206
	else
8 andreas 1207
	{
9 andreas 1208
		HTABLE *tb = akt->hTable;
1209
		time_t t = getTime();
1210
		// Check the current time and look in learned table whether we should
1211
		// start heating or not.
1212
		if (tb)											// Only if we already have a table!
1213
		{												// Yes there is one, so search through it.
1214
			bool strt = false;
1215
 
1216
			if (diffIn <= 0.0)							// Is it warmer inside than it should be?
1217
				return false;							// yes, then turn heating off
1218
 
1219
			if (akt->status == OFF && diffIn >= 0.5)	// Are we in off mode and is the temperature lower than it should be?
1220
				return true;							// yes, then start heating
1221
			else if (akt->status == OFF)				// otherwise ...
1222
				return false;							// no heating
1223
 
1224
			while (tb)									// Search through learned table
1225
			{
1226
				if (!tb->start && tb->tempDifferOut == diffOut && tb->tempDifferIn == diffIn)
1227
					break;
1228
 
1229
				tb = tb->next;
1230
			}
1231
 
1232
			if (tb)										// Have we found a known time to heat?
1233
			{											// Yes, then look if it's time to heat
1234
				if (akt->status == NORMAL)
1235
				{
1236
					if ((t + tb->heat) >= akt->start && (t + tb->heat) <= akt->end)
1237
						return true;
1238
				}
1239
				else if (akt->status == NIGHT)
1240
				{
1241
					if ((t + tb->heat) >= akt->end || t < (akt->end - tb->heat))
1242
						return true;
1243
				}
1244
			}
1245
			else										// None of the saved temperture differences match.
1246
			{											// So test if it's time to add a new one
1247
				if (akt->status == NORMAL)
1248
				{
1249
					if ((t + 600) >= akt->start && (t + 600) <= akt->end)
1250
						strt = true;
1251
				}
1252
				else if (akt->status == NIGHT)
1253
				{
1254
					if ((t + 600) >= akt->end || t < (akt->end - 600))
1255
						strt = true;
1256
				}
1257
 
1258
				if (strt)
1259
				{
1260
					bool add = true;
1261
 
1262
					// Make sure there is no open start
1263
					tb = akt->hTable;
1264
 
1265
					while (tb)
1266
					{
1267
						if (tb->start)
1268
						{
1269
							add = false;
1270
							break;
1271
						}
1272
 
1273
						tb = tb->next;
1274
					}
1275
 
1276
					if (add)
1277
						tb = appendHTable(akt);
1278
 
1279
					tb->tempDifferOut = diffOut;
1280
					tb->tempDifferIn = diffIn;
1281
					tb->start = t;
1282
					return true;
1283
				}
1284
			}
1285
		}
8 andreas 1286
	}
9 andreas 1287
 
1288
	return false;
8 andreas 1289
}