/**************************************************************************** Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED. This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT be copied by any method or incorporated into another program without the express written consent of Aerospace C.Power. This Information or any portion thereof remains the property of Aerospace C.Power. The Information contained herein is believed to be accurate and Aerospace C.Power assumes no responsibility or liability for its use in any way and conveys no license or title under any patent or copyright and makes no representation or warranty that this Information is free from patent or copyright infringement. ****************************************************************************/ /* os_shim header files */ #include "os_utils_api.h" /* iot common header files */ #include "iot_io_api.h" #include "iot_oem_api.h" /* smart grid internal header files */ #include "iot_sg_fr.h" #include "iot_sg.h" #include "proto_645.h" #include "proto_69845.h" #include "iot_sg_sta_ext.h" #if (IOT_SMART_GRID_EXT_NLI_FUNC_ENABLE) static const uint32_t nli_645_07_di_list[] = { PROTO_645_2007_DI_I_A, PROTO_645_2007_DI_I_N, PROTO_645_2007_DI_TIME, PROTO_645_2007_DI_TIME_YMD, PROTO_645_2007_DI_TIME_HMS, }; void iot_sg_sta_ext_nli_info_reset(void) { iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_info_t *ext_info = &sta_glb->ext_info; ext_info->nli_info.di_idx = 0; ext_info->nli_info.flag_resp = 0; ext_info->nli_info.repeat_cnt = 0; ext_info->nli_info.state = IOT_SG_STA_EXT_NLI_STATE_IDLE; ext_info->nli_info.timeout_cnt = 0; } static void iot_sg_sta_ext_nli_node_reset(void) { uint8_t i; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; for (i = 0; i < IOT_SG_STA_SEC_NODE_MAX; i++) { if (sta_glb->node_list[i] != NULL) { sta_glb->node_list[i]->nli_detected = 0; } } } static void iot_sg_sta_ext_nli_rpt_info_reset(void) { iot_sg_sta_ext_info_t *ext_info = &p_sg_glb->desc.sta->ext_info; iot_sg_sta_ext_nli_evt_rpt_info_t *rpt_info = &ext_info->nli_rpt_info; if (rpt_info->pkt) { iot_pkt_free(rpt_info->pkt); } os_mem_set(rpt_info, 0, sizeof(*rpt_info)); } void iot_sg_sta_ext_nli_reset(void) { iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_nli_info_t *nli_info = &sta_glb->ext_info.nli_info; nli_info->next_time = 0; iot_sg_sta_ext_nli_node_reset(); iot_sg_sta_ext_nli_info_reset(); iot_sg_sta_ext_nli_rpt_info_reset(); } void iot_sg_sta_ext_nl_abnormal_detect_state_set(uint8_t enable) { uint8_t ref; uint16_t ticket; iot_sg_sta_app_info_t *sta_pib = iot_sg_sta_get_rw_pib(); if (sta_pib == NULL || iot_sg_sta_get_user_type() != USER_TYPE_STATE_GRID_XIAN) { return; } iot_sg_printf("%s - %lu\n", __FUNCTION__, enable); if (sta_pib->ext_nli_valid) { if (sta_pib->ext_nli_en == !!enable) { return; } } iot_pib_acquire_app_commit_ref(&ref); sta_pib->ext_nli_en = !!enable; sta_pib->ext_nli_valid = 1; iot_pib_release_app_commit_ref(&ref); iot_pib_app_commit(&ticket); iot_sg_sta_ext_nli_reset(); } uint8_t iot_sg_sta_ext_nl_abnormal_detect_state_get(void) { uint8_t ret = 0; iot_sg_sta_app_info_t *sta_pib = iot_sg_sta_get_rw_pib(); if (iot_sg_sta_get_user_type() == USER_TYPE_STATE_GRID_XIAN) { if (sta_pib != NULL && sta_pib->ext_nli_valid == 1) { ret = (uint8_t)sta_pib->ext_nli_en; } } return ret; } static void iot_sg_sta_ext_nli_evt_node_add( iot_sg_sta_ext_nli_evt_data_t *data) { uint32_t pkt_len = 0; iot_sg_sta_ext_info_t *ext_info = &p_sg_glb->desc.sta->ext_info; iot_sg_sta_ext_nli_evt_rpt_info_t *rpt_info = &ext_info->nli_rpt_info; iot_sg_sta_ext_nli_evt_data_field_t *rpt_hdr = NULL; iot_pkt_t *pkt = NULL; if (rpt_info->pkt == NULL) { /* if cache pkt is null, create a pkt that can store * IOT_STA_EXT_NLI_EVT_PKT_PM_CNT_DEFAULT nodes. */ pkt_len = sizeof(*rpt_hdr) + sizeof(*data) * IOT_STA_EXT_NLI_EVT_PKT_PM_CNT_DEFAULT; rpt_info->pkt = iot_pkt_alloc(pkt_len, IOT_SMART_GRID_MID); if (rpt_info->pkt == NULL) { iot_sg_printf("%s err mem 1\n", __FUNCTION__); return; } rpt_hdr = (iot_sg_sta_ext_nli_evt_data_field_t *) iot_pkt_data(rpt_info->pkt); iot_pkt_put(rpt_info->pkt, sizeof(*rpt_hdr)); } else if (iot_pkt_tail_len(rpt_info->pkt) < sizeof(*data)) { /* if cache pkt is lack, expand the size of the cache pkt. */ pkt = iot_pkt_alloc(iot_pkt_data_len(rpt_info->pkt) + sizeof(*data), IOT_SMART_GRID_MID); if (pkt == NULL) { iot_sg_printf("%s err mem 2\n", __FUNCTION__); return; } iot_pkt_cpy(pkt, rpt_info->pkt); iot_pkt_free(rpt_info->pkt); rpt_info->pkt = pkt; } rpt_hdr = (iot_sg_sta_ext_nli_evt_data_field_t *) iot_pkt_data(rpt_info->pkt); rpt_hdr->rpt_cnt++; os_mem_cpy(iot_pkt_tail(rpt_info->pkt), data, sizeof(*data)); iot_pkt_put(rpt_info->pkt, sizeof(*data)); } void iot_sg_sta_ext_nli_evt_ack_done(void) { iot_sg_sta_ext_nli_rpt_info_reset(); } uint8_t iot_sg_sta_ext_nli_seq_check(uint16_t seq) { iot_sg_sta_ext_info_t *ext_info = &p_sg_glb->desc.sta->ext_info; iot_sg_sta_ext_nli_evt_rpt_info_t *rpt_info = &ext_info->nli_rpt_info; if (rpt_info->pkt && rpt_info->seq == seq) { return 1; } return 0; } static uint32_t iot_sg_sta_ext_nli_evt_rpt_start(void) { uint32_t ret = ERR_OK; iot_sg_sta_ext_info_t *ext_info = &p_sg_glb->desc.sta->ext_info; iot_sg_sta_ext_nli_evt_rpt_info_t *rpt_info = &ext_info->nli_rpt_info; if (rpt_info->pkt) { iot_sg_printf("%s start\n", __FUNCTION__); rpt_info->report_cnt = 0; rpt_info->timeout_cnt = IOT_SG_STA_EXT_NLI_EVT_RPT_START; rpt_info->seq = 0; } return ret; } void iot_sg_sta_ext_nli_rpt_refresh(void) { uint32_t ret; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_nli_evt_rpt_info_t *rpt_info = &sta_glb->ext_info.nli_rpt_info; if (iot_sg_sta_get_user_type() != USER_TYPE_STATE_GRID_XIAN || rpt_info->pkt == NULL) { return; } if (rpt_info->timeout_cnt) { rpt_info->timeout_cnt--; if (!rpt_info->timeout_cnt) { if (rpt_info->report_cnt < IOT_SG_STA_EXT_NLI_REPEAT_MAX) { ret = iot_sg_sta_report_nli_event( (iot_sg_sta_ext_nli_evt_data_field_t *) iot_pkt_data(rpt_info->pkt)); if (ret == ERR_OK) { rpt_info->report_cnt++; rpt_info->timeout_cnt = IOT_SG_STA_EXT_NLI_REPORT_TIMEOUT; iot_sg_printf("%s nli evt rpt %luth\n", __FUNCTION__, rpt_info->report_cnt); } else if (ret == ERR_NOSUPP) { iot_sg_sta_ext_nli_evt_ack_done(); iot_sg_printf("%s nli evt rpt nosuppt\n", __FUNCTION__); } } else { iot_sg_sta_ext_nli_evt_ack_done(); iot_sg_printf("%s nli evt rpt failed\n", __FUNCTION__); } } } } static iot_pkt_t *iot_sg_ext_nli_69845_mr_msg_build(uint8_t *addr) { uint8_t i, attribute; uint32_t get_list_req_size, oad; iot_pkt_t *pkt = NULL, *get_list_req = NULL; proto_69845_app_get_list_req_info_t *req = NULL; server_addr_info_t server = {0}; iot_sg_sta_ext_nli_info_t *nli_info = &p_sg_glb->desc.sta->ext_info.nli_info; get_list_req_size = sizeof(proto_69845_app_oad_t) * 3 + sizeof(*req); get_list_req = iot_pkt_alloc(get_list_req_size, IOT_SMART_GRID_MID); if (!get_list_req) { goto out; } req = (proto_69845_app_get_list_req_info_t *)iot_pkt_put(get_list_req, get_list_req_size); req->type = PROTO_69845_APP_GET_NORMALLIST; req->piid.priority = PROTO_69845_APP_PIID_PRIORITY_GENERAL; req->piid.sn = iot_sg_sta_ext_get_69845_apdu_sn(); for (i = 0; i <= IOT_SG_STA_EXT_NLI_READ_TIME; i++) { oad = proto_645_di_to_698_oad(nli_645_07_di_list[i]); if (oad != PROTO_69845_APP_OAD_INVALID) { req->oad[i].oi = (uint16_t)(oad >> 16); attribute = (oad >> 8) & 0xff; req->oad[i].attribute_id = attribute & 0x1f; req->oad[i].attribute_char = attribute >> 5; req->oad[i].element_index = oad & 0xff; } } nli_info->di_idx = IOT_SG_STA_EXT_NLI_READ_TIME; req->oad_cnt = IOT_SG_STA_EXT_NLI_READ_TIME + 1; server.len = PROTO_69845_SA_LEN; server.type = PROTO_69845_SA_TYPE_SIG; iot_mac_addr_cpy(server.addr, addr); pkt = proto_69845_build_get_req_msg_with_rn(req, &server); iot_pkt_free(get_list_req); out: return pkt; } static void iot_sg_ext_nli_evt_check(uint8_t *addr) { uint32_t d_value = 0; int32_t phase_a = 0, gnd_a = 0; iot_pkt_t *pkt = NULL; iot_sg_sta_ext_nli_evt_data_t *evt_node = NULL; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_meter_data_t *data = &sta_glb->ext_info.meter_data; if (!proto_645_bcd_data_check(data->a.a, PROTO_645_07_A_LEN, 1) || !proto_645_bcd_data_check(data->gnd_a, PROTO_645_07_A_LEN, 1)) { return; } phase_a = proto_645_bcd_to_integer(data->a.a, PROTO_645_07_A_LEN, 1); gnd_a = proto_645_bcd_to_integer(data->gnd_a, PROTO_645_07_A_LEN, 1); if (IOT_ABS(gnd_a) > IOT_ABS(phase_a)) { /* If the neutral wire current is greater than the live wire * current by more than 100mA, record the event. */ d_value = IOT_ABS(gnd_a) - IOT_ABS(phase_a); if (d_value > IOT_STA_EXT_NLI_EVT_TRIGGER_VALUE) { pkt = iot_pkt_alloc(sizeof(*evt_node), IOT_SMART_GRID_MID); if (!pkt) { iot_sg_printf("%s err mem\n", __FUNCTION__); return; } evt_node = (iot_sg_sta_ext_nli_evt_data_t *)iot_pkt_data(pkt); iot_mac_addr_cpy(evt_node->pm_addr, addr); os_mem_cpy(evt_node->phase_electric,data->a.a, GW_APP_ELECTRIC_CURRENT_LEN); os_mem_cpy(evt_node->neutralwire_electric,data->gnd_a, GW_APP_ELECTRIC_CURRENT_LEN); proto_645_unsig_to_bcd(d_value, GW_APP_ELECTRIC_CURRENT_LEN, evt_node->nl_electric); evt_node->time[0] = data->time.second; evt_node->time[1] = data->time.minute; evt_node->time[2] = data->time.hour; evt_node->time[3] = data->time.day; evt_node->time[4] = data->time.month; evt_node->time[5] = data->time.year; iot_sg_sta_ext_nli_evt_node_add(evt_node); iot_sg_printf("%s nli event happened\n", __FUNCTION__); iot_pkt_free(pkt); } } } void iot_sg_sta_ext_nli_read_handle(uint16_t seq, uint8_t data_type, uint8_t *resp_data, uint16_t resp_len, uint8_t *req_data, uint16_t req_len) { uint32_t ret = ERR_INVAL; uint8_t flag_nack = 0; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_info_t *ext_info = &sta_glb->ext_info; if (data_type == IOT_SG_STA_DATA_TYPE_69845) { ret = iot_sg_ext_read_698_data_handle(seq, data_type, resp_data, resp_len, req_data, req_len); } else if (data_type == IOT_SG_STA_DATA_TYPE_645_07) { ret = iot_sg_ext_read_645_07_data_handle(seq, data_type, resp_data, resp_len, req_data, req_len, &flag_nack); } if (ret == ERR_OK || ret == ERR_TIMEOVER) { if (ret == ERR_OK) { ext_info->nli_info.flag_resp = !flag_nack; ext_info->nli_info.repeat_cnt = 0; } ext_info->nli_info.timeout_cnt = 0; iot_sg_sta_ext_sm_stop_action(); iot_sg_sta_ext_sm_next_action(IOT_SG_STA_EXT_SM_MIN_DELAY); } } void iot_sg_sta_ext_nli_plc_link_state_change_handle(uint8_t linked) { if (!linked) { iot_sg_sta_ext_nli_reset(); } } static uint8_t iot_sg_sta_ext_nli_detect_state_check(void) { uint8_t ret = IOT_SG_STA_EXT_NLI_DETECTED_STATE_IDLE; uint8_t i, dct = 0, undct = 0; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; for (i = 0; i < IOT_SG_STA_SEC_NODE_MAX; i++) { if (sta_glb->node_list[i] != NULL) { if (sta_glb->node_list[i]->nli_detected) { dct++; } else { undct++; } } } if (dct == 0) { /* if all power meter undetected, in IDLE */ ret = IOT_SG_STA_EXT_NLI_DETECTED_STATE_IDLE; } else if (undct) { /* if any one undetected, and some meter detected, in BUSY */ ret = IOT_SG_STA_EXT_NLI_DETECTED_STATE_BUSY; } else { /* if all power meter detected, in END */ ret = IOT_SG_STA_EXT_NLI_DETECTED_STATE_END; } return ret; } static uint8_t iot_sg_sta_ext_nli_idle_handle(iot_sg_sta_node_desc_t *node) { uint8_t ret = IOT_SG_STA_EXT_NLI_STATE_END, state; uint64_t time; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_info_t *ext_info = &sta_glb->ext_info; iot_sg_sta_ext_nli_info_t *nli_info = &ext_info->nli_info; if (!p_sg_glb->plc_state.link_ready || !iot_sg_sta_ext_nl_abnormal_detect_state_get()) { goto out; } if (node->nli_detected) { goto out; } state = iot_sg_sta_ext_nli_detect_state_check(); if (state == IOT_SG_STA_EXT_NLI_DETECTED_STATE_IDLE) { time = os_boot_time64(); if (time >= nli_info->next_time) { nli_info->next_time = time + IOT_SG_STA_EXT_NLI_INTER; ret = IOT_SG_STA_EXT_NLI_STATE_SEND; } } else if (state == IOT_SG_STA_EXT_NLI_DETECTED_STATE_BUSY) { ret = IOT_SG_STA_EXT_NLI_STATE_SEND; } out: return ret; } static uint8_t iot_sg_sta_ext_nli_wait_handle(iot_sg_sta_node_desc_t *node, uint8_t *flag_new) { uint8_t ret = IOT_SG_STA_EXT_NLI_STATE_WAIT; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_info_t *ext_info = &sta_glb->ext_info; iot_sg_sta_ext_nli_info_t *nli_info = &ext_info->nli_info; if (nli_info->flag_resp) { if (node->data_type == IOT_SG_STA_DATA_TYPE_69845) { goto done; } else { if (nli_info->di_idx == IOT_SG_STA_EXT_NLI_READ_TIME || nli_info->di_idx == IOT_SG_STA_EXT_NLI_READ_TIME_HMS) { /* 645 07 read timestamp or time succeed, deemed complete */ goto done; } else { nli_info->di_idx++; *flag_new = 1; ret = IOT_SG_STA_EXT_NLI_STATE_SEND; } } } else { if (nli_info->timeout_cnt) { nli_info->timeout_cnt--; } else { if (nli_info->repeat_cnt) { nli_info->repeat_cnt--; *flag_new = 0; ret = IOT_SG_STA_EXT_NLI_STATE_SEND; } else { iot_sg_printf("%s read %lu faild\n", __FUNCTION__, nli_info->di_idx); if (node->data_type == IOT_SG_STA_DATA_TYPE_645_07 && nli_info->di_idx == IOT_SG_STA_EXT_NLI_READ_TIME) { /* if the timestamp of 645_07 read failed, continue * trying to read the date and time */ nli_info->di_idx++; *flag_new = 1; ret = IOT_SG_STA_EXT_NLI_STATE_SEND; } else { /* if other data reading fails, abandoned */ ret = IOT_SG_STA_EXT_NLI_STATE_END; node->nli_detected = 1; } } } } goto out; done: ret = IOT_SG_STA_EXT_NLI_STATE_END; node->nli_detected = 1; iot_sg_ext_nli_evt_check(node->entry.addr); out: return ret; } static uint8_t iot_sg_sta_ext_nli_send_handle(iot_sg_sta_node_desc_t *node, uint8_t flag_new) { uint32_t tmp, di; uint8_t ret = IOT_SG_STA_EXT_NLI_STATE_SEND; iot_pkt_t *pkt = NULL; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_info_t *ext_info = &sta_glb->ext_info; iot_sg_sta_ext_nli_info_t *nli_info = &ext_info->nli_info; if (node->data_type == IOT_SG_STA_DATA_TYPE_69845) { iot_sg_printf("%s read 698\n", __FUNCTION__); pkt = iot_sg_ext_nli_69845_mr_msg_build(node->entry.addr); } else if (node->data_type == IOT_SG_STA_DATA_TYPE_645_07) { if (nli_info->di_idx > sizeof(nli_645_07_di_list) / sizeof(nli_645_07_di_list[0])) { ret = IOT_SG_STA_EXT_NLI_STATE_END; goto out; } di = nli_645_07_di_list[nli_info->di_idx]; iot_sg_printf("%s read 645 %.8X\n", __FUNCTION__, di); pkt = proto_645_build_mr_msg(PROTO_645_2007_ID, node->entry.addr, di); } if (pkt) { tmp = iot_sg_sta_add_mr_req(IOT_SG_STA_MR_SRC_ID_NLI_DETECT, node->entry.addr, 0, node->data_type, iot_pkt_data(pkt), (uint16_t)iot_pkt_data_len(pkt), IOT_SG_STA_EXT_DRV_READ_TIMEOUT); if (tmp == ERR_OK) { nli_info->flag_resp = 0; ret = IOT_SG_STA_EXT_NLI_STATE_WAIT; nli_info->timeout_cnt = IOT_SG_STA_EXT_READ_DATA_TIMEOUT; if (flag_new) { nli_info->repeat_cnt = 1; } } iot_pkt_free(pkt); } else { iot_sg_printf("%s err mem\n", __FUNCTION__); } out: return ret; } uint8_t iot_sg_sta_ext_nli_func(uint8_t *addr) { uint8_t complete = 0, flag_new = 0; iot_sg_sta_global_t *sta_glb = p_sg_glb->desc.sta; iot_sg_sta_ext_info_t *ext_info = &sta_glb->ext_info; iot_sg_sta_ext_nli_info_t *nli_info = &ext_info->nli_info; iot_sg_sta_node_desc_t *node = iot_sg_sta_node_find_by_addr(addr); IOT_ASSERT(node); switch (nli_info->state) { case IOT_SG_STA_EXT_NLI_STATE_IDLE: { nli_info->state = iot_sg_sta_ext_nli_idle_handle(node); if (nli_info->state == IOT_SG_STA_EXT_NLI_STATE_END) { goto done; } /* else state is IOT_SG_STA_EXT_NLI_STATE_SEND, * and enters the case of send. */ flag_new = 1; } case IOT_SG_STA_EXT_NLI_STATE_SEND: { send: nli_info->state = iot_sg_sta_ext_nli_send_handle(node, flag_new); break; } case IOT_SG_STA_EXT_NLI_STATE_WAIT: { nli_info->state = iot_sg_sta_ext_nli_wait_handle(node, &flag_new); if (nli_info->state == IOT_SG_STA_EXT_NLI_STATE_SEND) { goto send; } else if (nli_info->state == IOT_SG_STA_EXT_NLI_STATE_END) { goto done; } break; } case IOT_SG_STA_EXT_NLI_STATE_END: { done: if (iot_sg_sta_ext_nli_detect_state_check() == IOT_SG_STA_EXT_NLI_DETECTED_STATE_END) { /* All power meter has been detected neutralwire * and livewire abnormal. */ iot_sg_sta_ext_nli_node_reset(); iot_sg_sta_ext_nli_evt_rpt_start(); } complete = 1; break; } default: IOT_ASSERT(0); break; } return complete; } #endif /* IOT_SMART_GRID_EXT_NLI_FUNC_ENABLE */