Commit b01215da authored by captainwong's avatar captainwong

防拆索要

parent 85f9f57a
......@@ -22,6 +22,7 @@
#include <algorithm>
#include <chrono>
#include <unordered_map>
#include <map>
#include <event2/listener.h>
#include <event2/buffer.h>
......@@ -57,8 +58,11 @@ static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
#define DISABLE_JLIB_LOG2
#include <ademco_packet.h>
#define ENABLE_COMMON_MACHINE_STATUS_TO_STRING
#define ENABLE_COMMON_MACHINE_TYPE_TO_STRING
#define ENABLE_COMMON_ZONE_PROPERTY_TO_STRING
#include <hb_detail.h>
#include <hb_helper.h>
using namespace ademco;
using namespace hb;
......@@ -83,7 +87,7 @@ enum class QueryZoneStage {
struct ZonePropertyAndLostConfig {
ZoneProperty prop = ZoneProperty::InvalidZoneProperty;
bool enableLostReport = false; // 失联开关
bool tamper_enabled = false; // 失联开关
};
struct Client {
......@@ -93,7 +97,11 @@ struct Client {
size_t ademco_id = 0;
uint16_t seq = 0;
int type = -1;
std::unordered_map<size_t, ZonePropertyAndLostConfig> zones = {};
MachineStatus status = MachineStatus::Arm;
MachineStatus status1 = MachineStatus::Arm;
MachineStatus status2 = MachineStatus::Arm;
MachineStatus status3 = MachineStatus::Arm;
std::map<size_t, ZonePropertyAndLostConfig> zones = {};
QueryZoneStage queryZoneStage = QueryZoneStage::None;
uint16_t nextSeq() { if (++seq == 10000) { seq = 1; } return seq; }
......@@ -167,30 +175,34 @@ void usage(const char* name)
void handle_com_passthrough(ThreadContext* context, Client& client, evbuffer* output)
{
if (!context->packet.xdata_) {
printf("WARNING! 1704 NO XDATA!\n");
printf("\tWARNING! 1704 NO XDATA!\n");
return;
}
// GRPS cannot send us hex data, we need to convert data like "1234ABCDEF" to hex 0x1234ABCDEF
std::vector<char> xdata(context->packet.xdata_->data_.size() / 2);
detail::ConvertHiLoAsciiToHexCharArray(&xdata[0], context->packet.xdata_->data_.data(), context->packet.xdata_->data_.size());
printf("1704 real xdata is %s\n", detail::toString(xdata, detail::ToStringOption::ALL_CHAR_AS_HEX, false, false).data());
printf("\t%s\n", detail::toString(xdata, detail::ToStringOption::ALL_CHAR_AS_HEX, false, false).data());
char buf[1024];
auto resp_type = com::ResponseParser::parse((const Char*)xdata.data(), xdata.size() & 0xFF);
switch (resp_type) {
case com::ResponseParser::ResponseType::A0_response:
break;
case com::ResponseParser::ResponseType::A2_response:
{
char buf[1024];
com::A2R resp; memcpy(resp.data, xdata.data(), xdata.size()); resp.len = xdata.size() & 0xFF;
com::A2R::ZoneAndProperties zps; bool hasMore = false;
if (client.queryZoneStage == QueryZoneStage::QueryingZones && resp.parse(zps, hasMore)) {
for (const auto& zp : zps) {
ZonePropertyAndLostConfig zplc;
zplc.prop = zp.prop;
zplc.enableLostReport = false;
zplc.tamper_enabled = false;
client.zones[zp.zone] = zplc;
snprintf(buf, sizeof(buf), getZoneFormatString(machineTypeFromAdemcoEvent((ADEMCO_EVENT)client.type)), zp.zone);
printf("\t\tZone:");
printf(buf);
printf(" Prop:0x%02X %s\n", zp.prop, zonePropertyToStringEn(zp.prop));
}
XDataPtr xdata;
if (hasMore) { // 继续索要剩余防区
......@@ -237,16 +249,56 @@ void handle_com_passthrough(ThreadContext* context, Client& client, evbuffer* ou
case com::ResponseParser::ResponseType::AB_response:
break;
case com::ResponseParser::ResponseType::AD_response:
break;
{
com::ADR resp; memcpy(resp.data, xdata.data(), xdata.size()); resp.len = xdata.size() & 0xFF;
bool hasMore = false;
std::vector<size_t> zones;
if (client.queryZoneStage == QueryZoneStage::QueryingLostConfig && resp.parse(zones, hasMore)) {
for (const auto& zone : zones) {
auto& z = client.zones[zone];
client.zones[zone].tamper_enabled = true;
snprintf(buf, sizeof(buf), getZoneFormatString(machineTypeFromAdemcoEvent((ADEMCO_EVENT)client.type)), zone);
printf("\t\tZone:");
printf(buf);
printf(" Tamper Enabled: true\n");
}
XDataPtr xdata;
if (hasMore) { // 继续索要剩余防区
com::A2 req;
xdata = makeXData((const char*)req.data, req.len);
auto n = context->packet.make_hb(buf, sizeof(buf), client.nextSeq(), client.acct, client.ademco_id, 0,
EVENT_COM_PASSTHROUGH, 0, xdata);
evbuffer_add(output, buf, n);
if (!disable_data_print) {
printf("T#%d S#%d acct=%s ademco_id=%06zX :%s\n",
context->worker_id, client.fd, client.acct.data(), client.ademco_id,
context->packet.toString(detail::ToStringOption::ALL_CHAR_AS_HEX).data());
}
break;
}
}
auto n = context->packet.make_hb(buf, sizeof(buf), client.nextSeq(), client.acct, client.ademco_id, 0,
EVENT_EXIT_SET_MODE, 0);
evbuffer_add(output, buf, n);
if (!disable_data_print) {
printf("T#%d S#%d acct=%s ademco_id=%06zX :%s\n",
context->worker_id, client.fd, client.acct.data(), client.ademco_id,
context->packet.toString(detail::ToStringOption::ALL_CHAR_AS_HEX).data());
}
break;
}
case com::ResponseParser::ResponseType::AE_response:
break;
case com::ResponseParser::ResponseType::B1_response:
{
com::B1R b1; memcpy(b1.data.data, xdata.data(), xdata.size());
printf("\t\t status1:%s status2:%s status3:%s\n",
b1.data.cmd.status1 == 0 ? "Arm" : "Disarm",
b1.data.cmd.status2 == 0 ? "Arm" : "Disarm",
b1.data.cmd.status3 == 0 ? "Arm" : "Disarm");
client.status1 = b1.data.cmd.status1 == 0 ? MachineStatus::Arm : MachineStatus::Disarm;
client.status2 = b1.data.cmd.status2 == 0 ? MachineStatus::Arm : MachineStatus::Disarm;
client.status3 = b1.data.cmd.status3 == 0 ? MachineStatus::Arm : MachineStatus::Disarm;
printf("\t\t status1: %d %s\n\t\t status2: %d %s\n\t\t status3: %d %s\n",
client.status1, machineStatusToString(client.status1),
client.status2, machineStatusToString(client.status2),
client.status3, machineStatusToString(client.status3));
break;
}
case com::ResponseParser::ResponseType::Invalid_response:
......@@ -290,6 +342,13 @@ void handle_ademco_msg(ThreadContext* context, bufferevent* bev)
}
break;
}
} else if (ademco::isMachineStatusEvent(context->packet.ademcoData_.ademco_event_)) {
auto status = machienStatusFromAdemcoEvent(context->packet.ademcoData_.ademco_event_);
if (context->packet.ademcoData_.gg_ == 0) { client.status = status; }
else if (context->packet.ademcoData_.gg_ == 1) { client.status1 = status; }
else if (context->packet.ademcoData_.gg_ == 2) { client.status2 = status; }
else if (context->packet.ademcoData_.gg_ == 3) { client.status3 = status; }
else { break; }
} else if (context->packet.ademcoData_.ademco_event_ == EVENT_COM_PASSTHROUGH) {
handle_com_passthrough(context, client, output);
break;
......@@ -795,8 +854,31 @@ int main(int argc, char** argv)
}
}
printf("Total connnected %zd clients:\n", copiedClients.size());
for (const auto& client : copiedClients) {
printf(" fd=#%d acct=%s ademco_id=%zd\n", client.fd, client.acct.data(), client.ademco_id);
if (client.type == EVENT_I_AM_3_SECTION_MACHINE) {
printf(" fd=#%d acct=%s ademco_id=%zd type=%s\n",
client.fd, client.acct.data(), client.ademco_id,
machineTypeToString(machineTypeFromAdemcoEvent((ADEMCO_EVENT)client.type)));
printf(" status1: %d %s status2: %d %s status3: %d %s\n",
client.status1, machineStatusToString(client.status1),
client.status2, machineStatusToString(client.status2),
client.status3, machineStatusToString(client.status3));
} else {
printf(" fd=#%d acct=%s ademco_id=%zd type=%s status=%d %s\n",
client.fd, client.acct.data(), client.ademco_id,
machineTypeToString(machineTypeFromAdemcoEvent((ADEMCO_EVENT)client.type)),
client.status, machineStatusToString(client.status));
}
for (const auto& zp : client.zones) {
char buf[512];
snprintf(buf, sizeof(buf), getZoneFormatString(machineTypeFromAdemcoEvent((ADEMCO_EVENT)client.type)), zp.first);
printf(" Zone:");
printf(buf);
printf(" Prop:0x%02X %s \tTamper Enabled:%s\n",
zp.second.prop, zonePropertyToStringEn(zp.second.prop),
zp.second.tamper_enabled ? "true" : "false");
}
}
}
break;
......
......@@ -344,7 +344,7 @@ inline const std::wstring ademcoEventToStringChinese(ADEMCO_EVENT ademco_event,
#endif
//! 是否主机状态事件
static inline bool isStatusEvent(ADEMCO_EVENT ademco_event)
static inline bool isMachineStatusEvent(ADEMCO_EVENT ademco_event)
{
return ademco_event == EVENT_ARM
|| ademco_event == EVENT_HALFARM
......
......@@ -275,7 +275,7 @@ struct CongwinFe100Packet
data_[ndx++] = ' ';
// 'C' for zone, 'U' for user
if (isStatusEvent(evnt)) {
if (isMachineStatusEvent(evnt)) {
data_[ndx++] = 'U'; // U
} else {
data_[ndx++] = 'C'; // C
......
......@@ -286,7 +286,7 @@ static const wchar_t* machineTypeToWString(MachineType type) {
//! 防区属性
enum ZoneProperty : Char {
//! 匪警全局
BuglarGlobal = 0x00,
Buglar = 0x00,
//! 匪警紧急
Emergency = 0x01,
//! 火警防区
......@@ -325,27 +325,27 @@ enum ZoneProperty : Char {
};
static ZoneProperty zonePropertyFromChar(Char zp) {
if (ZoneProperty::BuglarGlobal <= zp && zp < ZoneProperty::ZonePropertyCount)
if (ZoneProperty::Buglar <= zp && zp < ZoneProperty::ZonePropertyCount)
{ return static_cast<ZoneProperty>(zp); }
return ZoneProperty::InvalidZoneProperty;
}
static std::vector<ZoneProperty> getAvailableZoneProperties(MachineType type) {
switch (type) {
case NetMod: return { BuglarGlobal, Emergency, Fire, Duress, Gas, Water, SubMachine, RemoteControl, BuglarHalf, Shield, DoorRing };
case Gprs: return { BuglarGlobal, Emergency, Fire, Duress, Gas, Water, };
case Lcd: return { BuglarGlobal, Emergency, Fire, Duress, Gas, Water, SubMachine, RemoteControl, BuglarHalf, Shield, DoorRing };
case Wired: return { BuglarGlobal, Emergency, Fire, Duress, Gas, Water, };
case TrueColor: return { BuglarGlobal, Emergency, Fire, Duress, Gas, Water, RemoteControl, Shield, DoorRing, Bypass };
case ThreeSection:return { BuglarGlobal, Emergency, Fire, Duress, Gas, Water, RemoteControl, Shield, DoorRing, Bypass };
case NetMod: return { Buglar, Emergency, Fire, Duress, Gas, Water, SubMachine, RemoteControl, BuglarHalf, Shield, DoorRing };
case Gprs: return { Buglar, Emergency, Fire, Duress, Gas, Water, };
case Lcd: return { Buglar, Emergency, Fire, Duress, Gas, Water, SubMachine, RemoteControl, BuglarHalf, Shield, DoorRing };
case Wired: return { Buglar, Emergency, Fire, Duress, Gas, Water, };
case TrueColor: return { Buglar, Emergency, Fire, Duress, Gas, Water, RemoteControl, Shield, DoorRing, Bypass };
case ThreeSection:return { Buglar, Emergency, Fire, Duress, Gas, Water, RemoteControl, Shield, DoorRing, Bypass };
default: return {};
}
}
#ifdef ENABLE_COMMON_ZONE_PROPERTY_TO_STRING
static const wchar_t* zonePropertyToString(ZoneProperty zp) {
static const wchar_t* zonePropertyToStringChinese(ZoneProperty zp) {
switch (zp) {
case ZoneProperty::BuglarGlobal: return L"匪警全局";
case ZoneProperty::Buglar: return L"匪警全局";
case ZoneProperty::Emergency: return L"匪警紧急";
case ZoneProperty::Fire: return L"火警防区";
case ZoneProperty::Duress: return L"胁迫防区";
......@@ -365,6 +365,28 @@ static const wchar_t* zonePropertyToString(ZoneProperty zp) {
default: return L"无效属性";
}
}
static const char* zonePropertyToStringEn(ZoneProperty zp) {
switch (zp) {
case ZoneProperty::Buglar: return "Buglar";
case ZoneProperty::Emergency: return "Emergency";
case ZoneProperty::Fire: return "Fire";
case ZoneProperty::Duress: return "Duress";
case ZoneProperty::Gas: return "Gas";
case ZoneProperty::Water: return "Water";
case ZoneProperty::SubMachine: return "SubMachine";
case ZoneProperty::RemoteControl: return "RemoteControl";
case ZoneProperty::BuglarHalf: return "BuglarHalf";
case ZoneProperty::Shield: return "Shield";
case ZoneProperty::DoorRing: return "DoorRing";
case ZoneProperty::Reserved0B: return "Reserved0B";
case ZoneProperty::Reserved0C: return "Reserved0C";
case ZoneProperty::Reserved0D: return "Reserved0D";
case ZoneProperty::Reserved0E: return "Reserved0E";
case ZoneProperty::Bypass: return "Bypass";
case ZoneProperty::InvalidZoneProperty:
default: return "InvalidZoneProperty";
}
}
#endif // ENABLE_COMMON_ZONE_PROPERTY_TO_STRING
......@@ -428,7 +450,7 @@ struct MachineStatusResponse3Section {
Char head[6];
// 0 撤防,1 布防
Char status3 : 2;
Char status2 : 2;
Char status2 : 2;
Char status1 : 2;
Char status : 2;
Char sum;
......@@ -445,6 +467,16 @@ struct MachineStatusResponse3Section {
&& data.cmd.head[5] == 0xB1
&& sum(data.data, len) == s;
}
void make(MachineStatus status1, MachineStatus status2, MachineStatus status3, MachineStatus status = MachineStatus::Arm) {
Char head[6] = { 0xEB, 0xBA, 0x3F, 0x08, 0xCC, 0xB1 };
memcpy(data.cmd.head, head, 6);
data.cmd.status = (Char)status & 0x3;
data.cmd.status1 = (Char)status1 & 0x3;
data.cmd.status2 = (Char)status2 & 0x3;
data.cmd.status3 = (Char)status3 & 0x3;
sum(data.data, len);
}
};
static_assert(sizeof(MachineStatusResponse3Section) == 8, "sizeof(MachineStatusResponse3Section) must be 8");
typedef MachineStatusResponse3Section B1R;
......@@ -478,22 +510,20 @@ typedef ZoneRequest A1;
//! 回应主机防区 EB BA 3F PN P0 A2 [Z, P]xN P1 SUM
struct ZoneResponse {
//! 一包数据最多有20个字节,所以最多可以包含 (20 - 8) / 2 = 6 个防区
static constexpr Char max_zone = 6;
static constexpr Char max_len = 8 + 2 * max_zone;
static constexpr Char min_len = 8; // 无防区防拆数据时长度最小为8
static constexpr Char max_len = 20; // 一包数据最多有20个字节
static constexpr Char max_zone = (max_len - min_len) / 2; // 最多可以包含 (20 - 8) / 2 = 6 个防区
/*
* when param is not 0xFF, means there's more zone coming; vice versa
* zone&prop can be placed as much as 20 times
* 0 1 2 3.len 4 5 6.zone 7.prop 8.param 9.sum
*/
Char data[max_len] = { 0xEB, 0xBA, 0x3F, 0x0A, 0xCC, 0xA2, 0x00, 0x00, 0xFF, 0x00 };
Char len = 10; // init as minimum len
Char data[max_len] = {};
Char len = min_len; // init as minimum len
// default ctor, no zone exists
ZoneResponse() {
len = 8;
Char nodata[8] = { 0xEB, 0xBA, 0x3F, 0x08, 0xCC, 0xA2, 0xFF, 0x00 };
memcpy(data, nodata, 8);
Char nodata[min_len] = { 0xEB, 0xBA, 0x3F, 0x08, 0xCC, 0xA2, 0xFF, 0x00 }; sum(nodata, min_len);
memcpy(data, nodata, min_len); len = min_len;
}
/*
......@@ -521,9 +551,9 @@ struct ZoneResponse {
//! parser, make sure to call ResponseParser::parse first and return is A2_response to check data valid, then copy data/len to member.
bool parse(ZoneAndProperties& zps, bool& hasMore) {
if (len < 8 || data[3] != len) { return false; } // check valid again
if (len < min_len || data[3] != len) { return false; } // check valid again
Char sum_ = data[len - 1]; sum(data, len); if (sum_ != data[len - 1]) { return false; } // check sum again
Char count = len - 8 / 2; // zone/prop pairs
Char count = (len - min_len) / 2; // zone/prop pairs
if (count == 0) { zps.clear(); hasMore = false; return true; }
for (Char i = 0; i < count; i++) {
ZoneAndProperty zp;
......@@ -675,25 +705,55 @@ typedef QueryMoreSensorLostSettingsRequest AD;
//! 索要防区探头遗失/失联回应 EB BA 3F PN P0 AD P1 DATA P2 SUM
struct QuerySensorLostSettingsResponse {
CharVector data{};
const Char p1 = 0xF0; // F0 防区号单字节表示,F1防区号双字节表示
static constexpr Char P1FlagZoneAs1Char = 0xF0;
static constexpr Char P1FlagZoneAs2Chars = 0xF1;
static constexpr Char min_len = 9; // 无防区防拆数据时长度最小为9
static constexpr Char max_len = 20;
QuerySensorLostSettingsResponse(Char p1 = 0xF0) :p1(p1) {
data = { 0xEB, 0xBA, 0x3F, 0, 0xAD, p1 };
Char data[max_len] = {};
Char len = 9; // init as minimum len
const Char p1 = P1FlagZoneAs1Char; // F0 防区号单字节表示,F1防区号双字节表示
// default ctor, no zone exists
QuerySensorLostSettingsResponse(Char p1 = P1FlagZoneAs1Char) :p1(p1) {
Char nodata[min_len] = { 0xEB, 0xBA, 0x3F, 0x09, 0x00, 0xAD, p1, 0xFF, 0x00 };
sum(nodata, min_len); memcpy(data, nodata, min_len); len = min_len;
}
void addZone(size_t zone, bool isOver = true, bool hasMore = false) {
data[3]++;
if (p1 == 0xF0) { data.push_back(zone & 0xFF); }
else {
data.push_back((zone >> 8) & 0xFF);
data.push_back(zone & 0xFF);
Char maxZone() const { Char res = (max_len - min_len); if (p1 == P1FlagZoneAs2Chars) { res /= 2; } return res; }
QuerySensorLostSettingsResponse& addZone(size_t zone, bool isOver = true, bool hasMore = false) {
if (len == max_len) { return *this; }
Char pos = static_cast<Char>(len - 2);
if (p1 == P1FlagZoneAs1Char) {
data[pos] = zone & 0xFF;
len = static_cast<Char>(len + 1); data[3] = len;
} else {
data[pos] = (zone >> 8) & 0xFF; data[pos + 1] = zone & 0xFF;
len = static_cast<Char>(len + 2); data[3] = len;
}
if (isOver) {
data.push_back(hasMore ? 0x00 : 0xFF);
data.push_back(0); // sum
sum(data);
data[len - 2] = hasMore ? 0x00 : 0xFF;
if (isOver) { sum(this); }
return *this;
}
//! parser, make sure to call ResponseParser::parse first and return is ADR_response to check data valid, then copy data/len to member.
bool parse(std::vector<size_t>& zones, bool& hasMore) {
if (len < min_len || data[3] != len) { return false; } // check valid again
Char sum_ = data[len - 1]; sum(data, len); if (sum_ != data[len - 1]) { return false; } // check sum again
Char flag = data[6]; if (flag != P1FlagZoneAs1Char && flag != P1FlagZoneAs2Chars) { return false; } // check param
Char count = (len - min_len); if (flag == P1FlagZoneAs2Chars) { count /= 2; }
if (count == 0) { zones.clear(); hasMore = false; return true; }
for (Char i = 0; i < count; i++) {
size_t zone = data[7 + i];
if (p1 == P1FlagZoneAs2Chars) {
zone <<= 8;
zone |= data[8 + i];
}
zones.push_back(zone);
}
hasMore = data[len - 2] != 0xFF;
return true;
}
};
typedef QuerySensorLostSettingsResponse ADR;
......@@ -839,7 +899,7 @@ struct RequestParser {
}*/
// EB CB 3F 06 B0 AB
if (data[3] == 0x08 && data[4] == 0xB0 && len == B0::len && memcmp(B0::data, data, len) == 0) {
if (data[3] == 0x06 && data[4] == 0xB0 && len == B0::len && memcmp(B0::data, data, len) == 0) {
return RequestType::B0_request;
}
}
......@@ -944,14 +1004,8 @@ struct ResponseParser {
case 0xAD: // EB BA 3F PN P0 AD P1 DATA P2 SUM
{
//Char lenExpected = 0;
//if (data[6] == 0xF0) { // 防区号1个字节
// lenExpected = 6 + data[3] + 2;
//} else if (data[6] == 0xF1) { // 防区号2个字节
// lenExpected = 6 + data[3] * 2 + 2;
//} else { break; }
if (len != data[3]) { break; }
ADR resp; std::copy(data, data + len, std::back_inserter(resp.data)); sum(resp.data.data(), (Char)resp.data.size());
ADR resp; memcpy(resp.data, data, len); sum(resp); sum(resp);
if (resp.data[len - 1] != data[len - 1]) { break; }
return ResponseType::AD_response;
}
......
......@@ -76,7 +76,7 @@ static ademco::ADEMCO_EVENT ademcoEventFromMachineStatus(common::MachineStatus s
static ademco::ADEMCO_EVENT ademcoEventFromZoneProperty(common::ZoneProperty zp)
{
switch (zp) {
case hb::common::BuglarGlobal: return ademco::EVENT_BURGLAR;
case hb::common::Buglar: return ademco::EVENT_BURGLAR;
case hb::common::BuglarHalf: return ademco::EVENT_BURGLAR;
case hb::common::Emergency: return ademco::EVENT_EMERGENCY;
case hb::common::Fire: return ademco::EVENT_FIRE;
......@@ -104,7 +104,7 @@ static ademco::ADEMCO_EVENT ademcoEventFromZonePropertyOld(old::ZoneProperty zp)
{
switch (zp) {
case hb::old::ZP_GLOBAL: return ademco::EVENT_BURGLAR;
case hb::old::ZP_HALF: return ademco::EVENT_HALFARM;
case hb::old::ZP_HALF: return ademco::EVENT_BURGLAR;
case hb::old::ZP_EMERGENCY: return ademco::EVENT_EMERGENCY;
case hb::old::ZP_DOORRING: return ademco::EVENT_DOORRINGING;
case hb::old::ZP_FIRE: return ademco::EVENT_FIRE;
......
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