1 /*
2  * ebusd - daemon for communication with eBUS heating systems.
3  * Copyright (C) 2014-2021 John Baier <ebusd@ebusd.eu>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "lib/ebus/symbol.h"
20 #include <iostream>
21 #include <iomanip>
22 #include <string>
23 #include <vector>
24 #include "lib/ebus/result.h"
25 
26 namespace ebusd {
27 
28 using std::ostringstream;
29 using std::nouppercase;
30 using std::setw;
31 using std::dec;
32 using std::hex;
33 using std::setfill;
34 
35 /**
36  * CRC8 lookup table for the polynom 0x9b = x^8 + x^7 + x^4 + x^3 + x^1 + 1.
37  */
38 static const symbol_t CRC_LOOKUP_TABLE[] = {
39   0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
40   0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
41   0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
42   0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
43   0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
44   0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
45   0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
46   0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
47   0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
48   0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
49   0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
50   0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
51   0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
52   0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
53   0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
54   0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
55 };
56 
57 
parseInt(const char * str,int base,unsigned int minValue,unsigned int maxValue,result_t * result,size_t * length)58 unsigned int parseInt(const char* str, int base, unsigned int minValue, unsigned int maxValue,
59     result_t* result, size_t* length) {
60   char* strEnd = nullptr;
61 
62   unsigned long ret = strtoul(str, &strEnd, base);
63 
64   if (strEnd == nullptr || strEnd == str || *strEnd != 0) {
65     *result = RESULT_ERR_INVALID_NUM;  // invalid value
66     return 0;
67   }
68 
69   if (minValue > ret || ret > maxValue) {
70     *result = RESULT_ERR_OUT_OF_RANGE;  // invalid value
71     return 0;
72   }
73   if (length != nullptr) {
74     *length = (unsigned int)(strEnd - str);
75   }
76   *result = RESULT_OK;
77   return (unsigned int)ret;
78 }
79 
parseSignedInt(const char * str,int base,int minValue,int maxValue,result_t * result,size_t * length)80 int parseSignedInt(const char* str, int base, int minValue, int maxValue,
81     result_t* result, size_t* length) {
82   char* strEnd = nullptr;
83 
84   long ret = strtol(str, &strEnd, base);
85 
86   if (strEnd == nullptr || *strEnd != 0) {
87     *result = RESULT_ERR_INVALID_NUM;  // invalid value
88     return 0;
89   }
90 
91   if (minValue > ret || ret > maxValue) {
92     *result = RESULT_ERR_OUT_OF_RANGE;  // invalid value
93     return 0;
94   }
95   if (length != nullptr) {
96     *length = (unsigned int)(strEnd - str);
97   }
98   *result = RESULT_OK;
99   return static_cast<int>(ret);
100 }
101 
102 
updateCrc(symbol_t value,symbol_t * crc)103 void SymbolString::updateCrc(symbol_t value, symbol_t* crc) {
104   *crc = CRC_LOOKUP_TABLE[*crc]^value;
105 }
106 
parseHex(const string & str)107 result_t SymbolString::parseHex(const string& str) {
108   result_t result;
109   for (size_t i = 0; i < str.size(); i += 2) {
110     symbol_t value = (symbol_t)parseInt(str.substr(i, 2).c_str(), 16, 0, 0xff, &result);
111     if (result != RESULT_OK) {
112       return result;
113     }
114     m_data.push_back(value);
115   }
116   return RESULT_OK;
117 }
118 
parseHexEscaped(const string & str)119 result_t SymbolString::parseHexEscaped(const string& str) {
120   result_t result;
121   bool inEscape = false;
122   for (size_t i = 0; i < str.size(); i += 2) {
123     symbol_t value = (symbol_t)parseInt(str.substr(i, 2).c_str(), 16, 0, 0xff, &result);
124     if (result != RESULT_OK) {
125       return result;
126     }
127     if (inEscape) {
128       if (value == 0x00) {
129         m_data.push_back(ESC);
130         inEscape = false;
131       } else if (value == 0x01) {
132         m_data.push_back(SYN);
133         inEscape = false;
134       } else {
135         return RESULT_ERR_ESC;  // invalid escape sequence
136       }
137     } else if (value == ESC) {
138       inEscape = true;
139     } else if (value == SYN) {
140       return RESULT_ERR_ESC;  // invalid escape sequence
141     } else {
142       m_data.push_back(value);
143     }
144   }
145   return inEscape ? RESULT_ERR_ESC : RESULT_OK;
146 }
147 
getStr(size_t skipFirstSymbols) const148 const string SymbolString::getStr(size_t skipFirstSymbols) const {
149   ostringstream sstr;
150   for (size_t i = 0; i < m_data.size(); i++) {
151     if (skipFirstSymbols > 0) {
152       skipFirstSymbols--;
153     } else {
154       sstr << nouppercase << setw(2) << hex
155           << setfill('0') << static_cast<unsigned>(m_data[i]);
156     }
157   }
158   return sstr.str();
159 }
160 
dumpJson(bool withSeparator,ostringstream * output) const161 bool SymbolString::dumpJson(bool withSeparator, ostringstream* output) const {
162   if (size() == 0) {
163     return false;
164   }
165   if (withSeparator) {
166     *output << ",\n    ";
167   }
168   *output << "\"" << (isMaster() ? "master" : "slave") << "\": [";
169   for (size_t pos = 0; pos < size(); pos++) {
170     if (pos > 0) {
171       *output << ", ";
172     }
173     *output << dec << static_cast<unsigned>(m_data[pos]);
174   }
175   *output << "]";
176   return true;
177 }
178 
calcCrc() const179 symbol_t SymbolString::calcCrc() const {
180   symbol_t crc = 0;
181   for (size_t i = 0; i < m_data.size(); i++) {
182     symbol_t value = m_data[i];
183     if (value == ESC) {
184       updateCrc(ESC, &crc);
185       updateCrc(0x00, &crc);
186     } else if (value == SYN) {
187       updateCrc(ESC, &crc);
188       updateCrc(0x01, &crc);
189     } else {
190       updateCrc(value, &crc);
191     }
192   }
193   return crc;
194 }
195 
196 
197 /**
198  * Return the index of the upper or lower 4 bits of a master address.
199  * @param bits the upper or lower 4 bits of the address.
200  * @return the 1-based index of the upper or lower 4 bits of a master address (1 to 5), or 0.
201  */
getMasterPartIndex(symbol_t bits)202 unsigned int getMasterPartIndex(symbol_t bits) {
203   switch (bits) {
204   case 0x0:
205     return 1;
206   case 0x1:
207     return 2;
208   case 0x3:
209     return 3;
210   case 0x7:
211     return 4;
212   case 0xF:
213     return 5;
214   default:
215     return 0;
216   }
217 }
218 
isMaster(symbol_t addr)219 bool isMaster(symbol_t addr) {
220   return getMasterPartIndex(addr & 0x0F) > 0
221     && getMasterPartIndex((addr & 0xF0)>>4) > 0;
222 }
223 
isSlaveMaster(symbol_t addr)224 bool isSlaveMaster(symbol_t addr) {
225   return isMaster((symbol_t)(addr+256-5));
226 }
227 
getSlaveAddress(symbol_t addr)228 symbol_t getSlaveAddress(symbol_t addr) {
229   if (isMaster(addr)) {
230     return (symbol_t)(addr+5);
231   }
232   if (isValidAddress(addr, false)) {
233     return addr;
234   }
235   return SYN;
236 }
237 
getMasterAddress(symbol_t addr)238 symbol_t getMasterAddress(symbol_t addr) {
239   if (isMaster(addr)) {
240     return addr;
241   }
242   addr = (symbol_t)(addr+256-5);
243   if (isMaster(addr)) {
244     return addr;
245   }
246   return SYN;
247 }
248 
getMasterNumber(symbol_t addr)249 unsigned int getMasterNumber(symbol_t addr) {
250   unsigned int priority = getMasterPartIndex(addr & 0x0F);
251   if (priority == 0) {
252     return 0;
253   }
254   unsigned int index = getMasterPartIndex((addr & 0xF0) >> 4);
255   if (index == 0) {
256     return 0;
257   }
258   return 5*(priority-1) + index;
259 }
260 
isValidAddress(symbol_t addr,bool allowBroadcast)261 bool isValidAddress(symbol_t addr, bool allowBroadcast) {
262   return addr != SYN && addr != ESC && (allowBroadcast || addr != BROADCAST);
263 }
264 
265 }  // namespace ebusd
266