Subversion Repositories heating

Rev

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