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