Subversion Repositories heating

Rev

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