#pragma once #include "ademco_event.h" #include <assert.h> #include <string.h> // memset #if defined(DISABLE_JLIB_LOG2) && !defined(JLIB_DISABLE_LOG) #define JLIB_DISABLE_LOG #endif #ifndef JLIB_DISABLE_LOG #include <jlib/log2.h> #else // JLIB_DISABLE_LOG #define init_logger #define JLOG_DBUG #define JLOG_INFO #define JLOG_WARN #define JLOG_ERRO #define JLOG_CRTC #define JLOG_ALL class range_log { public: range_log() {} range_log(const char*) {} }; #define AUTO_LOG_FUNCTION #define dump_hex #define dump_asc #define JLOG_HEX #define JLOG_ASC #endif // JLIB_DISABLE_LOG namespace ademco { // for crossplatform #ifndef __GNUG__ static inline int localtime_r(time_t* t, struct tm* tm_time) { return localtime_s(tm_time, t); } #ifdef min #undef min #endif #ifdef max #undef max #endif #endif // __GNUG__ namespace detail { /** * @brief 计算一个字节(char)的crc16值 * @param c 要计算crc16的字节 * @param crc 初始crc值 * @return crc16 */ static unsigned short CalculateCRC_char(char c, unsigned short crc) { static constexpr unsigned short crcTable[] = { /* DEFINE THE FIRST ORDER POLYINOMIAL TABLE */ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, }; unsigned short CRC = crc; auto tmp = static_cast<unsigned short>(CRC >> 8) ^ (crcTable[static_cast<unsigned char>(c) ^ static_cast<unsigned char>(CRC & 0xFF)]); CRC = static_cast<unsigned short>(tmp); return CRC; } /** * @brief 计算一段数据包的crc16值 * @param buff 要计算crc16的数据包指针 * @param len 要计算crc16的数据包长度 * @param crc 初始crc值 * @return crc16 */ static unsigned short CalculateCRC(const char* buff, size_t len, unsigned short crc = 0) { unsigned short CRC = crc; for (size_t i = 0; i < len; ++i) { CRC = CalculateCRC_char(buff[i], CRC); } return CRC; } /** * @brief 计算一段数据包的crc16值 * @param a 要计算crc16的数据包 * @param crc 初始crc值 * @return crc16 */ static unsigned short CalculateCRC(const std::vector<char>& a, unsigned short crc) { unsigned short CRC = crc; for (auto c : a) { CRC = CalculateCRC_char(c, CRC); } return CRC; } /** * @brief 将'0' ~ '9', 'a' ~ 'f', 'A' ~ 'F' 的字节转换为十进制数字,范围0~15 * @param hex 要计算的字节 * @return 字节表示的十进制数字 * @note 示例:'f' 返回十进制数字15 */ static char HexChar2Dec(char hex) { if (hex >= '0' && hex <= '9') return static_cast<char>(hex - '0'); else if (hex >= 'A' && hex <= 'F') return static_cast<char>(hex - 'A' + 10); else if (hex >= 'a' && hex <= 'f') return static_cast<char>(hex - 'a' + 10); else if (hex == 'l' || hex == 'L') return 0; else { JLOG_WARN("HexChar2Dec: not a hex char. ({}) (0x{:02X})", hex, hex); //assert(0); return 0; } } /** * @brief 将十六进制数字的字符串转换为十进制数字 * @param hex 要计算的字符串地址,单个字节范围为'0' ~ '9', 'a' ~ 'f', 'A' ~ 'F' * @param len 要计算的字符串长度 * @return 十进制数字 * @note 示例: 字符串"FF"返回十进制数字255 * @note 仅用于安定宝数据包内的16进制字符串解析,长度较小,返回的十进制数字不能超过size_t范围 */ static size_t HexCharArrayToDec(const char* hex, size_t len) { size_t dec = 0; for (size_t i = 0; i < len; i++) { dec = dec * 0x10 + HexChar2Dec(hex[i]); } return dec; } /** * @brief 将十进制数字字符串转换为十进制数字 * @param str 要计算的字符串地址,单个字节范围为'0' ~ '9' * @param len 要计算的字符串长度 * @return 十进制数字 * @note 示例: 字符串"99"返回十进制数字99 * @note 仅用于安定宝数据包内的16进制字符串解析,长度较小,返回的十进制数字不能超过size_t范围 */ static size_t NumCharArray2Dec(const char* str, size_t str_len) { size_t dec = 0; for (size_t i = 0; i < str_len; i++) { dec = dec * 10 + HexChar2Dec(str[i]); } return dec; } /** * @brief 将范围为0~15的十进制数字字节转换为16进制字节 * @param d 要计算的10进制数字,范围0~15 * @return 十六进制字节,大写 * @note 示例: 数字10 返回字节 'A' */ static char Dec2Hex(char d) { if (0 <= d && d <= 9) { return static_cast<char>((unsigned char)d + (unsigned char)'0'); } else if (0x0A <= d && d <= 0x0F) { return char(d - 0x0A + (char)'A'); } else { JLOG_ERRO("Dec2Hex: not a 0-f value. ({}) (0x{:02X})", d, d); assert(0); return '0'; } } /** * @brief 将一串以高低字节表示的十六进制数组转换为字符串 * @dst 直接将结果写入到dst * @param hex 十六进制字符串地址 * @param len 十六进制字符串长度 * @mask 掩码 扫描到掩码后立即停止 * @note 示例:输入数组:0x18 0x24 0x08 0x88 0x10 0x1f,以0x0F为掩码,输出字符串"18240888101" */ static const char* HexCharArrayToStr(char* dst, const char* hex, size_t len, unsigned char mask = (unsigned char)0x0F) { //AUTO_LOG_FUNCTION; //jlib::dump_hex(hex, len, spdlog::level::debug); memset(dst, 0, len * 2); JLOG_DBUG("memset ok"); char tmp[8] = { 0 }; unsigned char high = 0, low = 0; for (size_t i = 0; i < len; i++) { high = (unsigned char)((hex[i] >> 4) & mask); if (high >= mask) { break; } snprintf(tmp, sizeof(tmp), "%d", high); strncat(dst, tmp, sizeof(tmp)); low = (unsigned char)(hex[i] & mask); if (low >= mask) { break; } snprintf(tmp, sizeof(tmp), "%d", low); strncat(dst, tmp, sizeof(tmp)); } return dst; } /** * @brief 将数字转换为十六进制数组 * @param dec 10进制数字 * @param hex 将结果写入到此地址,定长4字节 * @param bMax0FFF 输入的数字是否有最大值 0x0FFF * @note 示例:输入 256, 其hex为 0x0100, 输出 "0100" */ static void Dec2HexCharArray_4(size_t dec, char* hex, bool bMax0FFF = true) { if (dec < 0) { JLOG_ERRO("Dec2HexCharArray_4: 0LLL can't be negative."); return; } else if (dec == 0) { memset(hex, '0', 4); return; } else if (bMax0FFF && dec > 0x0FFF) { JLOG_WARN("Dec2HexCharArray_4: 0LLL {} is bigger than 0x0fff.", dec); memcpy(hex, "0FFF", 4); return; } else { char tmp[5] = { 0 }; snprintf(tmp, sizeof(tmp), "%04X", static_cast<unsigned int>(dec & 0xFFFF)); memcpy(hex, tmp, 4); } } /** * @brief 将字符串表示的数字转换为以高低字节表示的十六进制数组 * @param str 字符串,字面值为 10 进制数字 * @param hexarr 将结果写入到此地址,长度为字符串长度的 1/2 * @param max_hex_len 结果数组是否有最大长度 9 * @note 限制了字符串最大长度为 32,因此 max_hex_len 最大为 16 * @note 当字符串转换后长度若不足max_hex_len则补 0x0F * @note 示例:输入 "18240888101",max_hex_len为9,输出 0x18 0x24 0x08 0x88 0x10 0x1f 0xff 0xff 0xff */ static void NumStr2HexCharArray_N(const char* str, char* hexarr, size_t max_hex_len = 9) { //AUTO_LOG_FUNCTION; if (str == nullptr) { JLOG_ERRO("NumStr2HexCharArray_N: memory access denied."); return; } auto len = strlen(str); max_hex_len = std::min(max_hex_len, size_t(16)); if (len > max_hex_len * 2) { JLOG_ERRO("NumStr2HexCharArray_N: length too long."); return; } size_t i = 0; for (i = 0; i < len; i++) { if (!isdigit(str[i])) { JLOG_ERRO("NumStr2HexCharArray_N: not all character is digit."); return; } } const size_t full_str_len = max_hex_len * 2; char full_str[32] = { 0 }; memset(full_str, 0, sizeof(full_str)); strncpy(full_str, str, len); full_str[len] = '\0'; while (strlen(full_str) < full_str_len) { strncat(full_str, "f", 1); } for (i = 0; i < max_hex_len; i++) { char ch = static_cast<char>(HexChar2Dec(full_str[i * 2]) & 0x0F); ch = static_cast<char>(ch << 4); ch = static_cast<char>(ch | (HexChar2Dec(full_str[i * 2 + 1]) & 0x0F)); hexarr[i] = ch; } } /** * @brief 将字符串形式的高低字节转换为真正的高低字节数组 * @param dst 结果地址 * @param src 字符串形式的高低字节, 如 "ffffabcd1234", 输出 0xff 0xff 0xab 0xcd 0x12 0x34 * @param len 字符串长度 * @note 起因是钱工的GPRS模块无法传输hex,只能传输ascii字节 */ static void ConvertHiLoAsciiToHexCharArray(char* dst, const char* src, size_t len) { for (size_t i = 0; i < len; i++) { if (!isdigit(src[i]) && !isalpha(src[i])) { JLOG_ERRO("NumStr2HexCharArray_N: not all character is digit or alpha."); return; } } const size_t dst_len = len / 2; memset(dst, 0, dst_len); for (size_t i = 0; i < dst_len; i++) { char ch = HexChar2Dec(src[i * 2]) & 0x0F; ch = static_cast<char>(ch << 4); ch = static_cast<char>(ch | (HexChar2Dec(src[i * 2 + 1]) & 0x0F)); dst[i] = ch; } } enum class ToStringOption { //! 所有字节按照16进制表示,如 '0' 表示为 "\x30" ALL_CHAR_AS_HEX, //! 如果字节是isalnum的直接输出,否则输出16进制格式 TRY_IS_ALNUM_FIRST, }; /** * @brief 常规数组转字符串 * @param data 数据 * @param len 数据长度 * @param option 选项 * @param show_x_for_hex 为 hex 显示 \x * @param show_space_between_hex 在 hex 之间插入空格 * @return std::string */ static std::string toString(const char* data, size_t len, // 数据 ToStringOption option = ToStringOption::TRY_IS_ALNUM_FIRST, // 选项 bool show_x_for_hex = true, // 为 hex 显示 \x bool show_space_between_hex = false) // 在 hex 之间插入空格 { std::string str; for (size_t i = 0; i < len; i++) { auto c = data[i]; if (option == ToStringOption::TRY_IS_ALNUM_FIRST && std::isalnum(static_cast<unsigned char>(c))) { str.push_back(c); } else { if (show_x_for_hex) { str += "\\x"; } str.push_back(ademco::detail::Dec2Hex((c >> 4) & 0x0F)); str.push_back(ademco::detail::Dec2Hex(c & 0x0F)); if (show_space_between_hex) { str.push_back(' '); } } } return str; } /** * @brief 常规数组转字符串 * @param data 数据 * @param option 选项 * @param show_x_for_hex 为 hex 显示 \x * @param show_space_between_hex 在 hex 之间插入空格 * @return std::string */ static std::string toString(const std::vector<char>& data, // 数据 ToStringOption option = ToStringOption::TRY_IS_ALNUM_FIRST, // 选项 bool show_x_for_hex = true, // 为 hex 显示 \x bool show_space_between_hex = false) // 在 hex 之间插入空格 { return toString(data.data(), data.size(), option, show_x_for_hex, show_space_between_hex); } } // namespace detail } // namespace ademco