1 #include <list>
2 
3 #include "stdafx.h"
4 #include "MultiFun.h"
5 #include "hardwaretypes.h"
6 #include "../main/Logger.h"
7 #include "../main/RFXtrx.h"
8 #include "../main/Helper.h"
9 #include "../main/localtime_r.h"
10 #include "../main/mainworker.h"
11 #include "../main/SQLHelper.h"
12 #include "csocket.h"
13 #include <boost/assign.hpp>
14 
15 #ifdef _DEBUG
16 	#define DEBUG_MultiFun
17 #endif
18 
19 #define BUFFER_LENGHT 100
20 #define MULTIFUN_POLL_INTERVAL 10 //TODO - to settings on www
21 
22 #define round(a) ( int ) ( a + .5 )
23 
24 typedef struct sensorType {
25 	std::string name;
26 	float div;
27 } Model;
28 
29 #define sensorsCount 16
30 #define registersCount 34
31 
32 typedef std::map<int, std::string> dictionary;
33 
34 static dictionary alarmsType = boost::assign::map_list_of
35 (0x0001, "BOILER STOP - FIRING FAILURE")
36 (0x0004, "BOILER OVERHEATING")
37 (0x0010, "EXTINUISHED BOILER")
38 (0x0080, "DAMAGED SENSOR BOILER")
39 (0x0100, "DAMAGED SENSOR FEEDER")
40 (0x0200, "FLUE GAS SENSOR")
41 (0x0400, "FAILURE - LOCK WORKS");
42 
43 static dictionary warningsType = boost::assign::map_list_of
44 (0x0001, "No external sensor")
45 (0x0002, "No room sensor 1")
46 (0x0004, "Wrong version supply module program")
47 (0x0008, "No return sensor")
48 (0x0010, "No room sensor 2")
49 (0x0020, "Open flap")
50 (0x0040, "Thermal protection has tripped");
51 
52 static dictionary devicesType = boost::assign::map_list_of
53 (0x0001, "C.H.1 PUMP")
54 (0x0002, "C.H.2 PUMP")
55 (0x0004, "RESERVE PUMP")
56 (0x0008, "H.W.U.PUMP")
57 (0x0010, "CIRCULATION PUMP")
58 (0x0020, "PUFFER PUMP")
59 (0x0040, "MIXER C.H.1 Close")
60 (0x0080, "MIXER C.H.1 Open")
61 (0x0100, "MIXER C.H.2 Close")
62 (0x0200, "MIXER C.H.2 Open");
63 
64 static dictionary statesType = boost::assign::map_list_of
65 (0x0001, "STOP")
66 (0x0002, "Firing")
67 (0x0004, "Heating")
68 (0x0008, "Maintain")
69 (0x0010, "Blanking");
70 
71 static sensorType sensors[sensorsCount] =
72 {
73 	{ "External", 10.0 },
74 	{ "Room 1", 10.0 },
75 	{ "Room 2", 10.0 },
76 	{ "Return", 10.0 },
77 	{ "C.H.1", 10.0 },
78 	{ "C.H.2", 10.0 },
79 	{ "H.W.U.", 10.0 },
80 	{ "Heat", 1.0 },
81 	{ "Flue gas", 10.0 },
82 	{ "Module", 10.0 },
83 	{ "Boiler", 10.0 },
84 	{ "Feeder", 10.0 },
85 	{ "Calculated Boiler", 10.0 },
86 	{ "Calculated H.W.U.", 10.0 },
87 	{ "Calculated C.H.1", 10.0 },
88 	{ "Calculated C.H.2", 10.0 }
89 };
90 
91 static dictionary quickAccessType = boost::assign::map_list_of
92 	(0x0001, "Shower")
93 	(0x0002, "Party")
94 	(0x0004, "Comfort")
95 	(0x0008, "Airing")
96 	(0x0010, "Frost protection");
97 
98 static std::string errors[4] =
99 {
100 	"Incorrect function code",
101 	"Incorrect register address",
102 	"Incorrect number of registers",
103 	"Server error"
104 };
105 
MultiFun(const int ID,const std::string & IPAddress,const unsigned short IPPort)106 MultiFun::MultiFun(const int ID, const std::string &IPAddress, const unsigned short IPPort) :
107 	m_IPPort(IPPort),
108 	m_IPAddress(IPAddress),
109 	m_socket(NULL),
110 	m_LastAlarms(0),
111 	m_LastWarnings(0),
112 	m_LastDevices(0),
113 	m_LastState(0),
114 	m_LastQuickAccess(0)
115 {
116 	_log.Log(LOG_STATUS, "MultiFun: Create instance");
117 	m_HwdID = ID;
118 
119 	m_isSensorExists[0] = false;
120 	m_isSensorExists[1] = false;
121 	m_isWeatherWork[0] = false;
122 	m_isWeatherWork[1] = false;
123 }
124 
~MultiFun()125 MultiFun::~MultiFun()
126 {
127 	_log.Log(LOG_STATUS, "MultiFun: Destroy instance");
128 }
129 
StartHardware()130 bool MultiFun::StartHardware()
131 {
132 	RequestStart();
133 
134 #ifdef DEBUG_MultiFun
135 	_log.Log(LOG_STATUS, "MultiFun: Start hardware");
136 #endif
137 
138 	m_thread = std::make_shared<std::thread>(&MultiFun::Do_Work, this);
139 	SetThreadNameInt(m_thread->native_handle());
140 	m_bIsStarted = true;
141 	sOnConnected(this);
142 	return (m_thread != nullptr);
143 }
144 
StopHardware()145 bool MultiFun::StopHardware()
146 {
147 #ifdef DEBUG_MultiFun
148 	_log.Log(LOG_STATUS, "MultiFun: Stop hardware");
149 #endif
150 
151 	if (m_thread)
152 	{
153 		RequestStop();
154 		m_thread->join();
155 		m_thread.reset();
156 	}
157 	m_bIsStarted = false;
158 	return true;
159 }
160 
Do_Work()161 void MultiFun::Do_Work()
162 {
163 #ifdef DEBUG_MultiFun
164 	_log.Log(LOG_STATUS, "MultiFun: Start work");
165 #endif
166 
167 	int sec_counter = MULTIFUN_POLL_INTERVAL;
168 
169 	bool firstTime = true;
170 
171 	while (!IsStopRequested(1000))
172 	{
173 		sec_counter++;
174 
175 		if (sec_counter % 12 == 0) {
176 			m_LastHeartbeat = mytime(NULL);
177 		}
178 
179 		if (sec_counter % MULTIFUN_POLL_INTERVAL == 0)
180 		{
181 			GetTemperatures();
182 			GetRegisters(firstTime);
183 			firstTime = false;
184 #ifdef DEBUG_MultiFun
185 			_log.Log(LOG_STATUS, "MultiFun: fetching changed data");
186 #endif
187 		}
188 	}
189 	DestroySocket();
190 }
191 
WriteToHardware(const char * pdata,const unsigned char)192 bool MultiFun::WriteToHardware(const char *pdata, const unsigned char /*length*/)
193 {
194 	const tRBUF *output = reinterpret_cast<const tRBUF*>(pdata);
195 
196 	if (output->ICMND.packettype == pTypeGeneralSwitch && output->LIGHTING2.subtype == sSwitchTypeAC)
197 	{
198 		const _tGeneralSwitch *general = reinterpret_cast<const _tGeneralSwitch*>(pdata);
199 
200 		if (general->id == 0x21)
201 		{
202 			int change;
203 			if (general->cmnd == gswitch_sOn)
204 			{
205 				change = m_LastQuickAccess | (general->unitcode);
206 			}
207 			else
208 			{
209 				change = m_LastQuickAccess & ~(general->unitcode);
210 			}
211 
212 			unsigned char buffer[100];
213 			unsigned char cmd[20];
214 			cmd[0] = 0x01; // transaction id (2 bytes)
215 			cmd[1] = 0x02;
216 			cmd[2] = 0x00; // protocol id (2 bytes)
217 			cmd[3] = 0x00;
218 			cmd[4] = 0x00; // length (2 bytes)
219 			cmd[5] = 0x09;
220 			cmd[6] = 0xFF; // unit id
221 			cmd[7] = 0x10; // function code
222 			cmd[8] = 0x00; // start address (2 bytes)
223 			cmd[9] = 0x21;
224 			cmd[10] = 0x00; // number of sensor (2 bytes)
225 			cmd[11] = 0x01;
226 			cmd[12] = 0x02; // number of bytes
227 			cmd[13] = 0x00;
228 			cmd[14] = (uint8_t)change;
229 
230 			int ret = SendCommand(cmd, 15, buffer, true);
231 			if (ret == 4)
232 				return true;
233 		}
234 	}
235 
236 	if (output->ICMND.packettype == pTypeThermostat && output->LIGHTING2.subtype == sTypeThermSetpoint)
237 	{
238 		const _tThermostat *therm = reinterpret_cast<const _tThermostat*>(pdata);
239 
240 		float temp = therm->temp;
241 		int calculatedTemp = (int)temp;
242 
243 		if ((therm->id2 == 0x1F || therm->id2 == 0x20) ||
244 			((therm->id2 == 0x1C || therm->id2 == 0x1D) && m_isWeatherWork[therm->id2 - 0x1C]))
245 		{
246 			calculatedTemp = (int)(temp * 5);
247 			calculatedTemp = calculatedTemp | 0x8000;
248 		}
249 
250 		unsigned char buffer[100];
251 		unsigned char cmd[20];
252 		cmd[0] = 0x01; // transaction id (2 bytes)
253 		cmd[1] = 0x02;
254 		cmd[2] = 0x00; // protocol id (2 bytes)
255 		cmd[3] = 0x00;
256 		cmd[4] = 0x00; // length (2 bytes)
257 		cmd[5] = 0x09;
258 		cmd[6] = 0xFF; // unit id
259 		cmd[7] = 0x10; // function code
260 		cmd[8] = 0x00; // start address (2 bytes)
261 		cmd[9] = therm->id2;
262 		cmd[10] = 0x00; // number of sensor (2 bytes)
263 		cmd[11] = 0x01;
264 		cmd[12] = 0x02; // number of bytes
265 		cmd[13] = 0x00;
266 		cmd[14] = (uint8_t)calculatedTemp;
267 
268 		int ret = SendCommand(cmd, 15, buffer, true);
269 		if (ret == 4)
270 			return true;
271 	}
272 
273 	return false;
274 }
275 
ConnectToDevice()276 bool MultiFun::ConnectToDevice()
277 {
278 	if (m_socket != NULL)
279 		return true;
280 
281 	m_socket = new csocket();
282 
283 	m_socket->connect(m_IPAddress.c_str(), m_IPPort);
284 
285 	if (m_socket->getState() != csocket::CONNECTED)
286 	{
287 		_log.Log(LOG_ERROR, "MultiFun: Unable to connect to specified IP Address on specified Port (%s:%d)", m_IPAddress.c_str(), m_IPPort);
288 		DestroySocket();
289 		return false;
290 	}
291 
292 	_log.Log(LOG_STATUS, "MultiFun: connected to %s:%d", m_IPAddress.c_str(), m_IPPort);
293 
294 	return true;
295 }
296 
DestroySocket()297 void MultiFun::DestroySocket()
298 {
299 	if (m_socket != NULL)
300 	{
301 #ifdef DEBUG_MultiFun
302 		_log.Log(LOG_STATUS, "MultiFun: destroy socket");
303 #endif
304 		delete m_socket;
305 		m_socket = NULL;
306 	}
307 }
308 
GetTemperatures()309 void MultiFun::GetTemperatures()
310 {
311 	unsigned char buffer[50];
312 	unsigned char cmd[12];
313 	cmd[0] = 0x01; // transaction id (2 bytes)
314 	cmd[1] = 0x02;
315 	cmd[2] = 0x00; // protocol id (2 bytes)
316 	cmd[3] = 0x00;
317 	cmd[4] = 0x00; // length (2 bytes)
318 	cmd[5] = 0x06;
319 	cmd[6] = 0xFF; // unit id
320 	cmd[7] = 0x04; // function code
321 	cmd[8] = 0x00; // start address (2 bytes)
322 	cmd[9] = 0x00;
323 	cmd[10] = 0x00; // number of sensor (2 bytes)
324 	cmd[11] = sensorsCount;
325 
326 	int ret = SendCommand(cmd, 12, buffer, false);
327 	if (ret > 0)
328 	{
329 		if ((ret != 1 + sensorsCount * 2) || (buffer[0] != sensorsCount * 2))
330 		{
331 			_log.Log(LOG_ERROR, "MultiFun: Receive wrong number of bytes");
332 		}
333 		else
334 		{
335 			for (int i = 0; i < sensorsCount; i++)
336 			{
337 				unsigned int val = (buffer[i * 2 + 1] & 127) * 256 + buffer[i * 2 + 2];
338 				int signedVal = (((buffer[i * 2 + 1] & 128) >> 7) * -32768) + val;
339 				float temp = signedVal / sensors[i].div;
340 
341 				if ((temp > -39) && (temp < 1000))
342 				{
343 					SendTempSensor(i, 255, temp, sensors[i].name);
344 				}
345 				if ((i == 1) || (i == 2))
346 				{
347 					m_isSensorExists[i - 1] = ((temp > -39) && (temp < 1000));
348 				}
349 			}
350 		}
351 
352 	}
353 	else
354 	{
355 		_log.Log(LOG_ERROR, "MultiFun: Receive info about temperatures failed");
356 	}
357 }
358 
GetRegisters(bool firstTime)359 void MultiFun::GetRegisters(bool firstTime)
360 {
361 	unsigned char buffer[100];
362 	unsigned char cmd[12];
363 	cmd[0] = 0x01; // transaction id (2 bytes)
364 	cmd[1] = 0x02;
365 	cmd[2] = 0x00; // protocol id (2 bytes)
366 	cmd[3] = 0x00;
367 	cmd[4] = 0x00; // length (2 bytes)
368 	cmd[5] = 0x06;
369 	cmd[6] = 0xFF; // unit id
370 	cmd[7] = 0x03; // function code
371 	cmd[8] = 0x00; // start address (2 bytes)
372 	cmd[9] = 0x00;
373 	cmd[10] = 0x00; // number of sensor (2 bytes)
374 	cmd[11] = registersCount;
375 
376 	int ret = SendCommand(cmd, 12, buffer, false);
377 	if (ret > 0)
378 	{
379 		if ((ret != 1 + registersCount * 2) || (buffer[0] != registersCount * 2))
380 		{
381 			_log.Log(LOG_ERROR, "MultiFun: Receive wrong number of bytes");
382 		}
383 		else
384 		{
385 			for (int i = 0; i < registersCount; i++)
386 			{
387 				int value = buffer[2 * i + 1] * 256 + buffer[2 * i + 2];
388 				switch (i)
389 				{
390 				case 0x00:
391 				{
392 					dictionary::iterator it = alarmsType.begin();
393 					for (; it != alarmsType.end(); ++it)
394 					{
395 						if (((*it).first & value) && !((*it).first & m_LastAlarms))
396 						{
397 							SendTextSensor(1, 0, 255, (*it).second, "Alarms");
398 						}
399 						else
400 							if (!((*it).first & value) && ((*it).first & m_LastAlarms))
401 							{
402 								SendTextSensor(1, 0, 255, "End - " + (*it).second, "Alarms");
403 							}
404 					}
405 					if (((m_LastAlarms != 0) != (value != 0)) || firstTime)
406 					{
407 						SendAlertSensor(0, 255, value ? 4 : 1, "", "Alarm");
408 					}
409 					m_LastAlarms = value;
410 					break;
411 				}
412 				case 0x01:
413 				{
414 					dictionary::iterator it = warningsType.begin();
415 					for (; it != warningsType.end(); ++it)
416 					{
417 						if (((*it).first & value) && !((*it).first & m_LastWarnings))
418 						{
419 							SendTextSensor(1, 1, 255, (*it).second, "Warnings");
420 						}
421 						else
422 							if (!((*it).first & value) && ((*it).first & m_LastWarnings))
423 							{
424 								SendTextSensor(1, 1, 255, "End - " + (*it).second, "Warnings");
425 							}
426 					}
427 					if (((m_LastWarnings != 0) != (value != 0)) || firstTime)
428 					{
429 						SendAlertSensor(1, 255, value ? 3 : 1, "", "Warning");
430 					}
431 					m_LastWarnings = value;
432 					break;
433 				}
434 				case 0x02:
435 				{
436 					dictionary::iterator it = devicesType.begin();
437 					for (; it != devicesType.end(); ++it)
438 					{
439 						if (((*it).first & value) && !((*it).first & m_LastDevices))
440 						{
441 							SendGeneralSwitch(2, (*it).first, 255, true, 0, (*it).second.c_str());
442 						}
443 						else
444 							if (!((*it).first & value) && ((*it).first & m_LastDevices))
445 							{
446 								SendGeneralSwitch(2, (*it).first, 255, false, 0, (*it).second.c_str());
447 							}
448 					}
449 					m_LastDevices = value;
450 
451 					float level = (float)((value & 0xFC00) >> 10);
452 					SendPercentageSensor(2, 1, 255, level, "BLOWER POWER");
453 					break;
454 				}
455 				case 0x03:
456 				{
457 					dictionary::iterator it = statesType.begin();
458 					for (; it != statesType.end(); ++it)
459 					{
460 						if (((*it).first & value) && !((*it).first & m_LastState))
461 						{
462 							SendTextSensor(3, 1, 255, (*it).second, "State");
463 						}
464 						else
465 							if (!((*it).first & value) && ((*it).first & m_LastState))
466 							{
467 								SendTextSensor(3, 1, 255, "End - " + (*it).second, "State");
468 							}
469 					}
470 					m_LastState = value;
471 
472 					float level = (float)((value & 0xFC00) >> 10);
473 					SendPercentageSensor(3, 1, 255, level, "Fuel Level");
474 					break;
475 				}
476 
477 				case 0x1C:
478 				case 0x1D:
479 				{
480 					char name[20];
481 					sprintf(name, "C.H. %d Temperature", i - 0x1C + 1);
482 
483 					float temp = (float)value;
484 					if ((value & 0x8000) == 0x8000)
485 					{
486 						temp = (float)((value & 0x0FFF) * 0.2);
487 					}
488 					m_isWeatherWork[i - 0x1C] = (value & 0x8000) == 0x8000;
489 					SendSetPointSensor((uint8_t)i, 1, 1, temp, name);
490 					break;
491 				}
492 
493 				case 0x1E:
494 				{
495 					SendSetPointSensor(0x1E, 1, 1, (float)value, "H.W.U. Temperature");
496 					break;
497 				}
498 
499 				case 0x1F:
500 				case 0x20:
501 				{
502 					char name[20];
503 					sprintf(name, "Lowering C.H. %d", i - 0x1F + 1);
504 
505 					if (m_isSensorExists[i - 0x1F])
506 					{
507 						float temp = (float)((value & 0x0FFF) * 0.2);
508 						SendSetPointSensor((uint8_t)i, 1, 1, temp, name);
509 					}
510 					else
511 					{
512 						//SendGeneralSwitch(i, 1, 255, state, level, name); // TODO - send level (dimmer)
513 					}
514 					break;
515 				}
516 
517 				case 0x21:
518 				{
519 					dictionary::iterator it = quickAccessType.begin();
520 					for (; it != quickAccessType.end(); ++it)
521 					{
522 						if (((*it).first & value) && !((*it).first & m_LastQuickAccess))
523 						{
524 							SendGeneralSwitch(0x21, (*it).first, 255, true, 0, (*it).second.c_str());
525 						}
526 						else
527 							if ((!((*it).first & value) && ((*it).first & m_LastQuickAccess)) || firstTime)
528 							{
529 								SendGeneralSwitch(0x21, (*it).first, 255, false, 0, (*it).second.c_str());
530 							}
531 					}
532 					m_LastQuickAccess = value;
533 					break;
534 				}
535 				default: break;
536 				}
537 			}
538 		}
539 
540 	}
541 	else
542 	{
543 		_log.Log(LOG_ERROR, "MultiFun: Receive info about registers failed");
544 	}
545 }
546 
547 // return length of answer (-1 = error)
SendCommand(const unsigned char * cmd,const unsigned int cmdLength,unsigned char * answer,bool write)548 int MultiFun::SendCommand(const unsigned char* cmd, const unsigned int cmdLength, unsigned char *answer, bool write)
549 {
550 	if (!ConnectToDevice())
551 	{
552 		return -1;
553 	}
554 
555 	std::lock_guard<std::mutex> lock(m_mutex);
556 
557 	unsigned char databuffer[BUFFER_LENGHT];
558 	int ret = -1;
559 
560 	if (m_socket->write((char*)cmd, cmdLength) != (int)cmdLength)
561 	{
562 		_log.Log(LOG_ERROR, "MultiFun: Send command failed");
563 		DestroySocket();
564 		return -1;
565 	}
566 
567 	bool bIsDataReadable = true;
568 	m_socket->canRead(&bIsDataReadable, 3.0f);
569 	if (bIsDataReadable)
570 	{
571 		memset(databuffer, 0, BUFFER_LENGHT);
572 		ret = m_socket->read((char*)databuffer, BUFFER_LENGHT, false);
573 	}
574 
575 	if ((ret <= 0) || (ret >= BUFFER_LENGHT))
576 	{
577 		_log.Log(LOG_ERROR, "MultiFun: no data received");
578 		return -1;
579 	}
580 
581 	if (ret > 8)
582 	{
583 		if (cmd[0] == databuffer[0] && cmd[1] == databuffer[1] && cmd[2] == databuffer[2] && cmd[3] == databuffer[3] && cmd[6] == databuffer[6])
584 		{
585 			if (cmd[7] == databuffer[7])
586 			{
587 				unsigned int answerLength = 0;
588 				for (int i = 0; i < ret - 8; i++) // skip prefix
589 				{
590 					answer[i] = databuffer[i + 8];
591 				}
592 				answerLength = ret - 8; // answer = frame - prefix
593 
594 				if ((int)databuffer[4] * 256 + (int)databuffer[5] == (unsigned char)(answerLength + 2))
595 				{
596 					if (write)
597 					{
598 						if (cmd[8] == databuffer[8] && cmd[9] == databuffer[9] && cmd[10] == databuffer[10] && cmd[11] == databuffer[11])
599 						{
600 							return answerLength;
601 						}
602 						else
603 						{
604 							_log.Log(LOG_ERROR, "MultiFun: bad response after write");
605 						}
606 					}
607 					else
608 					{
609 						return answerLength;
610 					}
611 				}
612 				else
613 				{
614 					_log.Log(LOG_ERROR, "MultiFun: bad size of frame");
615 				}
616 			}
617 			else
618 				if (cmd[7] + 0x80 == databuffer[7])
619 				{
620 					if (databuffer[8] >= 1 && databuffer[8] <= 4)
621 					{
622 						_log.Log(LOG_ERROR, "MultiFun: Receive error (%s)", errors[databuffer[8] - 1].c_str());
623 					}
624 					else
625 					{
626 						_log.Log(LOG_ERROR, "MultiFun: Receive unknown error");
627 					}
628 				}
629 				else
630 				{
631 					_log.Log(LOG_ERROR, "MultiFun: Receive error (unknown function code)");
632 				}
633 		}
634 		else
635 		{
636 				_log.Log(LOG_ERROR, "MultiFun: received bad frame prefix");
637 		}
638 	}
639 	else
640 	{
641 		_log.Log(LOG_ERROR, "MultiFun: received frame is too short.");
642 		DestroySocket();
643 	}
644 
645 	return -1;
646 }
647