Commit c7c2306d authored by captainwong's avatar captainwong

init

parents
.vscode/
.vs/
packages/
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Dev partner files
*.DPbcd
# vs
*.sdf
#*.sln
*.suo
*.ipch
*.ilk
*.pdb
*.map
#*.vcxproj
#*.filters
*.log
*.tlog
*.nlb
*.DPbcd
*.pd_
*.opensdf
Debug/Maps/
Debug/config/
Debug/
Release/
ReleaseD/
# vc6
#*.dsp
#*.dsw
*.ncb
*.opt
*.plg
*.clw
*.aps
*.log
*.pch
*.idb
*.pdb
*.ilk
*.sbr
*.bsc
*.map
# graph
#*.bmp
*.jpg
*.jpeg
*.jpe
*.jifi
*.gif
#*.ico
#*.png
*.dib
*.tif
*.tiff
# video
*.mp4
/video_record
/video_capture
# Compiled Dynamic libraries
*.so
*.dylib
#*.dll
# Compiled Static libraries
*.lai
*.la
*.a
#*.lib
# Executables
#*.exe
#AlarmCenter_Setup.exe
*.out
*.app
# Database files
#*.mdb
*.ldb
*.db
# Resource files
*.manifest
#*.rc
#*.rc2
*.cur
*.avi
*.wav
# AlarmCenter files
*.bak
*.log
# Mindjet map files
*.mmap.~$lock
#baiduyun files
*.baiduyun.uploading.cfg
# Compressed files
*.rar
*.7z
*.zip
*.tar
*.gz
*.bz
*.bz2
MIT License
Copyright (c) 2019 HENGBO
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# Hengbo Protocal Library
恒博协议库
## 各头文件说明
* `ademco_event.h`
定义了安定宝事件码及所需的结构体
* `ademco_detail.h`
定义了各种辅助函数
* `ademco_packet.h`
定义了可用于安定宝协议封包、解包的结构体与相应函数

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29306.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo", "demo\demo.vcxproj", "{36640D95-55AB-4DCF-9062-91A53203E6A9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Debug|x64.ActiveCfg = Debug|x64
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Debug|x64.Build.0 = Debug|x64
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Debug|x86.ActiveCfg = Debug|Win32
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Debug|x86.Build.0 = Debug|Win32
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Release|x64.ActiveCfg = Release|x64
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Release|x64.Build.0 = Release|x64
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Release|x86.ActiveCfg = Release|Win32
{36640D95-55AB-4DCF-9062-91A53203E6A9}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A7C808AC-B572-4B53-A2B5-B50905BE6550}
EndGlobalSection
EndGlobal
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#define DISABLE_JLIB_LOG2
#include <ademco_packet.h>
#include <ademco_detail.h>
using namespace ademco;
#define EXPECT_EQ(a, b) assert((a) == (b))
#define EXPECT_TRUE(a) assert(a)
#define EXPECT_STREQ(a, b) assert(strcmp((a), (b)) == 0)
void test()
{
char pack[1024] = { 0 };
AdemcoPacket ap = {};
// parse data from self made
{
auto size = ap.make(pack, sizeof(pack));
size_t cb_commited = 0;
EXPECT_EQ(ap.parse(pack, size, cb_commited), ParseResult::RESULT_OK);
EXPECT_EQ(cb_commited, size);
}
// parse data from production
{
char raw[] = "\nC5C30053\"HENG-BO\"0000R000000L000000#90219125916578[#000000|1737 00 000]_09:11:19,08-05-2019\r";
size_t cb_commited = 0;
EXPECT_EQ(ap.parse(raw, strlen(raw), cb_commited), ParseResult::RESULT_OK);
EXPECT_EQ(cb_commited, strlen(raw));
EXPECT_EQ(ap.crc_.value_, 0xC5C3);
EXPECT_EQ(ap.len_.value_, 0x0053);
EXPECT_EQ(ap.id_.eid_, AdemcoId::Enum::id_hb);
EXPECT_EQ(ap.seq_.value_, 0); // 0 is not a valid seq, but...
EXPECT_TRUE(strncmp("R000000", ap.rrcvr_.data_.data(), 7) == 0);
EXPECT_TRUE(strncmp("L000000", ap.lpref_.data_.data(), 7) == 0);
EXPECT_STREQ(ap.acct_.acct(), "90219125916578");
EXPECT_EQ(ap.ademcoData_.ademco_id_, 0);
EXPECT_EQ(ap.ademcoData_.ademco_event_, ADEMCO_EVENT::EVENT_I_AM_WIRE_MACHINE);
EXPECT_EQ(ap.ademcoData_.gg_, 0);
EXPECT_EQ(ap.ademcoData_.zone_, 0);
EXPECT_STREQ(ap.timestamp_.data_, "_09:11:19,08-05-2019");
}
}
void generate_network_data()
{
char pack[1024];
AdemcoPacket ap;
ap.make_hb(pack, sizeof(pack), 1, "861234567890", 666666, 0, ademco::EVENT_ARM, 0);
printf("generate_network_data:%s\n", ap.toString().data());
}
void handle_network_data(const char* data_from_network)
{
AdemcoPacket ap;
size_t cb_commited = 0;
auto result = ap.parse(data_from_network, strlen(data_from_network), cb_commited);
switch (result) {
case ademco::ParseResult::RESULT_OK:
{
printf("id=%s\n", ap.id_.data());
switch (ap.id_.eid_) {
case AdemcoId::id_ack:
// success
break;
case AdemcoId::id_hb:
// event report
printf("%s\n", ap.ademcoData_.toString().data());
break;
default:
break;
}
break;
}
case ademco::ParseResult::RESULT_NOT_ENOUGH:
// do nothing
break;
case ademco::ParseResult::RESULT_DATA_ERROR:
default:
// error handle, e.g. clear buff
break;
}
}
int main()
{
test();
generate_network_data();
char data_from_network[] = "\nC5C30053\"HENG-BO\"0000R000000L000000#90219125916578[#000000|1737 00 000]_09:11:19,08-05-2019\r";
handle_network_data(data_from_network);
char buff[1024];
AdemcoPacket ap;
ap.make(buff, sizeof(buff));
handle_network_data(buff);
ap.make_ack(buff, sizeof(buff), 1, "861234567890", 123321);
handle_network_data(buff);
size_t ademco_id = 123456; // 主机标识
size_t zone = 123; // 防区标识
auto evnt = EVENT_FIRE; // 事件码
ap.make_hb(buff, sizeof(buff), 1, nullptr, ademco_id, 0, evnt, zone);
handle_network_data(buff);
}
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{36640D95-55AB-4DCF-9062-91A53203E6A9}</ProjectGuid>
<RootNamespace>demo</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="demo.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\ademco_detail.h" />
<ClInclude Include="..\include\ademco_event.h" />
<ClInclude Include="..\include\ademco_packet.h" />
<ClInclude Include="..\include\hb.h" />
<ClInclude Include="..\include\hb_detail.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="include">
<UniqueIdentifier>{9cddb4df-0c1d-4f36-a814-5412a486e938}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="demo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\ademco_detail.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\ademco_event.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\ademco_packet.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\hb.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\include\hb_detail.h">
<Filter>include</Filter>
</ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
\ No newline at end of file
#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
#pragma once
#include <time.h>
#include <vector>
#include <memory>
#include <algorithm>
#include <ctype.h>
#include <string>
#include <stdint.h>
namespace ademco
{
//! 安定宝事件代码
enum ADEMCO_EVENT : uint32_t {
EVENT_INVALID_EVENT = 0,
// -------------------标准安定宝协议事件--------------------------------
// 主机或分机状态报告
EVENT_ARM = 3400, // 布防
EVENT_DISARM = 1400, // 撤防
EVENT_HALFARM_1456 = 1456, // 半布防
EVENT_HALFARM = 3456, // 半布防
EVENT_EMERGENCY = 1120, // 紧急报警
// 防区报警
EVENT_BURGLAR = 1130, // 盗警
EVENT_DOORRINGING = 1134, // 门铃
EVENT_FIRE = 1110, // 火警
EVENT_DURESS = 1121, // 胁迫
EVENT_GAS = 1151, // 煤气
EVENT_WATER = 1113, // 水警
EVENT_TEMPER = 1137, // 主机防拆
EVENT_ZONE_TEMPER = 1383, // 防区防拆
EVENT_BY_PASS = 1570, // 旁路
EVENT_BY_PASS_RESUME = 3570, // 解除旁路
// 防区异常
EVENT_AC_BROKE = 1301, // 主机AC掉电
EVENT_AC_RECOVER = 3301, // 主机AC复电
EVENT_LOWBATTERY = 1302, // 低电
EVENT_BATTERY_RECOVER = 3302, // 复电
EVENT_BADBATTERY = 1311, // 坏电
EVENT_SOLARDISTURB = 1387, // 光扰
EVENT_DISCONNECT = 1381, // 失效
EVENT_RECONNECT = 3381, // 恢复
EVENT_BATTERY_EXCEPTION = 1384, // 电源故障
EVENT_BATTERY_EXCEPTION_RECOVER = 3384, // 电源故障恢复
EVENT_OTHER_EXCEPTION = 1380, // 其他故障
EVENT_OTHER_EXCEPTION_RECOVER = 3380, // 其他故障恢复
EVENT_LOST = 1393, // 失联
EVENT_LOST_RECOVER = 3393, // 失联恢复
// 标准事件,但不做处理
EVENT_3100 = 3100, // 液晶主机清除异常指示
// ------------------------------------------------------------------
// ------------------私有事件-----------------------------------------
EVENT_SERIAL485DIS = 1485, // 网络模块与主机485串口连接断开
EVENT_SERIAL485CONN = 3485, // 网络模块与主机485串口连接恢复
EVENT_CONN_HANGUP = 1700, // 链路挂起
EVENT_CONN_RESUME = 3700, // 链路恢复
EVENT_DISARM_PWD_ERR = 1701, // 撤防密码错误
EVENT_SUB_MACHINE_SENSOR_EXCEPTION = 1702, // 分机探头异常
EVENT_SUB_MACHINE_SENSOR_RESUME = 3702, // 分机探头恢复
EVENT_SUB_MACHINE_POWER_EXCEPTION = 1703, // 分机电源异常
EVENT_SUB_MACHINE_POWER_RESUME = 3703, // 分机电源恢复
EVENT_RETRIEVE_ZONE_OR_SUB_MACHINE = 1704, // 索要/回复防区或分机信息
EVENT_ENTER_SET_MODE = 2704, // 进入设置状态
EVENT_STOP_RETRIEVE = 3704, // 接警中心发送,为停止索要;报警主机发送,为拒绝索要
EVENT_QUERY_SUB_MACHINE = 1705, // 查询分机信息
EVENT_WRITE_TO_MACHINE = 1706, // 写入主机信息
EVENT_I_AM_NET_MODULE = 1707, // 主机类型--网络模块
EVENT_I_AM_GPRS = 1717, // 主机类型--GPRS主机
EVENT_I_AM_LCD_MACHINE = 1727, // 主机类型--液晶主机
EVENT_I_AM_WIRE_MACHINE = 1737, // 主机类型--网线主机
EVENT_I_AM_WIFI_MACHINE = 1747, // 主机类型--WiFi主机
EVENT_PHONE_USER_SOS = 1709, // 手机用户SOS
EVENT_PHONE_USER_CANCLE_ALARM = 1711, // 手机用户消警
EVENT_ENTER_SETTING_MODE = 1712, // 主机进入设置状态
EVENT_EXIT_SETTING_MODE = 3712, // 主机退出设置状态
EVENT_RESTORE_FACTORY_SETTINGS_710 = 1710, // 主机恢复出厂设置
EVENT_RESTORE_FACTORY_SETTINGS = 1713, // 主机恢复出厂设置
EVENT_SIM_IS_IOT_CARD = 1756, // 主机SIM卡为物联卡
EVENT_SIM_IS_NOT_IOT_CARD = 3756, // 主机SIM卡为非物联卡
EVENT_WHAT_IS_YOUR_TYPE = 1798, // 索要主机类型
EVENT_SIGNAL_STRENGTH_CHANGED = 1799, // 主机信号强度变化
// -------------------接警中心内部使用事件------------------------------
EVENT_PRIVATE_EVENT_BASE = 0x00010000,
EVENT_CLEARMSG, // 清除报警信息
EVENT_OFFLINE, // 主机断线
EVENT_ONLINE, // 主机上线
EVENT_SUBMACHINECNT, // 分机数量变化
EVENT_MACHINE_INFO_CHANGED, // 主机信息改变,需要界面刷新
EVENT_IM_GONNA_DIE, // 主机类已析构,通知界面
EVENT_LINK_TEST, // 主机心跳
EVENT_OP_FAILED, // 布撤防失败
EVENT_PRIVATE_EVENT_MAX,
};
#ifdef ENABLE_ADEMCO_EVENT_TO_STRING
static constexpr ADEMCO_EVENT AdemcoEvents[] = {
EVENT_ARM,
EVENT_DISARM,
EVENT_HALFARM,
EVENT_EMERGENCY,
EVENT_BURGLAR,
EVENT_DOORRINGING,
EVENT_FIRE,
EVENT_DURESS,
EVENT_GAS,
EVENT_WATER,
EVENT_TEMPER,
EVENT_ZONE_TEMPER,
EVENT_BY_PASS,
EVENT_BY_PASS_RESUME,
EVENT_AC_BROKE,
EVENT_AC_RECOVER,
EVENT_LOWBATTERY,
EVENT_BATTERY_RECOVER,
EVENT_BADBATTERY,
EVENT_SOLARDISTURB,
EVENT_DISCONNECT,
EVENT_RECONNECT,
EVENT_LOST,
EVENT_LOST_RECOVER,
EVENT_BATTERY_EXCEPTION,
EVENT_BATTERY_EXCEPTION_RECOVER,
EVENT_OTHER_EXCEPTION,
EVENT_OTHER_EXCEPTION_RECOVER,
EVENT_SERIAL485DIS,
EVENT_SERIAL485CONN,
EVENT_CONN_HANGUP,
EVENT_CONN_RESUME,
EVENT_DISARM_PWD_ERR,
EVENT_SUB_MACHINE_SENSOR_EXCEPTION,
EVENT_SUB_MACHINE_SENSOR_RESUME,
EVENT_SUB_MACHINE_POWER_EXCEPTION,
EVENT_SUB_MACHINE_POWER_RESUME,
EVENT_RETRIEVE_ZONE_OR_SUB_MACHINE,
EVENT_ENTER_SET_MODE,
EVENT_STOP_RETRIEVE,
EVENT_QUERY_SUB_MACHINE,
EVENT_WRITE_TO_MACHINE,
EVENT_I_AM_NET_MODULE,
EVENT_I_AM_GPRS,
EVENT_I_AM_LCD_MACHINE,
EVENT_I_AM_WIRE_MACHINE,
EVENT_I_AM_WIFI_MACHINE,
EVENT_SIM_IS_IOT_CARD,
EVENT_SIM_IS_NOT_IOT_CARD,
EVENT_PHONE_USER_SOS,
EVENT_PHONE_USER_CANCLE_ALARM,
EVENT_ENTER_SETTING_MODE,
EVENT_EXIT_SETTING_MODE,
EVENT_RESTORE_FACTORY_SETTINGS,
EVENT_WHAT_IS_YOUR_TYPE,
EVENT_SIGNAL_STRENGTH_CHANGED,
};
inline std::string ademcoEventToStringEnglish(ADEMCO_EVENT ademco_event, bool with_event_number = true)
{
auto n_to_s = [&with_event_number](uint32_t n) ->std::string {
if (with_event_number) {
return std::to_string(n) + "--";
}
return std::string();
};
switch (ademco_event) {
case EVENT_ARM: return n_to_s(ademco_event) + "ARM"; break;
case EVENT_BURGLAR: return n_to_s(ademco_event) + "BURGLAR"; break;
case EVENT_DISARM: return n_to_s(ademco_event) + "DISARM"; break;
case EVENT_DURESS: return n_to_s(ademco_event) + "DURESS"; break;
case EVENT_EMERGENCY: return n_to_s(ademco_event) + "EMERGENCY"; break;
case EVENT_FIRE: return n_to_s(ademco_event) + "FIRE"; break;
case EVENT_GAS: return n_to_s(ademco_event) + "GAS"; break;
case EVENT_HALFARM: case EVENT_HALFARM_1456: return n_to_s(ademco_event) + "HALFARM"; break;
case EVENT_TEMPER: return n_to_s(ademco_event) + "TEMPER"; break;
case EVENT_ZONE_TEMPER: return n_to_s(ademco_event) + "ZONE_TEMPER"; break;
case EVENT_BY_PASS: return n_to_s(ademco_event) + "EVENT_BY_PASS"; break;
case EVENT_BY_PASS_RESUME: return n_to_s(ademco_event) + "EVENT_BY_PASS_RESUME"; break;
case EVENT_WATER: return n_to_s(ademco_event) + "WATER"; break;
case EVENT_AC_BROKE: return n_to_s(ademco_event) + "AC BROKE"; break;
case EVENT_AC_RECOVER: return n_to_s(ademco_event) + "AC RECOVER"; break;
case EVENT_LOWBATTERY: return n_to_s(ademco_event) + "LOWBATTERY"; break;
case EVENT_BATTERY_RECOVER: return n_to_s(ademco_event) + "BATATTERY_RECOVER"; break;
case EVENT_BADBATTERY: return n_to_s(ademco_event) + "BADBATTERY"; break;
case EVENT_SOLARDISTURB: return n_to_s(ademco_event) + "SOLARDISTURB"; break;
case EVENT_DISCONNECT: return n_to_s(ademco_event) + "DISCONNECT"; break;
case EVENT_RECONNECT: return n_to_s(ademco_event) + "RECONNECT"; break;
case EVENT_LOST: return n_to_s(ademco_event) + "LOST"; break;
case EVENT_LOST_RECOVER: return n_to_s(ademco_event) + "LOST_RECOVER"; break;
case EVENT_BATTERY_EXCEPTION: return n_to_s(ademco_event) + "BATTERY_EXCEPTION"; break;
case EVENT_BATTERY_EXCEPTION_RECOVER: return n_to_s(ademco_event) + "BATTERY_EXCEPTION_RECOVER"; break;
case EVENT_OTHER_EXCEPTION: return n_to_s(ademco_event) + "OTHER_EXCEPTION"; break;
case EVENT_OTHER_EXCEPTION_RECOVER: return n_to_s(ademco_event) + "OTHER_EXCEPTION_RECOVER"; break;
case EVENT_SERIAL485DIS: return n_to_s(ademco_event) + "485DIS"; break;
case EVENT_SERIAL485CONN: return n_to_s(ademco_event) + "485CONN"; break;
case EVENT_DOORRINGING: return n_to_s(ademco_event) + "DOORRINGING"; break;
case EVENT_CONN_HANGUP: return n_to_s(ademco_event) + "CONN_HANGUP"; break;
case EVENT_CONN_RESUME: return n_to_s(ademco_event) + "CONN_RESUME"; break;
case EVENT_DISARM_PWD_ERR: return n_to_s(ademco_event) + "DISARM_PWD_ERR"; break;
case EVENT_SUB_MACHINE_SENSOR_EXCEPTION: return n_to_s(ademco_event) + "SUB_SENSOR_EXCEPTION"; break;
case EVENT_SUB_MACHINE_SENSOR_RESUME: return n_to_s(ademco_event) + "SUB_SENSOR_RESUME"; break;
case EVENT_SUB_MACHINE_POWER_EXCEPTION: return n_to_s(ademco_event) + "SUB_POWER_EXCEPTION"; break;
case EVENT_SUB_MACHINE_POWER_RESUME: return n_to_s(ademco_event) + "SUB_POWER_RESUME"; break;
case EVENT_RETRIEVE_ZONE_OR_SUB_MACHINE: return n_to_s(ademco_event) + "RETRIEVE"; break;
case EVENT_ENTER_SET_MODE: return n_to_s(ademco_event) + "ENTER_SET_MODE"; break;
case EVENT_STOP_RETRIEVE: return n_to_s(ademco_event) + "STOP RETRIEVE"; break;
case EVENT_QUERY_SUB_MACHINE: return n_to_s(ademco_event) + "QUERY"; break;
case EVENT_WRITE_TO_MACHINE: return n_to_s(ademco_event) + "WRITE_TO_MACHINE"; break;
case EVENT_I_AM_NET_MODULE: return n_to_s(ademco_event) + "I_AM_NET_MODULE"; break;
case EVENT_PHONE_USER_SOS: return n_to_s(ademco_event) + "SOS"; break;
case EVENT_PHONE_USER_CANCLE_ALARM: return n_to_s(ademco_event) + "PHONE_USER_CANCLE_ALARM"; break;
case EVENT_I_AM_GPRS: return n_to_s(ademco_event) + "EVENT_I_AM_GPRS"; break;
case EVENT_I_AM_LCD_MACHINE: return n_to_s(ademco_event) + "I_AM_LCD_MACHINE"; break;
case EVENT_I_AM_WIRE_MACHINE: return n_to_s(ademco_event) + "I_AM_WIRE_MACHINE"; break;
case EVENT_I_AM_WIFI_MACHINE: return n_to_s(ademco_event) + "I_AM_WIFI_MACHINE"; break;
case EVENT_SIM_IS_IOT_CARD: return n_to_s(ademco_event) + "SIM card is IOT"; break;
case EVENT_SIM_IS_NOT_IOT_CARD: return n_to_s(ademco_event) + "SIM card is not IOT"; break;
case EVENT_ENTER_SETTING_MODE: return n_to_s(ademco_event) + "ENTER_SETTING_MODE"; break;
case EVENT_EXIT_SETTING_MODE: return n_to_s(ademco_event) + "EXIT_SETTING_MODE"; break;
case EVENT_RESTORE_FACTORY_SETTINGS_710:
case EVENT_RESTORE_FACTORY_SETTINGS: return n_to_s(ademco_event) + "RESTORE_FACTORY_SETTINGS"; break;
case EVENT_WHAT_IS_YOUR_TYPE: return n_to_s(ademco_event) + "EVENT_WHAT_IS_YOUR_TYPE"; break;
case EVENT_SIGNAL_STRENGTH_CHANGED: return n_to_s(ademco_event) + "SIGNAL_STRENGTH_CHANGED"; break;
default: with_event_number = true; return n_to_s(ademco_event) + "undefined"; break;
}
}
inline const std::wstring ademcoEventToStringChinese(ADEMCO_EVENT ademco_event, bool with_event_number = true)
{
auto n_to_s = [&with_event_number](uint32_t n) ->std::wstring {
if (with_event_number) {
return std::to_wstring(n) + L"--";
}
return std::wstring();
};
switch (ademco_event) {
case EVENT_ARM: return n_to_s(ademco_event) + L"布防"; break;
case EVENT_BURGLAR: return n_to_s(ademco_event) + L"盗警"; break;
case EVENT_DISARM: return n_to_s(ademco_event) + L"撤防"; break;
case EVENT_DURESS: return n_to_s(ademco_event) + L"胁迫"; break;
case EVENT_EMERGENCY: return n_to_s(ademco_event) + L"紧急报警"; break;
case EVENT_FIRE: return n_to_s(ademco_event) + L"火警"; break;
case EVENT_GAS: return n_to_s(ademco_event) + L"煤气"; break;
case EVENT_HALFARM: case EVENT_HALFARM_1456: return n_to_s(ademco_event) + L"半布防"; break;
case EVENT_TEMPER: return n_to_s(ademco_event) + L"防拆"; break;
case EVENT_ZONE_TEMPER: return n_to_s(ademco_event) + L"防区防拆"; break;
case EVENT_WATER: return n_to_s(ademco_event) + L"水警"; break;
case EVENT_BY_PASS: return n_to_s(ademco_event) + L"旁路"; break;
case EVENT_BY_PASS_RESUME: return n_to_s(ademco_event) + L"解除旁路"; break;
case EVENT_AC_BROKE: return n_to_s(ademco_event) + L"主机AC掉电"; break;
case EVENT_AC_RECOVER: return n_to_s(ademco_event) + L"主机AC恢复"; break;
case EVENT_LOWBATTERY: return n_to_s(ademco_event) + L"低电"; break;
case EVENT_BATTERY_RECOVER: return n_to_s(ademco_event) + L"复电"; break;
case EVENT_BADBATTERY: return n_to_s(ademco_event) + L"坏电"; break;
case EVENT_SOLARDISTURB: return n_to_s(ademco_event) + L"光扰"; break;
case EVENT_DISCONNECT: return n_to_s(ademco_event) + L"失效"; break;
case EVENT_RECONNECT: return n_to_s(ademco_event) + L"恢复"; break;
case EVENT_LOST: return n_to_s(ademco_event) + L"失联"; break;
case EVENT_LOST_RECOVER: return n_to_s(ademco_event) + L"恢复"; break;
case EVENT_BATTERY_EXCEPTION: return n_to_s(ademco_event) + L"防区电源故障"; break;
case EVENT_BATTERY_EXCEPTION_RECOVER: return n_to_s(ademco_event) + L"防区电源故障恢复"; break;
case EVENT_OTHER_EXCEPTION: return n_to_s(ademco_event) + L"防区其他故障"; break;
case EVENT_OTHER_EXCEPTION_RECOVER: return n_to_s(ademco_event) + L"防区其他故障恢复"; break;
case EVENT_SERIAL485DIS: return n_to_s(ademco_event) + L"485断开"; break;
case EVENT_SERIAL485CONN: return n_to_s(ademco_event) + L"485恢复"; break;
case EVENT_DOORRINGING: return n_to_s(ademco_event) + L"门铃"; break;
case EVENT_CONN_HANGUP: return n_to_s(ademco_event) + L"链路挂起"; break;
case EVENT_CONN_RESUME: return n_to_s(ademco_event) + L"链路恢复"; break;
case EVENT_DISARM_PWD_ERR: return n_to_s(ademco_event) + L"撤防密码错误"; break;
case EVENT_SUB_MACHINE_SENSOR_EXCEPTION: return n_to_s(ademco_event) + L"分防区异常"; break;
case EVENT_SUB_MACHINE_SENSOR_RESUME: return n_to_s(ademco_event) + L"分防区恢复"; break;
case EVENT_SUB_MACHINE_POWER_EXCEPTION: return n_to_s(ademco_event) + L"分防区电源异常"; break;
case EVENT_SUB_MACHINE_POWER_RESUME: return n_to_s(ademco_event) + L"分防区电源恢复"; break;
case EVENT_RETRIEVE_ZONE_OR_SUB_MACHINE: return n_to_s(ademco_event) + L"索要/回复"; break;
case EVENT_ENTER_SET_MODE: return n_to_s(ademco_event) + L"进入设置状态"; break;
case EVENT_STOP_RETRIEVE: return n_to_s(ademco_event) + L"拒绝/停止索要"; break;
case EVENT_QUERY_SUB_MACHINE: return n_to_s(ademco_event) + L"查询"; break;
case EVENT_WRITE_TO_MACHINE: return n_to_s(ademco_event) + L"写入主机信息"; break;
case EVENT_I_AM_NET_MODULE: return n_to_s(ademco_event) + L"我是网络模块"; break;
case EVENT_PHONE_USER_SOS: return n_to_s(ademco_event) + L"手机用户SOS"; break;
case EVENT_PHONE_USER_CANCLE_ALARM: return n_to_s(ademco_event) + L"手机用户消警"; break;
case EVENT_I_AM_GPRS: return n_to_s(ademco_event) + L"我是GPRS主机"; break;
case EVENT_I_AM_LCD_MACHINE: return n_to_s(ademco_event) + L"我是液晶主机"; break;
case EVENT_I_AM_WIRE_MACHINE: return n_to_s(ademco_event) + L"我是网线主机"; break;
case EVENT_I_AM_WIFI_MACHINE: return n_to_s(ademco_event) + L"我是WiFi主机"; break;
case EVENT_SIM_IS_IOT_CARD: return n_to_s(ademco_event) + L"SIM卡为物联卡"; break;
case EVENT_SIM_IS_NOT_IOT_CARD: return n_to_s(ademco_event) + L"SIM卡为非物联卡"; break;
case EVENT_ENTER_SETTING_MODE: return n_to_s(ademco_event) + L"主机进入设置状态"; break;
case EVENT_EXIT_SETTING_MODE: return n_to_s(ademco_event) + L"主机退出设置状态"; break;
case EVENT_RESTORE_FACTORY_SETTINGS_710:
case EVENT_RESTORE_FACTORY_SETTINGS: return n_to_s(ademco_event) + L"主机恢复出厂设置"; break;
case EVENT_WHAT_IS_YOUR_TYPE: return n_to_s(ademco_event) + L"索要主机类型"; break;
case EVENT_SIGNAL_STRENGTH_CHANGED: return n_to_s(ademco_event) + L"信号强度变化"; break;
default: with_event_number = true; return n_to_s(ademco_event) + L"未定义"; break;
}
}
#endif
//! 是否主机状态事件
static inline bool isStatusEvent(ADEMCO_EVENT ademco_event)
{
return ademco_event == EVENT_ARM
|| ademco_event == EVENT_HALFARM
|| ademco_event == EVENT_DISARM;
}
// 安定宝事件级别
enum EventLevel
{
EVENT_LEVEL_NULL,
EVENT_LEVEL_STATUS, // 主机状态
EVENT_LEVEL_EXCEPTION_RESUME, // 黄色报警
EVENT_LEVEL_EXCEPTION, // 橙色报警
EVENT_LEVEL_ALARM, // 红色报警
};
#if !defined(__GNUG__) && defined(_AFX)
inline COLORREF GetEventLevelColor(EventLevel level)
{
switch (level) {
case ademco::EVENT_LEVEL_NULL:
return RGB(0, 187, 94); // 绿色
break;
case ademco::EVENT_LEVEL_EXCEPTION_RESUME:
return RGB(224, 224, 12); // 黄色
break;
case ademco::EVENT_LEVEL_EXCEPTION:
return RGB(0xFF, 0x80, 0x00); // 橙色
break;
case ademco::EVENT_LEVEL_ALARM:
return RGB(0xFF, 0x18, 0x18); // 红色
break;
default:
return RGB(255, 255, 255); // 白色
break;
}
}
#endif // !defined(__GNUG__) && defined(_AFX)
// 获取安定宝事件级别
inline EventLevel GetEventLevel(ADEMCO_EVENT ademco_event)
{
switch (ademco_event) {
case EVENT_ARM:
case EVENT_DISARM:
case EVENT_HALFARM:
return EVENT_LEVEL_STATUS;
break;
case EVENT_AC_RECOVER:
case EVENT_RECONNECT:
case EVENT_LOST_RECOVER:
case EVENT_SERIAL485CONN:
case EVENT_SUB_MACHINE_SENSOR_RESUME:
case EVENT_SUB_MACHINE_POWER_RESUME:
case EVENT_BATTERY_RECOVER:
case EVENT_BATTERY_EXCEPTION_RECOVER:
case EVENT_OTHER_EXCEPTION_RECOVER:
return EVENT_LEVEL_EXCEPTION_RESUME;
break;
case EVENT_AC_BROKE:
case EVENT_LOWBATTERY:
case EVENT_BADBATTERY:
case EVENT_SOLARDISTURB:
case EVENT_DISCONNECT:
case EVENT_LOST:
//case EVENT_DOORRINGING: //case EVENT_CONN_HANGUP: //case EVENT_CONN_RESUME:
case EVENT_SUB_MACHINE_SENSOR_EXCEPTION:
case EVENT_SUB_MACHINE_POWER_EXCEPTION:
case EVENT_BATTERY_EXCEPTION:
case EVENT_OTHER_EXCEPTION:
return EVENT_LEVEL_EXCEPTION;
break;
case EVENT_SERIAL485DIS:
case EVENT_BURGLAR:
case EVENT_DURESS:
case EVENT_EMERGENCY:
case EVENT_FIRE:
case EVENT_GAS:
case EVENT_TEMPER:
case EVENT_ZONE_TEMPER:
case EVENT_WATER:
return EVENT_LEVEL_ALARM;
default: break;
}
return EVENT_LEVEL_NULL;
}
//! 获取异常恢复事件所对应的异常事件
inline ADEMCO_EVENT getExceptionEventByResumeEvent(ADEMCO_EVENT resume_event)
{
switch (resume_event) {
case EVENT_RECONNECT: return EVENT_DISCONNECT; break;
case EVENT_LOST_RECOVER: return EVENT_LOST; break;
case EVENT_SERIAL485CONN: return EVENT_SERIAL485DIS; break;
case EVENT_SUB_MACHINE_SENSOR_RESUME: return EVENT_SUB_MACHINE_SENSOR_EXCEPTION; break;
case EVENT_SUB_MACHINE_POWER_RESUME: return EVENT_SUB_MACHINE_POWER_EXCEPTION; break;
case EVENT_BATTERY_RECOVER: return EVENT_LOWBATTERY; break;
case EVENT_BATTERY_EXCEPTION_RECOVER: return EVENT_BATTERY_EXCEPTION; break;
case EVENT_OTHER_EXCEPTION_RECOVER: return EVENT_OTHER_EXCEPTION; break;
case EVENT_AC_RECOVER: return EVENT_AC_BROKE; break;
default: return EVENT_INVALID_EVENT; break;
}
}
//! 安定宝事件源
enum EventSource
{
ES_UNKNOWN,
ES_TCP_CLIENT, // 从客户端接收的事件 (如直连型网络模块、GPRS主机等)
ES_TCP_SERVER1, // 从1号中转服务器接收的事件
ES_TCP_SERVER2, // 从2号中转服务器接收的事件
ES_SMS, // 接警中心短信模块收到的事件
};
//! 安定宝事件源字符串
static inline const char* eventSourceString(EventSource es) {
switch (es) {
case EventSource::ES_TCP_CLIENT:
return "ES_TCP_CLIENT";
break;
case EventSource::ES_TCP_SERVER1:
return "ES_TCP_SERVER1";
break;
case EventSource::ES_TCP_SERVER2:
return "ES_TCP_SERVER2";
break;
case EventSource::ES_SMS:
return "ES_SMS";
break;
case EventSource::ES_UNKNOWN:
default:
return "ES_UNKNOWN";
break;
}
}
//! 安定宝事件 xdata 段
struct XData
{
enum class LengthFormat {
TWO_HEX,
FOUR_DECIMAL,
};
LengthFormat lengthFormat_ = LengthFormat::TWO_HEX;
//! 刨去[ len ]后,真实有效数据的长度,用来程序调用
std::vector<char> data_ = {};
//! 包含 [ len ... ] 的全部数据长度,可以用来网络发送
std::vector<char> rawData_ = {};
XData() {}
size_t size() const { return data_.size(); }
size_t rawSize() const { return rawData_.size(); }
bool operator==(const XData& rhs) const {
return lengthFormat_ == rhs.lengthFormat_
&& data_ == rhs.data_;
}
};
typedef std::shared_ptr<XData> XDataPtr;
inline bool operator==(const XDataPtr& lhs, const XDataPtr& rhs)
{
if (lhs && rhs) {
return *lhs == *rhs;
} else if (!lhs && !rhs) {
return true;
} else {
return false;
}
}
//! 安定宝事件
struct AdemcoEvent
{
EventSource eventSource_ = ES_UNKNOWN; // 来源
ADEMCO_EVENT event_ = EVENT_INVALID_EVENT; // 事件码
uint32_t zone_ = 0; // 防区号
uint8_t gg_ = 0; // 分防区号
time_t timestamp_ = 0; // 时间戳
time_t recv_time_ = 0; // 接收时间
XDataPtr xdata_ = {}; // 附加信息
AdemcoEvent() {}
AdemcoEvent(EventSource source, ADEMCO_EVENT ademco_event, uint32_t zone, uint8_t gg, time_t timestamp,
time_t recv_time, const XDataPtr& xdata = {})
: eventSource_(source), event_(ademco_event), zone_(zone), gg_(gg),
timestamp_(timestamp), recv_time_(recv_time), xdata_(xdata)
{}
AdemcoEvent(const AdemcoEvent& rhs)
: eventSource_(rhs.eventSource_), event_(rhs.event_), zone_(rhs.zone_), gg_(rhs.gg_),
timestamp_(rhs.timestamp_), recv_time_(rhs.recv_time_), xdata_(rhs.xdata_)
{}
~AdemcoEvent()
{}
AdemcoEvent& operator=(const AdemcoEvent& rhs)
{
eventSource_ = rhs.eventSource_;
event_ = rhs.event_;
zone_ = rhs.zone_;
gg_ = rhs.gg_;
timestamp_ = rhs.timestamp_;
recv_time_ = rhs.recv_time_;
xdata_ = rhs.xdata_;
return *this;
}
bool operator == (const AdemcoEvent& rhs)
{
return (event_ == rhs.event_)
&& (zone_ == rhs.zone_)
&& (gg_ == rhs.gg_)
&& (xdata_ == rhs.xdata_);
}
};
typedef std::shared_ptr<AdemcoEvent> AdemcoEventPtr;
} // namespace ademco
#pragma once
#include "ademco_event.h"
#include "ademco_detail.h"
namespace ademco
{
//! 解析数据包的返回值
enum class ParseResult
{
//! 解析到正确、完整的一包
RESULT_OK,
//! 解析到正确、不完整的一包,需要缓存数据,下次继续处理
RESULT_NOT_ENOUGH,
//! 解析到错误的数据,需要清空缓冲区
RESULT_DATA_ERROR,
};
//! 安定宝数据包 data 段
struct AdemcoData
{
//! 数据
std::vector<char> data_ = {};
//! 安定宝ID
size_t ademco_id_ = 0;
//! 安定宝事件码
ADEMCO_EVENT ademco_event_ = ADEMCO_EVENT::EVENT_INVALID_EVENT;
//! gg
unsigned char gg_ = 0;
//! zone
size_t zone_ = 0;
AdemcoData() { make(); }
void clear() {
data_.clear();
ademco_id_ = 0;
ademco_event_ = EVENT_INVALID_EVENT;
gg_ = 0;
zone_ = 0;
}
bool valid() const { return data_.size() >= 2; }
size_t size() const { return data_.size(); }
std::string toString() const {
std::string str;
std::copy(data_.begin(), data_.end(), std::back_inserter(str));
return str;
}
void assignAdecoId(size_t ademco_id) {
char aid[7] = { 0 };
snprintf(aid, sizeof(aid), "%06X", static_cast<int>(ademco_id % 1000000));
std::copy(aid, aid + 6, std::back_inserter(data_));
ademco_id_ = ademco_id;
}
void assignAdemcoEvent(ADEMCO_EVENT ademco_event) {
char aev[5] = { 0 };
snprintf(aev, sizeof(aev), "%04d", static_cast<int>(ademco_event % 10000));
std::copy(aev, aev + 4, std::back_inserter(data_));
ademco_event_ = ademco_event;
}
void assignGG(size_t gg) {
if (gg == 0xEE) {
data_.push_back('E');
data_.push_back('E');
} else if (gg == 0xCC) {
data_.push_back('C');
data_.push_back('C');
} else {
data_.push_back(static_cast<char>(((gg / 10) & 0x0F) + '0'));
data_.push_back(static_cast<char>(((gg % 10) & 0x0F) + '0'));
}
gg_ = static_cast<unsigned char>(gg);
}
void assignZone(size_t zone) {
char z[4] = { 0 };
snprintf(z, sizeof(z), "%03d", static_cast<int>(zone % 1000));
std::copy(z, z + 3, std::back_inserter(data_));
zone_ = zone;
}
//! 生成可用于网络传输的数据
void make(size_t ademco_id, size_t gg, ADEMCO_EVENT ademco_event, size_t zone) {
if (EVENT_INVALID_EVENT == ademco_event) { make(); return; }
clear();
data_.reserve(21); // [#000000|1400 00 000]
data_.push_back('[');
data_.push_back('#');
assignAdecoId(ademco_id);
data_.push_back('|');
assignAdemcoEvent(ademco_event);
data_.push_back(' ');
assignGG(gg);
data_.push_back(' ');
assignZone(zone);
data_.push_back(']');
assert(data_.size() == 21);
}
//! 生成可用于网络传输的数据
void make(const char* acct, size_t gg, ADEMCO_EVENT ademco_event, size_t zone) {
clear();
data_.clear();
data_.push_back('[');
data_.push_back('#');
std::copy(acct, acct + strlen(acct), std::back_inserter(data_));
data_.push_back('|');
assignAdemcoEvent(ademco_event);
data_.push_back(' ');
assignGG(gg);
data_.push_back(' ');
assignZone(zone);
data_.push_back(']');
}
//! 生成可用于网络传输的空白data数据
void make() {
clear();
data_.push_back('[');
data_.push_back(']');
}
// parser
bool parse(const char* pack, size_t pack_len) {
//AUTO_LOG_FUNCTION;
clear();
const char* p = pack;
do {
if (*p++ != '[')
break;
size_t acct_len = 4;
if (pack_len == 2 && *p == ']') {
make();
return true;
// [ # acct | mt s q event s gg s zone ] // 19
} else if (pack_len == 1 + 1 + 4 + 1 + /*2 + 1 + */1 + 3 + 1 + 2 + 1 + 3 + 1) {
acct_len = 4;
} else if (pack_len == 1 + 1 + 6 + 1 + /*2 + 1 + */1 + 3 + 1 + 2 + 1 + 3 + 1) { // 21)
acct_len = 6;
} else if (pack_len == 1 + 1 + 14 + 1 + /*2 + 1 + */1 + 3 + 1 + 2 + 1 + 3 + 1) { // 29) // just ignore it
acct_len = 14;
} else if (pack_len > 15) {
//break;
acct_len = pack_len - 15; // just ignore it
}
if (*p++ != '#')
break;
if (acct_len == 4) {
if (*(p + 4) != '|')
break;
ademco_id_ = detail::HexCharArrayToDec(p, 4);
p += 5;
} else if (acct_len == 6) {
if (*(p + 6) != '|')
break;
ademco_id_ = detail::HexCharArrayToDec(p, 6);
p += 7;
} else if (acct_len == 14) { // just ignore it
if (*(p + 14) != '|')
break;
ademco_id_ = 0;
p += 15;
} else { // just ignore it
ademco_id_ = 0;
p += acct_len + 1;
}
ademco_event_ = static_cast<ADEMCO_EVENT>(detail::NumCharArray2Dec(p, 4));
p += 4;
if (*p++ != ' ')
break;
if (*p == 'E' && *(p + 1) == 'E') {
gg_ = 0xEE;
} else if (*p == 'C' && *(p + 1) == 'C') {
gg_ = 0xCC;
} else {
gg_ = static_cast<unsigned char>((*p - '0') * 10 + (*(p + 1) - '0'));
}
p += 2;
if (*p++ != ' ')
break;
zone_ = detail::NumCharArray2Dec(p, 3);
std::copy(pack, pack + pack_len, std::back_inserter(data_));
return true;
} while (0);
return false;
}
bool operator==(const AdemcoData& rhs) const {
return data_ == rhs.data_
&& ademco_id_ == rhs.ademco_id_
&& ademco_event_ == rhs.ademco_event_
&& gg_ == rhs.gg_
&& zone_ == rhs.zone_;
}
}; // AdemcoData
// 2016-11-4 16:47:51 for compatibility of congwin fe100 protocal
struct CongwinFe100Packet
{
char data_[32] = { 0 };
const int len_ = 31;
bool fromAdemcoData(const AdemcoData* data) {
assert(data && data->valid());
if (!data || !data->valid()) {
return false;
}
size_t acct = data->ademco_id_;
auto evnt = data->ademco_event_;
//int gg = data->_gg;
size_t zone = data->zone_;
int ndx = 0;
data_[ndx++] = 0x0A; // LF
data_[ndx++] = 0x20;
data_[ndx++] = 0x30; // RG
data_[ndx++] = 0x30;
data_[ndx++] = 0x20;
ndx += snprintf(data_ + ndx, sizeof(data_) - ndx, "%08d", static_cast<int>(acct)); // acct
//ndx += 4;
data_[ndx++] = 0x20;
data_[ndx++] = 0x31; // 18
data_[ndx++] = 0x38;
data_[ndx++] = 0x20;
bool status_evnt = isStatusEvent(evnt);
if (status_evnt) {
//data_[ndx++] = 'E';
}
if (evnt / 1000 == 1) {
data_[ndx++] = 'E';
} else {
if (evnt == 3456) {
data_[ndx++] = 'E';
} else {
data_[ndx++] = 'R';
}
}
snprintf(data_ + ndx, sizeof(data_) - ndx, "%03d", static_cast<int>(evnt % 1000)); // event
ndx += 3;
data_[ndx++] = 0x20;
data_[ndx++] = 0x30; // gg is always 00
data_[ndx++] = 0x30;
data_[ndx++] = 0x20;
//data_[ndx++] = 0x43; // FCCC, F is always, 'C' for zone, 'U' for user
if (status_evnt) {
data_[ndx++] = 0x55; // U
} else {
data_[ndx++] = 0x43; // C
}
sprintf(data_ + ndx, "%03d", static_cast<int>(zone % 10000));
ndx += 3;
data_[ndx++] = 0x20;
data_[ndx++] = 0x0D;
return true;
}
}; // CongwinFe100Packet
//! 生成xdata
static XDataPtr makeXData(const std::vector<char>& payload, XData::LengthFormat lengthFormat = XData::LengthFormat::TWO_HEX)
{
auto xdata = std::make_shared<XData>();
xdata->lengthFormat_ = lengthFormat;
switch (lengthFormat)
{
case XData::LengthFormat::FOUR_DECIMAL:
{
xdata->data_ = payload;
// [ len ]
xdata->rawData_.resize(1 + 4 + payload.size() + 1);
xdata->rawData_[0] = '[';
/*Dec2HexCharArray_4(payload.size() * 2, &xdata->rawData_[1], false);
for (size_t i = 0; i < payload.size(); i++) {
xdata->rawData_[5 + i * 2] = detail::Dec2Hex((payload[i] >> 4) & 0x0F);
xdata->rawData_[5 + i * 2 + 1] = detail::Dec2Hex((payload[i] & 0x0F));
}*/
detail::Dec2HexCharArray_4(payload.size(), &xdata->rawData_[1], false);
memcpy(&xdata->rawData_[5], payload.data(), payload.size());
xdata->rawData_.back() = ']';
break;
}
case XData::LengthFormat::TWO_HEX:
default:
{
xdata->data_ = payload;
xdata->rawData_.resize(1 + 2 + payload.size() + 1);
xdata->rawData_[0] = '[';
xdata->rawData_[1] = static_cast<char>((payload.size() << 8) & 0xFF);
xdata->rawData_[2] = static_cast<char>(payload.size() & 0xFF);
memcpy(&xdata->rawData_[3], payload.data(), payload.size());
xdata->rawData_.back() = ']';
}
break;
}
return xdata;
}
static XDataPtr makeXData(const char* pack, size_t len, XData::LengthFormat lengthFormat = XData::LengthFormat::TWO_HEX) {
std::vector<char> data(pack, pack + len);
return makeXData(data, lengthFormat);
}
static XDataPtr parseXData(const char* pack, const char* pack_end)
{
XDataPtr xdata;
do {
auto p = pack;
if (*p++ != '[') { break; } // skip [
size_t xdata_len = 0;
XData::LengthFormat format = XData::LengthFormat::FOUR_DECIMAL;
for (size_t i = 0; i < 4; i++) {
if (!isalpha(*(p + i)) && !isdigit(*(p + i))) {
format = XData::LengthFormat::TWO_HEX;
break;
}
}
if (format == XData::LengthFormat::FOUR_DECIMAL) {
// special condition, use 4 char to represent length.
xdata_len = detail::HexCharArrayToDec(p, 4);
p += 4; // skip len
} else {
// normal condition, use 2 hex to represent length
xdata_len = (*p * 256 + *(p + 1));
p += 2; // skip len
}
const char* xdata_pos = p;
p += xdata_len;
if (p >= pack_end || *p++ != ']') { // skip ]
JLOG_ERRO("AdemcoPacket::Parse parse xdata failed!");
break;
}
xdata = std::make_shared<XData>();
// copy raw data
std::copy(pack, p, std::back_inserter(xdata->rawData_));
// copy payload data
std::copy(xdata_pos, xdata_pos + xdata_len, std::back_inserter(xdata->data_));
} while (0);
return xdata;
} // parseXData
static std::string xdataToString(const XDataPtr& xdata, detail::ToStringOption option = detail::ToStringOption::TRY_IS_ALNUM_FIRST) {
std::string str;
if (!xdata) { return str; }
str += "[";
// length
if (xdata->lengthFormat_ == XData::LengthFormat::TWO_HEX) {
str += "\\x";
auto hi = (xdata->size() >> 8) & 0xFF;
str.push_back(ademco::detail::Dec2Hex((hi >> 4) & 0x0F));
str.push_back(ademco::detail::Dec2Hex(hi & 0x0F));
str += "\\x";
auto lo = xdata->size() & 0xFF;
str.push_back(ademco::detail::Dec2Hex((lo >> 4) & 0x0F));
str.push_back(ademco::detail::Dec2Hex(lo & 0x0F));
} else {
std::copy(&xdata->rawData_[1], &xdata->rawData_[5], std::back_inserter(str));
}
// data
str += toString(xdata->data_, option, true, false);
str += "]";
return str;
}
struct AdemcoTimeStamp
{
// timestamp, ademco format is _23:59:59,12-31-2000, so its len is 20.
static constexpr size_t length = 20;
char data_[length + 1] = { 0 };
time_t time_ = 0;
AdemcoTimeStamp() { make(); }
void make() {
time_ = time(nullptr);
struct tm tm;
localtime_r(&time_, &tm);
snprintf(data_, sizeof(data_), "_%02d:%02d:%02d,%02d-%02d-%04d",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_mon + 1, tm.tm_mday, tm.tm_year + 1900);
assert(length == strnlen(data_, sizeof(data_)));
}
// always return true
bool parse(const char* pack, size_t pack_len) {
if (pack_len < length) {
return false;
}
memcpy(data_, pack, length);
data_[length] = '\0'; // to string
struct tm tm;
int ret = sscanf(pack, "_%02d:%02d:%02d,%02d-%02d-%04d",
&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
&tm.tm_mon, &tm.tm_mday, &tm.tm_year);
if (ret != 6) {
JLOG_WARN("AdemcoTimeStamp::Parse sscanf_s ret {} != 6", ret);
JLOG_HEX(pack, pack_len);
// ignore it
//return false;
}
tm.tm_year -= 1900;
tm.tm_mon--;
tm.tm_isdst = -1;
time_ = mktime(&tm);
if (time_ < 0) {
make();
return true;
}
return true;
}
}; // AdemcoTimeStamp
/**
* @brief 数值的字符串
*/
struct NumStr16
{
static constexpr int length = 4;
char data_[length + 1] = { '0', '0', '0', '0', '\0' };
uint16_t value_ = 0;
NumStr16() = default;
virtual ~NumStr16() = default;
virtual void fromCharArray(const char[length]) = 0;
virtual void fromUint16(uint16_t value) = 0;
const char* toString() {
data_[length] = '\0';
return data_;
}
};
/**
* @brief 16进制数字的字符串
* @note 可以表示的数字范围为 [0x0000, 0xFFFF], 即一个 uint16
* @note 例:数字 0 表示为 "0000", 数字 256 表示为 "0100", 数字 65535 表示为 "FFFF"
*/
struct HexStr16 : public NumStr16
{
HexStr16() = default;
virtual ~HexStr16() = default;
HexStr16(uint16_t value) { fromUint16(value); }
HexStr16(const NumStr16& rhs) { fromCharArray(rhs.data_); }
HexStr16& operator=(uint16_t value) { fromUint16(value); return *this;}
HexStr16& operator=(const HexStr16& rhs) { fromCharArray(rhs.data_); return *this; }
virtual void fromCharArray(const char data[length]) override {
memcpy(data_, data, length);
value_ = static_cast<uint16_t>(detail::HexCharArrayToDec(data, length));
data_[length] = '\0';
}
virtual void fromUint16(uint16_t value) override {
assert(0 <= value && value <= 0xFFFF);
value_ = value;
detail::Dec2HexCharArray_4(value, data_, false);
data_[length] = '\0';
}
};
typedef HexStr16 Crc16;
//! 16进制数字的字符串,与 Crc16不同的是,Length16 有最大值 0x0FFF
struct Length16 : public HexStr16
{
Length16() = default;
virtual ~Length16() = default;
Length16(uint16_t value) { fromUint16(value); }
Length16(const Length16& rhs) { fromCharArray(rhs.data_); }
Length16& operator=(uint16_t value) { fromUint16(value); return *this; }
Length16& operator=(const Length16& rhs) { fromCharArray(rhs.data_); return *this; }
virtual void fromUint16(uint16_t value) override {
assert(0 <= value && value <= 0x0FFF);
value_ = value;
detail::Dec2HexCharArray_4(value, data_, true);
data_[length] = '\0';
}
};
/**
* @brief 10进制数字的字符串
* @note 可以表示的数字范围为 [0, 9999]
* @note 例:数字 0 表示为 "0000", 数字 256 表示为 "0256"
*/
struct DecStr16 : public NumStr16
{
DecStr16() = default;
virtual ~DecStr16() = default;
DecStr16(uint16_t value) { fromUint16(value); }
DecStr16(const DecStr16& rhs) { fromCharArray(rhs.data_); }
DecStr16& operator=(uint16_t value) { fromUint16(value); return *this; }
DecStr16& operator=(const DecStr16& rhs) { fromCharArray(rhs.data_); return *this; }
virtual void fromCharArray(const char data[length]) override {
memcpy(data_, data, length);
value_ = static_cast<uint16_t>(detail::NumCharArray2Dec(data, length) % 10000);
}
virtual void fromUint16(uint16_t value) override {
assert(0 <= value && value <= 9999);
value_ = value;
snprintf(data_, sizeof(data_), "%04d", static_cast<int>(value % 10000));
}
};
typedef DecStr16 AdemcoSeq;
static constexpr size_t ID_COUNT = 8;
static constexpr const char* ID_NULL = "\"NULL\"";
static constexpr const char* ID_ACK = "\"ACK\"";
static constexpr const char* ID_NAK = "\"NAK\"";
static constexpr const char* ID_DUH = "\"DUH\"";
static constexpr const char* ID_HB = "\"HENG-BO\"";
static constexpr const char* ID_ADM_CID = "\"ADM-CID\"";
static constexpr const char* ID_MOD_REG = "\"MODULE-REG\"";
static constexpr const char* ID_REG_RSP = "\"REG-RSP\"";
static constexpr const char* AdemcoIds[ID_COUNT] = {
ID_NULL,
ID_ACK,
ID_NAK,
ID_DUH,
ID_HB,
ID_ADM_CID,
ID_MOD_REG,
ID_REG_RSP,
};
struct AdemcoId
{
enum Enum {
id_null,
id_ack,
id_nak,
id_duh,
id_hb,
id_admcid,
id_modreg,
id_regresp,
};
static bool isKnownId(const char* id) {
for (auto p : AdemcoIds) { if (strncmp(id, p, strlen(p)) == 0) { return true; } }
return false;
}
Enum eid_ = Enum::id_null;
void clear() { eid_ = Enum::id_null; }
const char* data() const { return AdemcoIds[eid_]; }
size_t size() const { return strlen(AdemcoIds[eid_]); }
bool checkAndSet(const char* id, size_t id_len) {
for (Enum eid = Enum::id_null; eid <= Enum::id_regresp; eid = static_cast<Enum>(eid + 1)) {
auto p = AdemcoIds[eid];
if (strncmp(id, p, std::min(strlen(p), id_len)) == 0) {
eid_ = eid; return true;
}
}
return false;
}
}; // AdemcoId
struct Rrcvr
{
static constexpr const char* default_data_ = "R123ABC";
std::vector<char> data_ = {};
Rrcvr() { setDefault(); }
void clear() { data_.clear(); }
void setDefault() { data_.clear(); std::copy(default_data_, default_data_ + strlen(default_data_), std::back_inserter(data_)); }
size_t size() const { return data_.size(); }
void fromCharArray(const char* data, size_t len) {
data_.clear(); std::copy(data, data + len, std::back_inserter(data_));
}
};
struct Lpref
{
static constexpr const char* default_data_ = "L456DEF";
std::vector<char> data_ = {};
Lpref() { setDefault(); }
void clear() { data_.clear(); }
void setDefault() { data_.clear(); std::copy(default_data_, default_data_ + strlen(default_data_), std::back_inserter(data_)); }
size_t size() const { return data_.size(); }
void fromCharArray(const char* data, size_t len) {
data_.clear(); std::copy(data, data + len, std::back_inserter(data_));
}
};
struct AdemcoAcct
{
private:
std::vector<char> data_ = {};
public:
AdemcoAcct() { clear(); }
void clear() { setAcct(size_t(0)); }
//! asString
const char* acct() const { return &data_[1]; }
const char* data() const { return &data_[0]; }
size_t size() const { return data_.size() - 1; }
// acct must be null-terminated
void setAcct(const char* acct) { setAcct(acct, strlen(acct)); }
void setAcct(const char* acct, size_t len) {
data_.resize(1 + len + 1); data_[0] = '#';
std::copy(acct, acct + len, &data_[1]);
data_[1 + len] = '\0';
}
void setAcct(size_t acct) {
data_.resize(8); data_[0] = '#';
snprintf(&data_[1], 7, "%06d", static_cast<int>(acct % 1000000));
}
void setAcct(const std::string& acct) {
data_.resize(1 + acct.size() + 1); data_[0] = '#';
std::copy(acct.begin(), acct.end(), &data_[1]);
data_[1 + acct.size()] = '\0';
}
}; // AdemcoAcct
struct AdemcoPacket
{
static constexpr char LF = 0x0A;
Crc16 crc_ = {};
Length16 len_ = {};
AdemcoId id_ = {};
AdemcoSeq seq_ = {};
Rrcvr rrcvr_ = {};
Lpref lpref_ = {};
AdemcoAcct acct_ = {};
AdemcoData ademcoData_ = {};
XDataPtr xdata_ = {};
AdemcoTimeStamp timestamp_ = {};
static constexpr char CR = 0x0D;
AdemcoPacket() {}
void clear() {
crc_ = 0;
len_ = 0;
id_.clear();
seq_ = 0;
rrcvr_.clear();
lpref_.clear();
acct_.clear();
ademcoData_.clear();
xdata_.reset();
}
size_t calcLength() const {
// LF CRC LEN SEQ
return 1 + 4 + 4 + id_.size() + 4 + rrcvr_.size() + lpref_.size()
+ acct_.size() + ademcoData_.size() + (xdata_ ? xdata_->rawSize() : 0) +
timestamp_.length + 1; // CR
}
void copyData(char* dst, size_t length) {
assert(length == calcLength());
char* pos = dst;
*pos++ = LF;
char* crc_pos = pos; pos += 4;
char* len_pos = pos; pos += 4;
char* id_pos = pos;
memcpy(id_pos, id_.data(), id_.size()); pos += id_.size();
memcpy(pos, seq_.data_, seq_.length); pos += seq_.length;
memcpy(pos, &rrcvr_.data_[0], rrcvr_.size()); pos += rrcvr_.size();
memcpy(pos, &lpref_.data_[0], lpref_.size()); pos += lpref_.size();
memcpy(pos, acct_.data(), acct_.size()); pos += acct_.size();
memcpy(pos, &ademcoData_.data_[0], ademcoData_.size()); pos += ademcoData_.size();
if (xdata_) {
memcpy(pos, &xdata_->rawData_[0], xdata_->rawSize());
pos += xdata_->rawSize();
}
memcpy(pos, timestamp_.data_, timestamp_.length); pos += timestamp_.length;
*pos++ = CR;
assert(static_cast<size_t>(pos - dst) == length);
size_t ademco_len = length - 9 - 1;
detail::Dec2HexCharArray_4(detail::CalculateCRC(id_pos, ademco_len), crc_pos, false);
detail::Dec2HexCharArray_4(ademco_len, len_pos);
}
std::string toString() {
char buf[1024] = { 0 };
auto sz = make(buf, sizeof(buf));
buf[sz] = '\0';
char* pos = buf;
*pos++ = '\\';
*pos++ = 'n';
char* crc_pos = pos; pos += 4;
char* len_pos = pos; pos += 4;
char* id_pos = pos;
memcpy(id_pos, id_.data(), id_.size()); pos += id_.size();
memcpy(pos, seq_.data_, seq_.length); pos += seq_.length;
memcpy(pos, &rrcvr_.data_[0], rrcvr_.size()); pos += rrcvr_.size();
memcpy(pos, &lpref_.data_[0], lpref_.size()); pos += lpref_.size();
memcpy(pos, acct_.data(), acct_.size()); pos += acct_.size();
memcpy(pos, &ademcoData_.data_[0], ademcoData_.size()); pos += ademcoData_.size();
char* xdata_pos = pos;
if (xdata_) {
memcpy(pos, &xdata_->rawData_[0], xdata_->rawSize());
pos += xdata_->rawSize();
}
memcpy(pos, timestamp_.data_, timestamp_.length); pos += timestamp_.length;
size_t ademco_len = calcLength();
detail::Dec2HexCharArray_4(detail::CalculateCRC(id_pos, ademco_len), crc_pos, false);
detail::Dec2HexCharArray_4(ademco_len, len_pos);
if (xdata_) {
for (auto c : xdataToString(xdata_)) {
*xdata_pos++ = c;
}
memcpy(xdata_pos, timestamp_.data_, timestamp_.length); xdata_pos += timestamp_.length;
*xdata_pos++ = '\\';
*xdata_pos++ = 'r';
*xdata_pos++ = '\0';
} else {
*pos++ = '\\';
*pos++ = 'r';
*pos++ = '\0';
}
return buf;
}
// maker. set all data before call it.
size_t make(char* pack, size_t pack_len) {
assert(pack);
size_t length = calcLength();
assert(length < pack_len);
if (length < pack_len) {
copyData(pack, length);
return length;
} else { return 0; }
}
// set acct to nullptr if you want to use ademco_id as acct
size_t make(char* pack, size_t pack_len,
AdemcoId::Enum id,
uint16_t seq,
const char* acct,
size_t ademco_id,
unsigned char gg,
ADEMCO_EVENT aevnt,
size_t zone,
XDataPtr xdata = {})
{
assert(pack);
id_.eid_ = id;
seq_.fromUint16(seq);
rrcvr_.setDefault(); lpref_.setDefault();
acct ? acct_.setAcct(acct) : acct_.setAcct(ademco_id);
ademcoData_.make(ademco_id, gg, aevnt, zone);
xdata_ = xdata;
timestamp_.make();
size_t length = calcLength();
assert(length < pack_len);
if (length < pack_len) {
copyData(pack, length);
return length;
} else { return 0; }
}
// set acct to nullptr if you want to use ademco_id as acct
size_t make_null(char* pack, size_t pack_len, uint16_t seq, const char* acct, size_t ademco_id)
{
id_.eid_ = AdemcoId::Enum::id_null;
seq_ = seq;
rrcvr_.setDefault(); lpref_.setDefault();
acct ? acct_.setAcct(acct) : acct_.setAcct(ademco_id);
ademcoData_.make();
xdata_.reset();
timestamp_.make();
size_t length = calcLength();
assert(length < pack_len);
if (length < pack_len) {
copyData(pack, length);
return length;
} else { return 0; }
}
// set acct to empty if you want to use ademco_id as acct
size_t make_null(char* pack, size_t pack_len, uint16_t seq, const std::string& acct, size_t ademco_id) {
return make_null(pack, pack_len, seq, acct.empty() ? nullptr : acct.data(), ademco_id);
}
// set acct to nullptr if you want to use ademco_id as acct
size_t make_ack(char* pack, size_t pack_len, uint16_t seq, const char* acct, size_t ademco_id)
{
id_.eid_ = AdemcoId::Enum::id_ack;
seq_ = seq;
rrcvr_.setDefault(); lpref_.setDefault();
acct ? acct_.setAcct(acct) : acct_.setAcct(ademco_id);
ademcoData_.make();
xdata_.reset();
timestamp_.make();
size_t length = calcLength();
assert(length < pack_len);
if (length < pack_len) {
copyData(pack, length);
return length;
} else { return 0; }
}
// set acct to nullptr if you want to use ademco_id as acct
size_t make_ack(char* pack, size_t pack_len, uint16_t seq, const std::string& acct, size_t ademco_id) {
return make_ack(pack, pack_len, seq, acct.empty() ? nullptr : acct.data(), ademco_id);
}
// set acct to nullptr if you want to use ademco_id as acct
size_t make_hb(char* pack, size_t pack_len,
uint16_t seq,
const char* acct,
size_t ademco_id,
unsigned char gg,
ADEMCO_EVENT evnt,
size_t zone,
XDataPtr xdata = {})
{
id_.eid_ = AdemcoId::Enum::id_hb;
seq_ = seq;
rrcvr_.setDefault(); lpref_.setDefault();
acct ? acct_.setAcct(acct) : acct_.setAcct(ademco_id);
ademcoData_.make(ademco_id, gg, evnt, zone);
xdata_ = xdata;
timestamp_.make();
size_t length = calcLength();
assert(length < pack_len);
if (length < pack_len) {
copyData(pack, length);
return length;
} else { return 0; }
}
// set acct to empty if you want to use ademco_id as acct
size_t make_hb(char* pack, size_t pack_len,
uint16_t seq,
const std::string& acct,
size_t ademco_id,
unsigned char gg,
ADEMCO_EVENT evnt,
size_t zone,
XDataPtr xdata = {})
{
return make_hb(pack, pack_len, seq, acct.empty() ? nullptr : acct.data(), ademco_id, gg, evnt, zone, xdata);
}
// parser
ParseResult parse(const char* pack, size_t pack_len, size_t& cb_commited) {
//AUTO_LOG_FUNCTION;
do {
if (pack_len < 9) {
JLOG_INFO("AdemcoPacket::Parse pack_len {} < 9", pack_len); JLOG_HEX(pack, pack_len);
return ParseResult::RESULT_NOT_ENOUGH;
}
// check LF
if (pack[0] != LF) {
JLOG_ERRO("AdemcoPacket::Parse pack[0] {:d} 0x{:02X} is not _LF", pack[0], pack[0]);
#ifdef _WIN32
return ParseResult::RESULT_DATA_ERROR;
#else
break;
#endif
}
clear();
// read crc & len
crc_.fromCharArray(pack + 1);
len_.fromCharArray(pack + 5);
// read till CR
size_t len_to_parse = 9 + len_.value_ + 1; // 1 for CR
// check if packet is enough to parse
if (pack_len < len_to_parse) {
JLOG_ERRO("AdemcoPacket::Parse pack_len {} < len_to_parse {}", pack_len, len_to_parse);
return ParseResult::RESULT_NOT_ENOUGH;
}
// check CR
const char* id_pos = pack + 9;
const char* CR_pos = id_pos + len_.value_;
if (*CR_pos != CR) {
JLOG_ERRO("AdemcoPacket::Parse ademco_len err!");
break;
}
// check ademco CRC
unsigned short crc_calc = detail::CalculateCRC(id_pos, len_.value_);
if (crc_.value_ != crc_calc) {
JLOG_ERRO("AdemcoPacket::Parse crc failed!");
break;
}
// id
if (*id_pos != '\"') { // find first " of "id".
JLOG_ERRO("AdemcoPacket::Parse find left \" of \"id\" faild!");
break;
}
const char* p = id_pos + 1; // find last " of "id".
while (p < CR_pos && *p != '\"') { p++; } if (p >= CR_pos) { break; }
if (*p++ != '\"') { // " not found.
JLOG_ERRO("AdemcoPacket::Parse find right \" of \"id\" faild!");
break;
}
if (!id_.checkAndSet(id_pos, p - id_pos)) {
JLOG_ERRO("AdemcoPacket::Parse check id faild!");
break;
}
// seq (and Rrcvr, it may not exists)
const char* seq_pos = p;
while (p < CR_pos && *p != 'R' && *p != 'L') { p++; } if (p >= CR_pos) { break; }
if (p - seq_pos != 4) {
JLOG_ERRO("AdemcoPacket::Parse seq len != 4");
break;
}
seq_.fromCharArray(seq_pos);
if (*p == 'R') { // Rrcvr exists
const char* rrcvr_pos = p;
while (p < CR_pos && *p != 'L' && *p != '#') { p++; } if (p >= CR_pos) { break; }
//ASSIGH_SEG_DATA(rrcvr);
rrcvr_.fromCharArray(rrcvr_pos, p - rrcvr_pos);
} else if (*p == 'L') { // Rrcvr not exists, pass
// pass
} else {
JLOG_ERRO("AdemcoPacket::Parse Lpref and Rrcvr not found!");
break;
}
// Lpref
if (*p != 'L') { // L of Lpref not found.
JLOG_ERRO("AdemcoPacket::Parse Lpref not found!");
break;
}
const char* lpref_pos = p;
while (p < CR_pos && *p != '#') { p++; } if (p >= CR_pos) { break; }
lpref_.fromCharArray(lpref_pos, p - lpref_pos);
// acct
if (*p++ != '#') { // # of #acct not found
JLOG_ERRO("AdemcoPacket::Parse # of #acct not found!");
break;
}
const char* acct_pos = p;
while (p < CR_pos && *p != '[') {
if (!isalnum(*p)) { // acct must be alphanumeric
JLOG_ERRO("AdemcoPacket::Parse acct check isalnum failed!");
p = nullptr;
break;
}
p++;
}
if (p == nullptr) { break; }
if (p >= CR_pos) { break; }
acct_.setAcct(acct_pos, p - acct_pos);
// data
if (*p != '[') { // [ of [data] not found.
JLOG_ERRO("AdemcoPacket::Parse [ of [data] not found!");
break;
}
const char* data_pos = p;
while (p < CR_pos && *p != ']') { p++; } if (p >= CR_pos) { break; }
if (*p != ']') { // ] of [data] not found.
JLOG_ERRO("AdemcoPacket::Parse ] of [data] not found!");
break;
}
size_t ademco_data_len = ++p - data_pos;
if (!ademcoData_.parse(data_pos, ademco_data_len)) {
JLOG_ERRO("AdemcoPacket::Parse parse data failed!");
break;
}
// [x...data...]
if (*p == '[') { // xdata exists
xdata_ = parseXData(p, CR_pos);
if (!xdata_) { break; }
p += xdata_->rawSize();
}
if (*p != '_') { // _ of _timestamp not found.
JLOG_ERRO("AdemcoPacket::Parse _ of _timestamp not found!");
break;
}
if (!timestamp_.parse(p, CR_pos - p)) {
JLOG_ERRO("AdemcoPacket::Parse parse timestamp failed!");
break;
}
p += timestamp_.length;
// check CR
if (p++ != CR_pos) { assert(0); break; }
cb_commited = p - pack;
return ParseResult::RESULT_OK;
} while (0);
// dump data
JLOG_HEX(pack, pack_len);
JLOG_ASC(pack, pack_len);
assert(0);
return ParseResult::RESULT_DATA_ERROR;
}
}; // AdemcoPacket
} // namespace ademco
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment