1 /*
2 Domoticz Software : http://domoticz.com/
3 File : TeleinfoSerial.cpp
4 Author : Nicolas HILAIRE, Blaise Thauvin
5 Version : 2.3
6 Description : This class decodes the Teleinfo signal from serial/USB devices before processing them
7 
8 History :
9 - 2013-11-01 : Creation
10 - 2014-10-29 : Add 'EJP' contract (Laurent MEY)
11 - 2014-12-13 : Add 'Tempo' contract (Kevin NICOLAS)
12 - 2015-06-10 : Fix bug power divided by 2 (Christophe DELPECH)
13 - 2016-02-05 : Fix bug power display with 'Tempo' contract (Anthony LAGUERRE)
14 - 2016-02-11 : Fix power display when PAPP is missing (Anthony LAGUERRE)
15 - 2016-02-17 : Fix bug power usage (Anthony LAGUERRE). Thanks to Multinet
16 - 2017-01-28 : Add 'Heures Creuses' Switch (A.L)
17 - 2017-03-15 : 2.0 Renamed from Teleinfo.cpp to TeleinfoSerial.cpp in order to create
18 			   a shared class to process Teleinfo protocol (Blaise Thauvin)
19 - 2017-03-21 : 2.1 Fixed bug sending too many updates
20 - 2017-03-26 : 2.2 Fixed bug affecting tree-phases users. Consequently, simplified code
21 - 2017-04-01 : 2.3 Added RateLimit, flag to ignore CRC checks, and new CRC computation algorithm available on newer meters
22 - 2017-12-17 : 2.4 Fix bug affecting meters not providing PAPP, thanks to H. Lertouani
23 */
24 
25 #include "stdafx.h"
26 #include "TeleinfoSerial.h"
27 #include "hardwaretypes.h"
28 #include "../main/localtime_r.h"
29 #include "../main/Logger.h"
30 
31 #include <boost/bind.hpp>
32 #include <boost/exception/diagnostic_information.hpp>
33 
CTeleinfoSerial(const int ID,const std::string & devname,const int datatimeout,unsigned int baud_rate,const bool disable_crc,const int ratelimit)34 CTeleinfoSerial::CTeleinfoSerial(const int ID, const std::string& devname, const int datatimeout, unsigned int baud_rate, const bool disable_crc, const int ratelimit)
35 {
36 	m_HwdID = ID;
37 	m_szSerialPort = devname;
38 	m_iOptParity = boost::asio::serial_port_base::parity(TELEINFO_PARITY);
39 	m_iOptCsize = boost::asio::serial_port_base::character_size(TELEINFO_CARACTER_SIZE);
40 	m_iOptFlow = boost::asio::serial_port_base::flow_control(TELEINFO_FLOW_CONTROL);
41 	m_iOptStop = boost::asio::serial_port_base::stop_bits(TELEINFO_STOP_BITS);
42 	m_bDisableCRC = disable_crc;
43 	m_iRateLimit = ratelimit;
44 	m_iDataTimeout = datatimeout;
45 
46 	if (baud_rate == 0)
47 		m_iBaudRate = 1200;
48 	else
49 		m_iBaudRate = 9600;
50 
51 	// RateLimit > DataTimeout is an inconsistent setting. In that case, decrease RateLimit (which increases update rate)
52 	// down to Timeout in order to avoir watchdog errors due to this user configuration mistake
53 	if ((m_iRateLimit > m_iDataTimeout) && (m_iDataTimeout > 0))  m_iRateLimit = m_iDataTimeout;
54 
55 	Init();
56 }
57 
58 
~CTeleinfoSerial()59 CTeleinfoSerial::~CTeleinfoSerial()
60 {
61 	StopHardware();
62 }
63 
64 
Init()65 void CTeleinfoSerial::Init()
66 {
67 	InitTeleinfo();
68 }
69 
70 
StartHardware()71 bool CTeleinfoSerial::StartHardware()
72 {
73 	RequestStart();
74 
75 	Init();
76 	//Try to open the Serial Port
77 	try
78 	{
79 		_log.Log(LOG_STATUS, "(%s) Teleinfo device uses serial port: %s at %i bauds", m_Name.c_str(), m_szSerialPort.c_str(), m_iBaudRate);
80 		open(m_szSerialPort, m_iBaudRate, m_iOptParity, m_iOptCsize);
81 	}
82 	catch (boost::exception & e)
83 	{
84 		_log.Debug(DEBUG_HARDWARE, "-----------------\n%s\n-----------------", boost::diagnostic_information(e).c_str());
85 		_log.Log(LOG_STATUS, "Teleinfo: Serial port open failed, let's retry with CharSize:8 ...");
86 
87 		try {
88 			open(m_szSerialPort, m_iBaudRate, m_iOptParity, boost::asio::serial_port_base::character_size(8));
89 			_log.Log(LOG_STATUS, "Teleinfo: Serial port open successfully with CharSize:8 ...");
90 		}
91 		catch (...) {
92 			_log.Log(LOG_ERROR, "Teleinfo: Error opening serial port, even with CharSize:8 !");
93 			return false;
94 		}
95 	}
96 	catch (...)
97 	{
98 		_log.Log(LOG_ERROR, "Teleinfo: Error opening serial port!!!");
99 		return false;
100 	}
101 	StartHeartbeatThread();
102 
103 	setReadCallback(boost::bind(&CTeleinfoSerial::readCallback, this, _1, _2));
104 	m_bIsStarted = true;
105 	sOnConnected(this);
106 
107 	if (m_bDisableCRC)
108 		_log.Log(LOG_STATUS, "(%s) CRC checks on incoming data are disabled", m_Name.c_str());
109 	else
110 		_log.Log(LOG_STATUS, "(%s) CRC checks will be performed on incoming data", m_Name.c_str());
111 
112 	return true;
113 }
114 
115 
StopHardware()116 bool CTeleinfoSerial::StopHardware()
117 {
118 	StopHeartbeatThread();
119 	terminate();
120 	m_bIsStarted = false;
121 	return true;
122 }
123 
124 
WriteToHardware(const char * pdata,const unsigned char length)125 bool CTeleinfoSerial::WriteToHardware(const char *pdata, const unsigned char length)
126 {
127 	return true;
128 }
129 
130 
readCallback(const char * data,size_t len)131 void CTeleinfoSerial::readCallback(const char *data, size_t len)
132 {
133 	if (!m_bEnableReceive)
134 	{
135 		_log.Log(LOG_ERROR, "(%s) Receiving is not enabled", m_Name.c_str());
136 		return;
137 	}
138 	ParseTeleinfoData(data, static_cast<int>(len));
139 }
140