443 lines
14 KiB
C
443 lines
14 KiB
C
/*
|
|
* Copyright (c) CompanyNameMagicTag 2019-2020. All rights reserved.
|
|
* Description: MRS CCO EVENT report.
|
|
*/
|
|
|
|
#include "mrs_cco_evt.h"
|
|
#include "mrs_cco_srv.h"
|
|
#include "mrs_cco_queue.h"
|
|
#include "mrs_common_tools.h"
|
|
#include "mrs_common_power_failure.h"
|
|
#include "mrs_cco_power_failure.h"
|
|
#include "mrs_cco_archives.h"
|
|
|
|
#if defined(PRODUCT_CFG_PRODUCT_TYPE_CCO)
|
|
/* Events filters */
|
|
td_bool mrs_cco_evt_allow_enqueue(const td_u8 *meter, td_u16 seq);
|
|
td_bool mrs_cco_evt_filter_reported(const td_u8 *meter, td_u16 seq);
|
|
td_bool mrs_cco_evt_filter_repeat(const td_u8 *meter, td_u16 seq);
|
|
/* Send ack PLC frame */
|
|
td_void mrs_cco_evt_plc_ack(const td_u8 *meter, td_u16 seq, td_u8 func_code, const td_u8 *mac);
|
|
/* Remove aged events */
|
|
td_void mrs_cco_evt_reported_aged(mrs_cco_evt_reported_item *item);
|
|
/* Create 376.2 event report frame */
|
|
td_u32 mrs_cco_evt_create_1376_2_frame(mrs_meter_event_info *frame, td_pbyte *buffer, td_u16 *length);
|
|
/* Remove events from event queue */
|
|
td_void mrs_cco_evt_report_end(td_pvoid param);
|
|
/* Event report timeout */
|
|
td_u32 mrs_cco_evt_report_timeout(td_pvoid param);
|
|
/* Add event to reported list */
|
|
td_void mrs_cco_evt_reported_add_list(td_void);
|
|
/* Replace events when list is full */
|
|
td_void mrs_cco_evt_replace_reported_item(mrs_cco_evt_reported_item *report_item, const mrs_cco_evt_item *item);
|
|
|
|
mrs_cco_evt_ctx g_cco_evt_ctx;
|
|
mrs_cco_evt_ctx *mrs_cco_get_evt_ctx(td_void)
|
|
{
|
|
return &g_cco_evt_ctx;
|
|
}
|
|
|
|
td_void mrs_cco_event_init(td_void)
|
|
{
|
|
(td_void) memset_s(&g_cco_evt_ctx, sizeof(g_cco_evt_ctx), 0, sizeof(g_cco_evt_ctx));
|
|
mrs_queue_init(&g_cco_evt_ctx.evt_q);
|
|
uapi_list_init(&g_cco_evt_ctx.evt_reported_list);
|
|
}
|
|
|
|
td_void mrs_cco_rx_evt_plc_power_down_proc(const mrs_plc_frame_data *plc)
|
|
{
|
|
mrs_meter_event_info *evt_frm = (mrs_meter_event_info *)plc->payload;
|
|
/* power down */
|
|
mrs_power_failure_ctrl_st *power_failure_info = mrs_get_power_failure_ctrl();
|
|
td_u8 mac[EXT_PLC_MAC_ADDR_LEN] = { 0 };
|
|
if (memcpy_s(mac, EXT_PLC_MAC_ADDR_LEN, plc->addr, EXT_PLC_MAC_ADDR_LEN) != EXT_ERR_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
/* check validity of data */
|
|
if ((evt_frm->stru_size + evt_frm->data_len != plc->payload_len) || (evt_frm->data_len <= DATA_FIX_SIZE)) {
|
|
return;
|
|
}
|
|
|
|
if (plc->form_serial_number != uapi_mdm_get_form_serial_num()) {
|
|
return;
|
|
}
|
|
mrs_power_failure_get_others_frame_data(evt_frm);
|
|
|
|
/* if need send reply frame */
|
|
if (mrs_cco_not_reply_ack(mac) != TD_TRUE) {
|
|
/* CCO create reply frame */
|
|
mrs_cco_evt_plc_ack(evt_frm->meter, evt_frm->seq, MRS_EVNET_FUNC_ACK, plc->addr);
|
|
}
|
|
|
|
/* report event to concentrator */
|
|
if (power_failure_info->is_ready_to_send_power_failure == TD_FALSE &&
|
|
evt_frm->data[0] == POWER_FAILURE) {
|
|
mrs_printf("ENTER mrs_cco_evt_report().\n");
|
|
mrs_cco_evt_report();
|
|
}
|
|
|
|
return;
|
|
}
|
|
td_u32 mrs_cco_rx_evt_plc_check(const mrs_plc_frame_data *plc)
|
|
{
|
|
mrs_meter_event_info *evt_frm = (mrs_meter_event_info *)plc->payload;
|
|
td_u32 ret = EXT_ERR_FAILURE;
|
|
/* check frame */
|
|
if ((evt_frm->dir != MRS_PLC_UP_FLG) || (evt_frm->stru_size < sizeof(mrs_meter_event_info)) ||
|
|
(plc->payload_len != evt_frm->stru_size + evt_frm->data_len)) {
|
|
return ret;
|
|
}
|
|
|
|
if (evt_frm->func_code != MRS_EVENT_FUNC_METER_REPORT) {
|
|
return ret; /* Not support */
|
|
}
|
|
return EXT_ERR_SUCCESS;
|
|
}
|
|
|
|
td_u32 mrs_cco_rx_evt_plc_fill_ack_data(const mrs_plc_frame_data *plc, td_pbyte payload, td_u16 payload_len,
|
|
const td_pbyte buffer, mrs_cco_evt_item **item)
|
|
{
|
|
mrs_meter_event_info *evt_frm = (mrs_meter_event_info *)plc->payload;
|
|
td_u16 length = payload_len - sizeof(mrs_cco_evt_item);
|
|
mrs_cco_evt_item *evt_item = TD_NULL;
|
|
if (payload == TD_NULL) {
|
|
return EXT_ERR_FAILURE;
|
|
}
|
|
|
|
(td_void) memset_s(payload, payload_len, 0, payload_len);
|
|
evt_item = (mrs_cco_evt_item *)payload;
|
|
(td_void) memcpy_s(evt_item->meter, sizeof(evt_item->meter), evt_frm->meter, sizeof(evt_frm->meter));
|
|
evt_item->seq = evt_frm->seq;
|
|
evt_item->data_len = length;
|
|
evt_item->fn = 255; /* fn 255 */
|
|
if (memcpy_s(evt_item->data, evt_item->data_len, buffer, length) != EOK) {
|
|
return EXT_ERR_FAILURE;
|
|
}
|
|
|
|
*item = evt_item;
|
|
return EXT_ERR_SUCCESS;
|
|
}
|
|
|
|
td_void mrs_cco_evt_plc_frame_rx(mrs_plc_frame_data *plc)
|
|
{
|
|
mrs_meter_event_info *evt_frm = (mrs_meter_event_info *)plc->payload;
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
td_u8 func_code = MRS_EVNET_FUNC_ACK;
|
|
/* power down */
|
|
mrs_power_failure_ctrl_st *power_failure_info = mrs_get_power_failure_ctrl();
|
|
|
|
/* power down processing */
|
|
if (evt_frm->func_code == MRS_EVENT_FUNC_MODUL_REPORT && evt_frm->dir == MRS_PLC_UP_FLG &&
|
|
power_failure_info->power_failure_report_enable == MRS_CCO_DETECT_POWER_FAILURE_ENABLE) {
|
|
mrs_cco_rx_evt_plc_power_down_proc(plc);
|
|
return;
|
|
} else if (mrs_cco_rx_evt_plc_check(plc) != EXT_ERR_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
td_pbyte buffer = TD_NULL;
|
|
td_u16 length = 0;
|
|
td_u16 payload_len;
|
|
td_pbyte payload = TD_NULL;
|
|
mrs_cco_evt_item *item = TD_NULL;
|
|
td_u32 ret;
|
|
|
|
/* already in queue. send reply frame directly */
|
|
if (mrs_cco_evt_allow_enqueue(evt_frm->meter, evt_frm->seq) != TD_TRUE) {
|
|
break;
|
|
}
|
|
|
|
if (evt->evt_q_num >= MRS_CCO_EVT_ITEM_MAX) {
|
|
func_code = MRS_EVENT_FUNC_CCO_BUF_FULL;
|
|
break;
|
|
}
|
|
|
|
ret = mrs_cco_evt_create_1376_2_frame(evt_frm, &buffer, &length);
|
|
if (ret != EXT_ERR_SUCCESS) {
|
|
func_code = MRS_EVENT_FUNC_CCO_BUF_FULL;
|
|
break;
|
|
}
|
|
|
|
payload_len = sizeof(mrs_cco_evt_item) + length;
|
|
payload = (td_pbyte)mrs_malloc(payload_len);
|
|
ret = mrs_cco_rx_evt_plc_fill_ack_data(plc, payload, payload_len, buffer, &item);
|
|
if (ret != EXT_ERR_SUCCESS) {
|
|
mrs_free(payload);
|
|
mrs_free(buffer);
|
|
return;
|
|
}
|
|
|
|
mrs_queue_enqueue(&evt->evt_q, &item->link);
|
|
evt->evt_q_num++;
|
|
evt->acd_flg = TD_TRUE;
|
|
mrs_cco_evt_report_active();
|
|
|
|
mrs_free(buffer);
|
|
} while (0);
|
|
|
|
mrs_cco_evt_plc_ack(evt_frm->meter, evt_frm->seq, func_code, plc->addr);
|
|
}
|
|
|
|
td_bool mrs_cco_evt_allow_enqueue(const td_u8 *meter, td_u16 seq)
|
|
{
|
|
/* if already in reported queue, not allow join the queue */
|
|
if (mrs_cco_evt_filter_reported(meter, seq) == TD_TRUE) {
|
|
return TD_FALSE;
|
|
}
|
|
|
|
/* if already in queue of waiting to be reported, not allow join the queue */
|
|
if (mrs_cco_evt_filter_repeat(meter, seq) == TD_TRUE) {
|
|
return TD_FALSE;
|
|
}
|
|
|
|
return TD_TRUE;
|
|
}
|
|
|
|
td_bool mrs_cco_evt_filter_reported(const td_u8 *meter, td_u16 seq)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
ext_list *list = &evt->evt_reported_list;
|
|
ext_list *it = list->next;
|
|
|
|
/* find the same meter, and age the reported list. */
|
|
while (it != list) {
|
|
mrs_cco_evt_reported_item *item = (mrs_cco_evt_reported_item *)it;
|
|
it = it->next;
|
|
|
|
if ((memcmp(meter, item->meter, sizeof(item->meter)) == 0) && (seq == item->seq)) {
|
|
mrs_cco_evt_reported_aged(item);
|
|
return TD_TRUE;
|
|
}
|
|
|
|
mrs_cco_evt_reported_aged(item);
|
|
}
|
|
|
|
return TD_FALSE;
|
|
}
|
|
|
|
td_void mrs_cco_evt_reported_aged(mrs_cco_evt_reported_item *item)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
td_u32 cur_time = uapi_get_seconds();
|
|
if (cur_time >= item->time + MRS_CCO_EVT_REPORTED_AGED) {
|
|
uapi_list_delete(&item->link);
|
|
mrs_free(item);
|
|
evt->report_list_num--;
|
|
}
|
|
}
|
|
|
|
td_bool mrs_cco_evt_filter_repeat(const td_u8 *meter, td_u16 seq)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
mrs_srv_queue *q = (mrs_srv_queue *)&evt->evt_q;
|
|
mrs_queue_node *it;
|
|
|
|
for (it = q->head; it != TD_NULL; it = it->next) {
|
|
mrs_cco_evt_item *item = (mrs_cco_evt_item *)it;
|
|
if ((memcmp(meter, item->meter, sizeof(item->meter)) == 0) && (seq == item->seq)) {
|
|
return TD_TRUE;
|
|
}
|
|
}
|
|
|
|
return TD_FALSE;
|
|
}
|
|
|
|
td_void mrs_cco_evt_plc_ack(const td_u8 *meter, td_u16 seq, td_u8 func_code, const td_u8 *mac)
|
|
{
|
|
mrs_plc_frame_data plc;
|
|
mrs_meter_event_info evt_frm;
|
|
|
|
(td_void) memset_s(&evt_frm, sizeof(evt_frm), 0, sizeof(evt_frm));
|
|
evt_frm.stru_ver = MRS_PLC_PROTO_VERSION;
|
|
evt_frm.stru_size = sizeof(evt_frm);
|
|
evt_frm.dir = MRS_PLC_DN_FLG;
|
|
evt_frm.prm = MRS_EVENT_PRM_SLAVE;
|
|
evt_frm.func_code = func_code;
|
|
evt_frm.seq = seq;
|
|
evt_frm.data_len = 0;
|
|
|
|
if (memcpy_s(evt_frm.meter, sizeof(evt_frm.meter), meter, MRS_METER_ADDR_LEN) != 0) {
|
|
return;
|
|
}
|
|
|
|
(td_void) memset_s(&plc, sizeof(plc), 0, sizeof(plc));
|
|
plc.id = PLC_CMD_ID_EVT;
|
|
plc.payload_len = sizeof(evt_frm);
|
|
plc.payload = (td_pbyte)&evt_frm;
|
|
if (memcpy_s(plc.addr, sizeof(plc.addr), mac, EXT_PLC_MAC_ADDR_LEN) != EOK) {
|
|
return;
|
|
}
|
|
|
|
mrs_plc_frame_send(&plc);
|
|
}
|
|
|
|
td_u32 mrs_cco_evt_create_1376_2_frame(mrs_meter_event_info *frame, td_pbyte *buffer, td_u16 *length)
|
|
{
|
|
/* device type 1B + protocol type 1B + frame length 2B + frame->data_len */
|
|
td_u16 payload_len = (td_u8)frame->data_len + 4; /* 4: 1+1+2 */
|
|
td_pbyte payload = (td_pbyte)mrs_malloc(payload_len);
|
|
mrs_proto_1376_2_encode encode;
|
|
mrs_cco_srv_ctx *cco = mrs_cco_get_srv_ctx();
|
|
td_u32 ret;
|
|
td_u32 data_len;
|
|
|
|
if (payload == TD_NULL) {
|
|
return EXT_ERR_MALLOC_FAILUE;
|
|
}
|
|
|
|
(td_void) memset_s(&encode, sizeof(encode), 0, sizeof(encode));
|
|
encode.data = payload;
|
|
encode.length = payload_len;
|
|
encode.prm = 1;
|
|
encode.seq = ++cco->seq;
|
|
encode.afn = 6; /* afn 6 */
|
|
encode.fn = 255; /* fn 255 */
|
|
|
|
*payload++ = 0x01; /* electric meter */
|
|
*payload++ = mrs_archives_get_meter_protocol(frame->meter);
|
|
*payload++ = (td_u8)(frame->data_len & 0xFF);
|
|
*payload++ = (td_u8)((frame->data_len >> 8) & 0xFF); /* low 8bits */
|
|
data_len = payload_len - 4; /* data length: 4B + data length */
|
|
if (data_len > 0) {
|
|
if (memcpy_s(payload, data_len, (td_pbyte)frame + frame->stru_size, (td_u8)frame->data_len) != EOK) {
|
|
mrs_free(encode.data);
|
|
return EXT_ERR_MEMCPY_FAIL;
|
|
}
|
|
}
|
|
|
|
ret = mrs_proto_1376_2_create_frame(&encode, buffer, length);
|
|
mrs_free(encode.data);
|
|
return ret;
|
|
}
|
|
|
|
td_void mrs_msg_on_evt_notify(EXT_CONST mrs_queue_msg *msg)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
mrs_cco_evt_item *item = TD_NULL;
|
|
mrs_cco_queue_item *cco_item = TD_NULL;
|
|
|
|
ext_unref_param(msg);
|
|
|
|
if (mrs_try_lock(evt->evt_q_lock) == 0) {
|
|
return;
|
|
}
|
|
|
|
item = (mrs_cco_evt_item *)mrs_queue_top(&evt->evt_q);
|
|
if (item == TD_NULL) {
|
|
mrs_unlock(evt->evt_q_lock);
|
|
return;
|
|
}
|
|
|
|
cco_item = (mrs_cco_queue_item *)mrs_malloc(sizeof(mrs_cco_queue_item) + item->data_len);
|
|
if (cco_item == TD_NULL) {
|
|
mrs_cco_evt_report_end(TD_NULL);
|
|
return;
|
|
}
|
|
|
|
(td_void) memset_s(cco_item, sizeof(mrs_cco_queue_item) + item->data_len, 0,
|
|
sizeof(mrs_cco_queue_item) + item->data_len);
|
|
cco_item->valid = TD_TRUE;
|
|
cco_item->afn = 0x06;
|
|
cco_item->fn = item->fn;
|
|
cco_item->retry_max = MRS_CCO_EVT_RETRY_MAX;
|
|
cco_item->timeout = MRS_CCO_EVT_REPORT_TIMEOUT;
|
|
cco_item->data_len = item->data_len;
|
|
|
|
if (memcpy_s(cco_item->data, cco_item->data_len, item->data, item->data_len) != EOK) {}
|
|
|
|
cco_item->rx_handle = mrs_cco_evt_report_end;
|
|
cco_item->total_timeout = mrs_cco_evt_report_timeout;
|
|
mrs_cco_queue_enqueue(cco_item);
|
|
mrs_cco_queue_active();
|
|
}
|
|
|
|
td_void mrs_cco_evt_report_end(td_pvoid param)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
mrs_cco_evt_item *item = TD_NULL;
|
|
|
|
ext_unref_param(param);
|
|
|
|
mrs_cco_evt_reported_add_list();
|
|
|
|
item = (mrs_cco_evt_item *)mrs_queue_dequeue(&evt->evt_q);
|
|
mrs_free(item);
|
|
|
|
mrs_unlock(evt->evt_q_lock);
|
|
evt->evt_q_num--;
|
|
|
|
if (evt->evt_q_num == 0) {
|
|
evt->acd_flg = TD_FALSE;
|
|
return;
|
|
}
|
|
mrs_cco_evt_report_active();
|
|
}
|
|
|
|
td_u32 mrs_cco_evt_report_timeout(td_pvoid param)
|
|
{
|
|
mrs_cco_evt_report_end(param);
|
|
return EXT_ERR_SUCCESS;
|
|
}
|
|
|
|
td_void mrs_cco_evt_reported_add_list(td_void)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
mrs_cco_evt_item *item = TD_NULL;
|
|
ext_list *list = TD_NULL;
|
|
ext_list *it = TD_NULL;
|
|
ext_list *next = TD_NULL;
|
|
mrs_cco_evt_reported_item *temp = TD_NULL;
|
|
|
|
item = (mrs_cco_evt_item *)mrs_queue_top(&evt->evt_q);
|
|
if (item == TD_NULL) {
|
|
mrs_unlock(evt->evt_q_lock);
|
|
return;
|
|
}
|
|
|
|
list = &evt->evt_reported_list;
|
|
it = list->next;
|
|
while (it != list) {
|
|
temp = (mrs_cco_evt_reported_item *)it;
|
|
next = it->next;
|
|
|
|
if (memcmp(temp->meter, item->meter, MRS_METER_ADDR_LEN) == 0) {
|
|
mrs_cco_evt_replace_reported_item(temp, item);
|
|
return;
|
|
}
|
|
|
|
it = next;
|
|
}
|
|
|
|
if (evt->report_list_num >= MRS_CCO_EVT_REPORT_ITEM_MAX) {
|
|
temp = (mrs_cco_evt_reported_item *)it->next;
|
|
mrs_cco_evt_replace_reported_item(temp, item);
|
|
return;
|
|
}
|
|
|
|
temp = (mrs_cco_evt_reported_item *)mrs_malloc(sizeof(mrs_cco_evt_reported_item));
|
|
if (temp == TD_NULL) {
|
|
return;
|
|
}
|
|
|
|
(td_void) memcpy_s(temp->meter, sizeof(temp->meter), item->meter, sizeof(item->meter));
|
|
temp->seq = item->seq;
|
|
temp->time = uapi_get_seconds();
|
|
uapi_list_tail_insert(list, &temp->link);
|
|
evt->report_list_num++;
|
|
}
|
|
|
|
td_void mrs_cco_evt_replace_reported_item(mrs_cco_evt_reported_item *report_item, const mrs_cco_evt_item *item)
|
|
{
|
|
mrs_cco_evt_ctx *evt = mrs_cco_get_evt_ctx();
|
|
|
|
report_item->seq = item->seq;
|
|
report_item->time = uapi_get_seconds();
|
|
uapi_list_delete(&report_item->link);
|
|
uapi_list_tail_insert(&evt->evt_reported_list, &report_item->link);
|
|
}
|
|
|
|
#endif /* defined(PRODUCT_CFG_PRODUCT_TYPE_CCO) */
|
|
|