Files
kunlun/app/brm/src/iot_brm_ass_check.c
2024-09-28 14:24:04 +08:00

1604 lines
55 KiB
C

/****************************************************************************
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_types_api.h"
#include "os_timer_api.h"
#include "os_utils_api.h"
/* iot common header files */
#include "iot_config_api.h"
#include "iot_io_api.h"
#include "iot_module_api.h"
#include "iot_errno_api.h"
#include "iot_utils_api.h"
#include "iot_bitmap_api.h"
#include "iot_pkt_api.h"
#include "iot_system_api.h"
#include "iot_plc_hw_tsfm_api.h"
#include "iot_plc_msg_api.h"
#include "iot_plc_led_api.h"
#include "iot_uart_api.h"
/* branch management internal header files */
#include "iot_brm_msg.h"
#include "iot_brm_common.h"
#include "iot_brm_rtc.h"
#include "iot_brm_nv.h"
#include "iot_brm_mr.h"
#include "iot_brm_ass_check.h"
/* breaker header files */
#include "iot_brm_brk.h"
/* protocol header file */
#include "proto_69845.h"
#include "proto_645.h"
#include "proto_645_vendor.h"
#include "proto_645_topo.h"
#include "proto_hw_tsfm.h"
#include "proto_3761.h"
#if IOT_BRM_ENABLE
/* define assembly check state */
/* assembly check not performed */
#define IOT_BRM_ASS_CHK_STATE_IDLE 0
/* in this state, waiting to receive more assembly information */
#define IOT_BRM_ASS_CHK_STATE_RECV_WAIT 1
/* in this state, the node probe command is to be issued */
#define IOT_BRM_ASS_CHK_STATE_NODE_READY 2
/* in this state, the probe command has been issued, waiting for response */
#define IOT_BRM_ASS_CHK_STATE_NODE_WAIT 3
#define IOT_BRM_ASS_CHK_STATE_NODE_PLC 4
/* in this state, wait for other check to be done */
#define IOT_BRM_ASS_CHK_STATE_WAIT_OTHER 5
/* in this state, the assembly check is done, wait for some time
* and restart.
*/
#define IOT_BRM_ASS_CHK_STATE_DONE 6
/* define wiring check status */
#define IOT_BRM_ASS_WIRE_CHK_STATE_IDLE 0
#define IOT_BRM_ASS_WIRE_CHK_STATE_DIRECT 1
#define IOT_BRM_ASS_WIRE_CHK_STATE_BD_SEND 2
#define IOT_BRM_ASS_WIRE_CHK_STATE_BD_RCV 3
#define IOT_BRM_ASS_WIRE_CHK_STATE_DONE 4
/* define meter assembly check status */
#define IOT_BRM_ASS_NODE_CHK_STATE_PEND 0 /* no check completed */
#define IOT_BRM_ASS_NODE_CHK_STATE_OK 1 /* assembly ok */
#define IOT_BRM_ASS_NODE_CHK_STATE_NOT_OK 2 /* assembly not ok */
/* define the send wait timeout for branch identification signals, uint is 1s */
#define IOT_BRM_ASS_WIRE_CHK_BD_SEND_TIMEOUT 50
/* define the receive wait timeout for branch identification signals */
#define IOT_BRM_ASS_WIRE_CHK_BD_RCV_TIMEOUT 90
/* define the max try number of assembly check to meter */
#define IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485_698_PROXY 1
#define IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485 2
#define IOT_BRM_ASS_NODE_CHK_TRY_MAX_PLC 3
/* define the duration for which the configuration will take effect,
* uint is 1s
*/
#define IOT_BRM_ASS_NODE_CHK_CONFIG_DUR 15
/* define timeout for node assembly check, uint is 1ms */
#define IOT_BRM_ASS_NODE_CHK_TIMEOUT_PLC 11000 /* PLC node */
#define IOT_BRM_ASS_NODE_CHK_TIMEOUT_RS485 1600 /* RS485 node */
#define IOT_BRM_ASS_NODE_CHK_TIMEOUT_RS485_FOR_3761 12000
#define IOT_BRM_ASS_NODE_CHK_TIMEOUT_RS485_FOR_69845 20000
/* define the timeout for continuous receiving of assembly check info,
* uint is 1ms.
*/
#define IOT_BRM_ASS_REV_ASS_INFO_TIMEOUT 5000
/* node assembly check info descriptor */
typedef struct _iot_brm_ass_node_entry {
/* node addr, little-endian */
uint8_t addr[IOT_MAC_ADDR_LEN];
/* assembly check state, see IOT_BRM_ASS_NODE_CHK_STATE_XXX */
uint8_t state : 2,
/* node proto type, see IOT_BRM_PROTO_TYPE_XXX */
pid : 3,
/* node baud rate id, see IOT_BRM_BUAD_ID_XXX */
bid : 3;
/* remaining try counter */
uint8_t re_try;
} iot_brm_ass_node_entry_t;
/* assembly check control descriptor */
typedef struct _iot_brm_ass_chk_ctrl {
/* checked nodes */
iot_brm_ass_node_entry_t node_ent[IOT_BRM_SUPPORT_NODE_MAX];
/* check state, see IOT_BRM_ASS_CHK_STATE_XXX */
uint8_t state :3,
/* wiring check state, see IOT_BRM_ASS_WIRE_CHK_STATE_XXX */
wire_chk_state :3,
/* flag mark to nodes are connected by PLC */
is_plc_con :1,
/* reserved for further use */
check_en_for_3761 :1;
/* phase line of current branch launch, see IOT_PLC_PHASE_X */
uint8_t phase;
/* PLC connect mode, only bit "is_plc_con"set, these bit fields
* are valid, see PROTO_645_EXT_BRM_ASS_CHK_PLC_MODE_XXX
*/
uint8_t plc_mode;
/* PLC communicate band, only bit "is_plc_con"set, these bit fields
* are valid, see PLC_LIB_FREQ_BAND_XXX
*/
uint8_t plc_band;
/* mr buffer's customer ID */
uint8_t custom_id;
/* wiring check counter */
uint32_t wire_chk_cnt;
/* plc check counter */
uint32_t plc_cfg_cmd_cnt;
/* baud rate index in use */
uint8_t b_idx;
/* wiring check status, see IOT_BRM_ASS_WIRE_XXX */
uint8_t wire_chk_st[IOT_BRM_PHASE_C];
/* start time stamp of check, uint is 1s */
uint32_t start_ts;
/* terminal communication port number, see PROTO_PORT_RS485_X */
uint8_t trans_port :2,
/* if communication success, lock the terminal port */
trans_port_lock :1,
/* fix the terminal baud rate, if communicate success with terminal */
b_idx_lock :1,
/* not used */
rsvd :4;
/* timer for check */
timer_id_t chk_timer;
/* baud rate table to be checking, only used for assembly check of 485 nodes
*/
uint32_t buad_tab[IOT_BRM_PROTO_TYPE_MAX][IOT_BRM_SUPPORT_BAUD_MAX];
/* protocol table to be checking */
uint8_t protocol_table[IOT_BRM_PROTO_TYPE_MAX];
} iot_brm_ass_chk_ctrl_t;
static iot_brm_ass_chk_ctrl_t g_ass_ccb = {
.state = IOT_BRM_ASS_CHK_STATE_IDLE,
.buad_tab = {
{0, 0, 0, 0, 0}, /* transparent transmission */
{1200, 0, 0, 0, 0}, /* dl/t 645-1997 */
{2400, 0, 0, 0, 0}, /* dl/t 645-2007 */
{9600, 0, 0, 0, 0}, /* dl/t 698-45 */
{9600, 2400, 0, 0, 0}, /* 376.1 */
{9600, 0, 0, 0, 0}, /* 698.45-trans */
{9600, 2400, 0, 0, 0}, /* 698.45-trans 376.1*/
{9600, 0, 0, 0, 0}, /* 698.45-trans 698.45*/
},
};
static uint8_t sn_3761;
static uint8_t sn_698;
static iot_brm_ass_node_entry_t *iot_brm_ass_first_pend_node(void)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_ass_node_entry_t *node;
uint8_t i;
for (i = 0; i < IOT_BRM_SUPPORT_NODE_MAX; i++) {
node = &ccb->node_ent[i];
if (!iot_mac_addr_valid(node->addr)) {
break;
}
if (node->state == IOT_BRM_ASS_NODE_CHK_STATE_PEND) {
return node;
}
}
return NULL;
}
static iot_brm_ass_node_entry_t *iot_brm_ass_find_node(uint8_t *addr)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_ass_node_entry_t *node;
uint8_t i;
for (i = 0; i < IOT_BRM_SUPPORT_NODE_MAX; i++) {
node = &ccb->node_ent[i];
if (!iot_mac_addr_valid(node->addr)) {
break;
}
if (iot_mac_addr_cmp(node->addr, addr)) {
return node;
}
}
return NULL;
}
/**
* @brief iot_brm_ass_check_proto_table_init() - initialization the table to
load the protocols that will be used for check meter.
*/
static void iot_brm_ass_check_proto_table_init(void)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_terminal_cfg_t cfg;
/* Note that must use IOT_BRM_PROTO_TYPE_TRANSPARENT as the last protocol,
* and the baud rate table of each protocol in the protocol list must be
* consistent.
*/
if (ccb->is_plc_con) {
/* Protocol table for PLC communication with the meter */
ccb->protocol_table[0] = IOT_BRM_PROTO_TYPE_69845;
ccb->protocol_table[1] = IOT_BRM_PROTO_TYPE_645_2007;
ccb->protocol_table[2] = IOT_BRM_PROTO_TYPE_645_1997;
ccb->protocol_table[3] = IOT_BRM_PROTO_TYPE_TRANSPARENT;
} else if (ccb->check_en_for_3761) {
if (iot_brm_get_terminal_cfg(&cfg) != ERR_OK) {
/* unknown type proxy transfer */
iot_brm_printf("%s: please set the terminal info first.\n",
__FUNCTION__);
IOT_ASSERT(0);
} else if (cfg.is_698) {
/* Protocol table for 698.45 proxy transfer */
ccb->protocol_table[0] = IOT_BRM_PROTO_TYPE_69845_BY_69845;
ccb->protocol_table[1] = IOT_BRM_PROTO_TYPE_645_2007_BY_69845;
ccb->protocol_table[2] = IOT_BRM_PROTO_TYPE_TRANSPARENT;
} else {
/* Protocol table for 376.1 proxy transfer */
ccb->protocol_table[0] = IOT_BRM_PROTO_TYPE_69845_BY_3761;
ccb->protocol_table[1] = IOT_BRM_PROTO_TYPE_645_2007_BY_3761;
ccb->protocol_table[2] = IOT_BRM_PROTO_TYPE_TRANSPARENT;
}
} else {
/* Protocol table for direct communication with the meter */
ccb->protocol_table[0] = IOT_BRM_PROTO_TYPE_69845;
ccb->protocol_table[1] = IOT_BRM_PROTO_TYPE_645_2007;
ccb->protocol_table[2] = IOT_BRM_PROTO_TYPE_645_1997;
ccb->protocol_table[3] = IOT_BRM_PROTO_TYPE_TRANSPARENT;
}
}
/**
* @brief iot_brm_ass_check_load_proto() - initialization the global variable
* for start a new protocol.
* @param node: Node that need to load protocol.
*/
static void iot_brm_ass_check_load_proto(
iot_brm_ass_node_entry_t *node)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_printf("%s pid: %d\n", __FUNCTION__, node->pid);
if (!ccb->b_idx_lock) {
ccb->b_idx = 0;
} else if (ccb->buad_tab[node->pid][ccb->b_idx] == 0) {
IOT_ASSERT(0);
}
switch (node->pid) {
case IOT_BRM_PROTO_TYPE_69845:
case IOT_BRM_PROTO_TYPE_645_2007:
case IOT_BRM_PROTO_TYPE_645_1997:
{
if (ccb->is_plc_con)
node->re_try = IOT_BRM_ASS_NODE_CHK_TRY_MAX_PLC;
else
node->re_try = IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485;
break;
}
case IOT_BRM_PROTO_TYPE_69845_BY_3761:
case IOT_BRM_PROTO_TYPE_645_2007_BY_3761:
{
node->re_try = IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485;
if (!ccb->trans_port_lock)
ccb->trans_port = PROTO_PORT_RS485_2;
break;
}
case IOT_BRM_PROTO_TYPE_69845_BY_69845:
case IOT_BRM_PROTO_TYPE_645_2007_BY_69845:
{
node->re_try = IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485_698_PROXY;
if (!ccb->trans_port_lock)
ccb->trans_port = PROTO_PORT_RS485_1;
break;
}
case IOT_BRM_PROTO_TYPE_TRANSPARENT:
default:
/* never in this case, if goto here, please check protocol_table */
IOT_ASSERT(0);
break;
}
}
/**
* @brief iot_brm_ass_check_proto_switch_next() - switch to next protocol in the
* protocol table.
* @param node: Node that need to switch protocol.
* @retval: 1 - for success case, 0 - for no more protocol in the table.
*/
static uint8_t iot_brm_ass_check_proto_switch_next(
iot_brm_ass_node_entry_t *node)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
uint8_t p_idx;
/* get the protocol index of the node's pid */
for (p_idx = 0; p_idx < IOT_BRM_PROTO_TYPE_MAX; p_idx++) {
if (ccb->protocol_table[p_idx] == node->pid) {
break;
}
}
p_idx++;
if (p_idx >= IOT_BRM_PROTO_TYPE_MAX
|| ccb->protocol_table[p_idx] == IOT_BRM_PROTO_TYPE_TRANSPARENT) {
/* all protocol are tried, switch to the first protocol, and return
* switch fail.
*/
node->pid = IOT_BRM_PROTO_TYPE_TRANSPARENT;
return 0;
}
node->pid = ccb->protocol_table[p_idx];
iot_brm_ass_check_load_proto(node);
return 1;
}
/**
* @brief iot_brm_ass_build_69845_detect_pkt() - build request packet for assembly
* check use 698.45 protocol.
* @param node: node information.
* @retval if success, return the request packet,
* otherwise, return NULL.
*/
static iot_pkt_t *iot_brm_ass_build_69845_detect_pkt(
iot_brm_ass_node_entry_t *node)
{
server_addr_info_t server = {0};
proto_69845_app_get_req_info_t req = {0};
req.type = PROTO_69845_APP_GET_NORMAL;
req.piid.priority = PROTO_69845_APP_PIID_PRIORITY_GENERAL;
req.piid.sn = 0;
req.oad.oi = PROTO_69845_APP_OI_ADDR;
req.oad.attribute_id = 2;
req.oad.attribute_char = 0;
req.oad.element_index = 0;
server.len = PROTO_69845_SA_LEN;
server.type = PROTO_69845_SA_TYPE_SIG;
iot_mac_addr_cpy(server.addr, node->addr);
return proto_69845_build_get_req_msg(&req, &server);
}
/**
* @brief iot_brm_ass_build_3761_proxy_detect_pkt() - add wrap for use 376.1 protocol
* proxy transfer command request.
* @param content: original frame.
* @param baud: the baud rate of proxy device communicate with target device.
* @retval if success, return the proxy packet,
* otherwise, return NULL.
* @note this function will free content.
*/
static iot_pkt_t *iot_brm_ass_build_3761_proxy_detect_pkt(iot_pkt_t *content,
uint32_t baud)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_terminal_cfg_t config;
iot_pkt_t *fwd_pkt;
uint16_t dev_addr = PROTO_3761_BCAST_DEV_ADRR, area_code = 9999;
IOT_ASSERT(content != NULL);
if (iot_brm_get_terminal_cfg(&config) == ERR_OK && !config.is_698) {
dev_addr = config.terminal_addr.trans_3761.dev_addr;
area_code = config.terminal_addr.trans_3761.area_code;
}
sn_3761 = proto_3761_get_sn();
fwd_pkt = proto_3761_build_afn10f01_dl_msg(iot_pkt_data(content), \
(uint16_t)iot_pkt_data_len(content),
baud, ccb->trans_port, IOT_UART_PARITY_EVEN,
IOT_BRM_PROXY_TRANS_TIMEOUT, IOT_BRM_MID,
&dev_addr, &area_code, sn_3761);
iot_pkt_free(content);
return fwd_pkt;
}
/**
* @brief iot_brm_ass_build_69845_proxy_detect_pkt() - add wrap for use 698.45 protocol
* proxy transfer command request.
* @param content: original frame.
* @param baud: the baud rate of proxy device communicate with target device.
* @retval if success, return the proxy packet,
* otherwise, return NULL.
* @note this function will free content.
*/
static iot_pkt_t *iot_brm_ass_build_69845_proxy_detect_pkt(iot_pkt_t *content,
uint32_t baud)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
server_addr_info_t server = {0};
iot_brm_terminal_cfg_t config;
iot_pkt_t *fwd_pkt;
if (iot_brm_get_terminal_cfg(&config) == ERR_OK && config.is_698) {
server.len = PROTO_69845_SA_LEN;
server.type = PROTO_69845_SA_TYPE_SIG;
iot_mac_addr_cpy(server.addr, config.terminal_addr.trans_698_addr);
} else {
server = proto_69845_any_server_addr;
}
sn_698 = iot_brm_mr_698_get_apdu_sn();
fwd_pkt = proto_69845_build_proxy_trans_data_msg(iot_pkt_data(content), \
(uint16_t)iot_pkt_data_len(content),
baud, ccb->trans_port, PROTO_69845_PARITY_EVEN,
IOT_BRM_PROXY_TRANS_TIMEOUT, &server, sn_698);
iot_pkt_free(content);
return fwd_pkt;
}
static uint8_t iot_brm_ass_node_check_issue(uint32_t *to)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_ass_node_entry_t *node;
iot_pkt_t *pkt = NULL, *fwd_pkt;
uint8_t *preamble = NULL, *data, ret = ERR_OK, preamble_fill = 1;
uint32_t pre_len = 0;
node = iot_brm_ass_first_pend_node();
if (node == NULL) {
return ERR_NOT_EXIST;
}
/* When first time to check one node, load the first matching protocol */
if (node->pid == IOT_BRM_PROTO_TYPE_TRANSPARENT) {
node->pid = ccb->protocol_table[0];
iot_brm_ass_check_load_proto(node);
}
switch (node->pid) {
case IOT_BRM_PROTO_TYPE_645_1997:
{
preamble = (uint8_t *)proto_645_preamble;
pre_len = PROTO_645_PREAMBLE_LEN;
pkt = proto_645_build_mr_msg(PROTO_645_1997_ID,
node->addr, PROTO_645_1997_DI_R_ADDR);
break;
}
case IOT_BRM_PROTO_TYPE_645_2007:
{
preamble = (uint8_t *)proto_645_preamble;
pre_len = PROTO_645_PREAMBLE_LEN;
pkt = proto_645_build_mr_msg(PROTO_645_2007_ID,
node->addr, PROTO_645_2007_DI_R_ADDR);
break;
}
case IOT_BRM_PROTO_TYPE_69845:
{
preamble = (uint8_t *)proto_69845_preamble;
pre_len = PROTO_69845_PREAMBLE_LEN;
pkt = iot_brm_ass_build_69845_detect_pkt(node);
break;
}
case IOT_BRM_PROTO_TYPE_645_2007_BY_3761:
case IOT_BRM_PROTO_TYPE_645_2007_BY_69845:
{
pkt = proto_645_build_mr_msg(PROTO_645_2007_ID,
node->addr, PROTO_645_2007_DI_R_ADDR);
if (pkt == NULL) {
ret = ERR_NOMEM;
goto out;
}
data = iot_pkt_push(pkt, PROTO_645_PREAMBLE_LEN);
os_mem_cpy(data, proto_645_preamble, PROTO_645_PREAMBLE_LEN);
if (node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_3761) {
/* add 376.1 proxy wrap */
fwd_pkt = iot_brm_ass_build_3761_proxy_detect_pkt(pkt, 2400);
} else {
/* add 698.45 proxy wrap */
fwd_pkt = iot_brm_ass_build_69845_proxy_detect_pkt(pkt, 2400);
}
if (fwd_pkt == NULL) {
ret = ERR_NOMEM;
goto out;
}
preamble_fill = 0;
pkt = fwd_pkt;
break;
}
case IOT_BRM_PROTO_TYPE_69845_BY_3761:
case IOT_BRM_PROTO_TYPE_69845_BY_69845:
{
/* build 698.45 mr packet */
pkt = iot_brm_ass_build_69845_detect_pkt(node);
data = iot_pkt_push(pkt, PROTO_69845_PREAMBLE_LEN);
os_mem_cpy(data, proto_69845_preamble, PROTO_69845_PREAMBLE_LEN);
if (node->pid == IOT_BRM_PROTO_TYPE_69845_BY_3761) {
/* add 376.1 proxy wrap */
fwd_pkt = iot_brm_ass_build_3761_proxy_detect_pkt(pkt, 9600);
} else {
/* add 698.45 proxy wrap */
fwd_pkt = iot_brm_ass_build_69845_proxy_detect_pkt(pkt, 9600);
}
if (fwd_pkt == NULL) {
ret = ERR_NOMEM;
goto out;
}
preamble_fill = 0;
pkt = fwd_pkt;
break;
}
default:
IOT_ASSERT(0);
break;
}
if (!pkt) {
ret = ERR_NOMEM;
goto out;
}
if (preamble_fill) {
data = iot_pkt_push(pkt, pre_len);
os_mem_cpy(data, preamble, pre_len);
}
if (node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_3761
|| node->pid == IOT_BRM_PROTO_TYPE_69845_BY_3761) {
*to = IOT_BRM_ASS_NODE_CHK_TIMEOUT_RS485_FOR_3761
+ IOT_BRM_PROXY_TRANS_TIMEOUT * 1000;
} else if (node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_69845
|| node->pid == IOT_BRM_PROTO_TYPE_69845_BY_69845) {
*to = IOT_BRM_ASS_NODE_CHK_TIMEOUT_RS485_FOR_69845
+ IOT_BRM_PROXY_TRANS_TIMEOUT * 1000;
} else {
*to = IOT_BRM_ASS_NODE_CHK_TIMEOUT_RS485;
}
iot_brm_printf("%s protocol id: %d, 485 port: %d\n", __FUNCTION__,
node->pid, ccb->trans_port);
iot_brm_rs485_config_buad_internal(ccb->buad_tab[node->pid][ccb->b_idx],
node->pid);
iot_brm_rs485_send(pkt);
out:
return ret;
}
static uint8_t iot_brm_ass_node_check_issue_on_plc()
{
iot_pkt_t *pkt = NULL;
iot_brm_ass_node_entry_t *node;
proto_69845_app_get_req_info_t req = {0};
server_addr_info_t server = {0};
uint8_t *preamble = NULL, *data;
uint8_t idx, ret = ERR_OK, allow_mr = 0;
uint32_t pre_len = 0;
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
uint8_t addr[IOT_MAC_ADDR_LEN];
node = iot_brm_ass_first_pend_node();
if (node) {
ccb->plc_cfg_cmd_cnt++;
iot_set_bcast_mac(addr);
iot_brm_send_config_cmd(addr, ccb->plc_band,
IOT_BRM_ASS_NODE_CHK_CONFIG_DUR, 1);
if (ccb->plc_cfg_cmd_cnt > 15) {
/* send the configuration command first, fix the band of the node
* not connected to the network, and start to read (check) the
* node.
*/
allow_mr = 1;
}
} else {
/* all nodes are check done */
ccb->plc_cfg_cmd_cnt = 0;
ret = ERR_NOT_EXIST;
goto out;
}
for (idx = 0; idx < IOT_BRM_SUPPORT_NODE_MAX && allow_mr; idx++) {
node = &ccb->node_ent[idx];
if (!iot_mac_addr_valid(node->addr)) {
/* reach valid address list boundary */
break;
}
if (node->state == IOT_BRM_ASS_NODE_CHK_STATE_OK
|| node->state == IOT_BRM_ASS_NODE_CHK_STATE_NOT_OK) {
/* node assembly check has ben completed, skip */
continue;
}
/* When first time to check one node, load the first protocol */
if (node->pid == IOT_BRM_PROTO_TYPE_TRANSPARENT) {
node->pid = ccb->protocol_table[0];
iot_brm_ass_check_load_proto(node);
}
switch (node->pid) {
case IOT_BRM_PROTO_TYPE_645_1997:
{
preamble = (uint8_t *)proto_645_preamble;
pre_len = PROTO_645_PREAMBLE_LEN;
pkt = proto_645_build_mr_msg(PROTO_645_1997_ID,
node->addr, PROTO_645_1997_DI_R_ADDR);
break;
}
case IOT_BRM_PROTO_TYPE_645_2007:
{
preamble = (uint8_t *)proto_645_preamble;
pre_len = PROTO_645_PREAMBLE_LEN;
pkt = proto_645_build_mr_msg(PROTO_645_2007_ID,
node->addr, PROTO_645_2007_DI_R_ADDR);
break;
}
case IOT_BRM_PROTO_TYPE_69845:
{
preamble = (uint8_t *)proto_69845_preamble;
pre_len = PROTO_69845_PREAMBLE_LEN;
req.type = PROTO_69845_APP_GET_NORMAL;
req.piid.priority = PROTO_69845_APP_PIID_PRIORITY_GENERAL;
req.piid.sn = 0;
req.oad.oi = PROTO_69845_APP_OI_ADDR;
req.oad.attribute_id = 2;
req.oad.attribute_char = 0;
req.oad.element_index = 0;
server.len = PROTO_69845_SA_LEN;
server.type = PROTO_69845_SA_TYPE_SIG;
iot_mac_addr_cpy(server.addr, node->addr);
pkt = proto_69845_build_get_req_msg(&req, &server);
break;
}
default:
IOT_ASSERT(0);
break;
}
if (!pkt) {
ret = ERR_NOMEM;
goto out;
}
data = iot_pkt_push(pkt, pre_len);
os_mem_cpy(data, preamble, pre_len);
ret = iot_brm_mr_buf_cmd_add(ccb->custom_id, node->addr, 0,
node->pid, pkt, IOT_BRM_ASS_NODE_CHK_TIMEOUT_PLC,
iot_brm_mr_cmd_prio_1, iot_brm_mr_type_ass_chk, 0);
if (ret) {
break;
}
}
out:
return ret;
}
static uint8_t iot_brm_ass_node_check_resp_handle(iot_pkt_t *pkt)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
proto_645_header_t *hdr_645;
proto_69845_frame_head_info_t *hdr_698;
uint8_t *ds, *data, *ser_addr;
uint8_t msg_addr[IOT_MAC_ADDR_LEN], proto_type;
uint32_t len, baud = 0, ret;
uint8_t accept = 0, sn;
uint8_t timeout, *addr;
iot_brm_ass_node_entry_t *node;
proto_3761_app_data_desc_t app = { 0 };
proto_3761_afn10f1_ul_t *ul;
iot_brm_terminal_cfg_t cfg;
uint16_t fn, pn, len_temp;
uint16_t terminal_dev_addr;
uint8_t terminal_area_code[PROTO_3761_AREA_CODE_LEN];
if (!ccb->is_plc_con) {
node = iot_brm_ass_first_pend_node();
timeout = (pkt == NULL);
} else {
addr = iot_pkt_data(pkt);
addr -= IOT_MAC_ADDR_LEN;
len = iot_pkt_data_len(pkt);
timeout = !len;
node = iot_brm_ass_find_node(addr);
}
if (!node || node->state != IOT_BRM_ASS_NODE_CHK_STATE_PEND) {
goto out;
}
if (timeout) {
iot_brm_printf("%s %02x%02x%02x%02x%02x%02x timeout...\n",
__FUNCTION__, node->addr[5], node->addr[4], node->addr[3],
node->addr[2], node->addr[1], node->addr[0]);
IOT_ASSERT(node->re_try);
node->re_try--;
if (!node->re_try) {
if (!ccb->is_plc_con) {
/* switch baud rate, when the terminal baud rate is not fixed */
if (!ccb->b_idx_lock)
ccb->b_idx++;
if (node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_69845
|| node->pid == IOT_BRM_PROTO_TYPE_69845_BY_69845) {
node->re_try = IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485_698_PROXY;
} else {
node->re_try = IOT_BRM_ASS_NODE_CHK_TRY_MAX_RS485;
}
if (ccb->b_idx_lock ||
ccb->b_idx >= IOT_BRM_SUPPORT_BAUD_MAX ||
!ccb->buad_tab[node->pid][ccb->b_idx]) {
/* if the terminal baud rate is not fixed, try all baud
* of the protocol; if fixed, to use current baud rate.
*/
if (!ccb->b_idx_lock)
ccb->b_idx = 0;
/* all baud rate checks are done, switch port or protocol */
if ((node->pid == IOT_BRM_PROTO_TYPE_69845_BY_3761
|| node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_3761)
&& !ccb->trans_port_lock
&& ccb->trans_port == PROTO_PORT_RS485_2) {
ccb->trans_port = PROTO_PORT_RS485_1;
} else if ((node->pid == IOT_BRM_PROTO_TYPE_69845_BY_69845
|| node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_69845)
&& !ccb->trans_port_lock
&& ccb->trans_port == PROTO_PORT_RS485_1) {
ccb->trans_port = PROTO_PORT_RS485_2;
} else if (iot_brm_ass_check_proto_switch_next(node) != 1) {
goto node_finish;
}
}
} else if (iot_brm_ass_check_proto_switch_next(node) != 1) {
goto node_finish;
}
}
goto out;
}
ds = iot_pkt_data(pkt);
len = iot_pkt_data_len(pkt);
switch (node->pid) {
case IOT_BRM_PROTO_TYPE_645_1997:
case IOT_BRM_PROTO_TYPE_645_2007:
{
hdr_645 = proto_645_format_check(ds, len, PROTO_645_DIR_SLAVE);
if (!hdr_645) {
goto out;
}
if (!iot_mac_addr_cmp(node->addr, hdr_645->addr)) {
goto out;
}
break;
}
case IOT_BRM_PROTO_TYPE_69845:
{
hdr_698 = proto_69845_sanity_check(ds, (uint16_t)len);
if (!hdr_698) {
goto out;
}
ser_addr = proto_69845_get_ser_addr(hdr_698);
if (!iot_mac_addr_cmp(node->addr, ser_addr)) {
goto out;
}
break;
}
case IOT_BRM_PROTO_TYPE_645_2007_BY_3761:
case IOT_BRM_PROTO_TYPE_69845_BY_3761:
{
ret = proto_3761_parse(ds, (uint16_t)len, &app);
if (ret || app.prm != 0 || app.dir == PROTO_3761_DIR_MASTER) {
goto out;
}
if (app.seq != sn_3761)
goto out;
data = app.data;
len_temp = app.len;
if (iot_brm_get_terminal_cfg(&cfg) == ERR_OK && !cfg.is_698) {
iot_uint32_to_bcd(cfg.terminal_addr.trans_3761.area_code,
PROTO_3761_AREA_CODE_LEN,
terminal_area_code);
terminal_dev_addr = cfg.terminal_addr.trans_3761.dev_addr;
} else {
iot_uint32_to_bcd(9999, PROTO_3761_AREA_CODE_LEN,
terminal_area_code);
terminal_dev_addr = PROTO_3761_BCAST_DEV_ADRR;
}
if (os_mem_cmp(app.addr_filed.area_code, terminal_area_code,
PROTO_3761_AREA_CODE_LEN) == 0
&& app.addr_filed.dev_addr == terminal_dev_addr) {
ccb->b_idx_lock = 1;
iot_brm_printf("%s lock interminal baud: %d\n", __FUNCTION__,
ccb->buad_tab[node->pid][ccb->b_idx]);
} else {
goto out;
}
switch (app.afn) {
case PROTO_3761_AFN_10:
{
ret = proto_3761_du_parse(&pn, &fn, &data, &len_temp);
if (ret) {
goto out;
}
switch (fn) {
case 0x01:
{
if (len_temp < sizeof(*ul)) {
goto out;
}
ul = (proto_3761_afn10f1_ul_t *)data;
len_temp -= sizeof(*ul);
data += sizeof(*ul);
if (len_temp < ul->len) {
goto out;
}
if (iot_brm_get_addr_from_message(data, len_temp, msg_addr,
&proto_type) != ERR_OK) {
goto out;
}
if (!iot_mac_addr_cmp(node->addr, msg_addr)) {
goto out;
}
ccb->trans_port_lock = 1;
iot_brm_printf("%s lock interminal port: %d\n", __FUNCTION__,
ccb->trans_port);
ret = iot_brm_get_terminal_cfg(&cfg);
if (ret || cfg.is_698) {
cfg.terminal_addr.trans_3761.dev_addr =
PROTO_3761_BCAST_DEV_ADRR;
cfg.terminal_addr.trans_3761.area_code = 9999;
}
cfg.is_698 = 0;
cfg.trans_port = ccb->trans_port;
iot_brm_set_terminal_cfg(&cfg);
break;
}
default:
goto out;
}
break;
}
default:
goto out;
}
break;
}
case IOT_BRM_PROTO_TYPE_645_2007_BY_69845:
case IOT_BRM_PROTO_TYPE_69845_BY_69845:
{
if (proto_69845_proxy_trans_resp_parse(&ds, (uint16_t*)&len, &sn)
!= ERR_OK) {
goto out;
}
if (sn != sn_698)
goto out;
ccb->b_idx_lock = 1;
iot_brm_printf("%s lock interminal baud: %d\n", __FUNCTION__,
ccb->buad_tab[node->pid][ccb->b_idx]);
if (iot_brm_get_addr_from_message(ds, (uint16_t)len, msg_addr,
&proto_type) != ERR_OK) {
goto out;
}
if (!iot_mac_addr_cmp(node->addr, msg_addr)) {
goto out;
}
ccb->trans_port_lock = 1;
iot_brm_printf("%s lock interminal port: %d\n", __FUNCTION__,
ccb->trans_port);
ret = iot_brm_get_terminal_cfg(&cfg);
if (ret || !cfg.is_698) {
os_mem_set(cfg.terminal_addr.trans_698_addr, 0xAA,
IOT_MAC_ADDR_LEN);
}
cfg.is_698 = 1;
cfg.trans_port = ccb->trans_port;
iot_brm_set_terminal_cfg(&cfg);
break;
}
default:
IOT_ASSERT(0);
break;
}
if (!ccb->is_plc_con) {
baud = ccb->buad_tab[node->pid][ccb->b_idx];
node->bid = iot_brm_baud_to_bid(baud);
}
accept = 1;
node_finish:
if (accept) {
node->state = IOT_BRM_ASS_NODE_CHK_STATE_OK;
iot_brm_printf("assembly ok: addr %02x%02x%02x%02x%02x%02x, pid %lu,"
" baud %lu\n", node->addr[5], node->addr[4], node->addr[3],
node->addr[2], node->addr[1], node->addr[0], node->pid, baud);
} else {
node->pid = IOT_BRM_PROTO_TYPE_TRANSPARENT;
node->state = IOT_BRM_ASS_NODE_CHK_STATE_NOT_OK;
iot_brm_printf("assembly fail: addr %02x%02x%02x%02x%02x%02x\n",
node->addr[5], node->addr[4], node->addr[3],
node->addr[2], node->addr[1], node->addr[0]);
}
out:
if (pkt) {
iot_pkt_free(pkt);
}
return accept;
}
void iot_brm_ass_check_bd_recv_handle(uint8_t *phase)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
uint8_t idx = ccb->phase - 1;
if (ccb->wire_chk_state != IOT_BRM_ASS_WIRE_CHK_STATE_BD_RCV)
return;
iot_brm_printf("%s in phase %lu", __FUNCTION__, phase ? (*phase) : 0);
if (phase != NULL) {
if (*phase != ccb->phase) {
/* if the signal is not received on the transmission phase line,
* ignore it to avoid crosstalk affecting the check result.
*/
return;
}
ccb->wire_chk_cnt = 0;
} else {
/* branch identification signal not received, there may be wiring
* errors.
*/
if (!ccb->wire_chk_st[idx])
ccb->wire_chk_st[idx] = PROTO_645_EXT_BRM_ASS_WIRE_ERR3;
}
if (iot_plc_hw_tsfm_is_3p()) {
switch (ccb->phase) {
case IOT_PLC_PHASE_A:
ccb->phase = IOT_PLC_PHASE_B;
break;
case IOT_PLC_PHASE_B:
ccb->phase = IOT_PLC_PHASE_C;
break;
case IOT_PLC_PHASE_C:
goto done;
default:
IOT_ASSERT(0);
break;
}
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_BD_SEND;
} else {
done:
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_DONE;
}
}
void iot_brm_ass_check_wiring(void)
{
uint8_t reason = 0;
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_meter_info_t *m;
iot_brm_meter_var_t *var;
uint8_t data[PROTO_645_VENDOR_BR_DATA_LEN];
uint8_t i, temp[IOT_BRM_PHASE_C] = { 0 };
hw_tsfm_send_cfg_t cfg = { 0 };
proto_hw_tsfm_hdr_t *topo_hdr;
uint32_t ret;
switch (ccb->wire_chk_state) {
case IOT_BRM_ASS_WIRE_CHK_STATE_DIRECT:
{
m = iot_brm_load_meter_info();
var = &m->var;
for (i = IOT_BRM_PHASE_A; i <= IOT_BRM_PHASE_C; i++) {
if (var->s[i] > 10) {
/* reverse power, two possibilities:
* 1. CT reverse connection
* 2. voltage sequence and current sequence do not correspond
*/
if (var->p[i] < 0) {
temp[i - 1] = PROTO_645_EXT_BRM_ASS_WIRE_ERR1;
}
} else if (var->i[i - 1] >= 100 && var->v[ i - 1] < 700) {
/* possibility:
* when single-phase connection is used, CT is not connected
* to the first current terminal
*/
temp[i - 1] = PROTO_645_EXT_BRM_ASS_WIRE_ERR2;
}
}
if (!temp[1]) {
if (var->v[0] > 700 && var->v[2] > 700 && var->v[1] < 300) {
temp[1] = PROTO_645_EXT_BRM_ASS_WIRE_ERR2;
}
}
if (temp[0] != ccb->wire_chk_st[0]
|| temp[1] != ccb->wire_chk_st[1]
|| temp[2] != ccb->wire_chk_st[2]) {
ccb->wire_chk_st[0] = temp[0];
ccb->wire_chk_st[1] = temp[1];
ccb->wire_chk_st[2] = temp[2];
ccb->wire_chk_cnt = 0;
} else if (ccb->wire_chk_cnt++ > 10) {
/* don't need to check branch identification */
if (iot_brm_is_brk()) {
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_DONE;
} else {
/* next: wiring check by branch identification */
ccb->wire_chk_cnt = 0;
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_BD_SEND;
ccb->phase = IOT_PLC_PHASE_A;
}
}
break;
}
case IOT_BRM_ASS_WIRE_CHK_STATE_BD_SEND:
{
cfg.para.zc.ahead = 700;
cfg.para.zc.dur_output = IOT_PLC_HW_TSFM_OUTPUT_MAX_TIME;
cfg.mod_mode = IOT_PLC_HW_TSFM_SEND_MODE_ZC;
cfg.phase = ccb->phase;
topo_hdr = (proto_hw_tsfm_hdr_t*)data;
topo_hdr->data_id = PROTO_HW_TSFM_ID_TOPO_TEST;
topo_hdr->data_len = 0;
ret = proto_645_vendor_br_launch(data, sizeof(*topo_hdr), &cfg);
if (ret == ERR_OK) {
iot_brm_printf("%s send branch test ok\n", __FUNCTION__);
ccb->wire_chk_cnt = 0;
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_BD_RCV;
} else if (++ccb->wire_chk_cnt >= IOT_BRM_ASS_WIRE_CHK_BD_SEND_TIMEOUT) {
/* Send timeout, end check */
ccb->wire_chk_cnt = 0;
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_DONE;
reason = 1;
}
break;
}
case IOT_BRM_ASS_WIRE_CHK_STATE_BD_RCV:
{
if (++ccb->wire_chk_cnt >= IOT_BRM_ASS_WIRE_CHK_BD_RCV_TIMEOUT) {
/* branch identification signal not received */
ccb->wire_chk_cnt = 0;
reason = 2;
iot_brm_ass_check_bd_recv_handle(NULL);
}
break;
}
default:
break;
}
if (reason) {
iot_brm_printf("%s fail reason %lu\n", __FUNCTION__, reason);
}
}
static void iot_brm_ass_check_result_save()
{
iot_brm_nv_ass_chk_info_t *ass_info;
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_ass_node_entry_t *node;
iot_time_tm_t tm;
uint32_t cur_ts;
uint8_t week, i;
uint8_t creator, port;
uint8_t bm_creator;
ass_info = (iot_brm_nv_ass_chk_info_t *)
iot_brm_nv_get_section(iot_brm_nv_id_ass_chk_info);
os_mem_set(ass_info, 0x0, sizeof(*ass_info));
iot_brm_rtc_get_time(&tm, &week);
ass_info->ts = iot_brm_rtctime_to_time(&tm);
cur_ts = (uint32_t)(os_boot_time64() / 1000);
ass_info->time_cons = (uint16_t)(cur_ts - ccb->start_ts);
ass_info->phase1_wire_st = ccb->wire_chk_st[0];
ass_info->phase2_wire_st = ccb->wire_chk_st[1];
ass_info->phase3_wire_st = ccb->wire_chk_st[2];
iot_brm_printf("%s cons = %lus, wire[%lu %lu %lu]\n",
__FUNCTION__, ass_info->time_cons,
ass_info->phase1_wire_st, ass_info->phase2_wire_st,
ass_info->phase3_wire_st);
bm_creator = (1 << IOT_BRM_OPERATOR_EXT) | (1 << IOT_BRM_OPERATOR_INTERNAL);
iot_brm_node_rm_by_creator(bm_creator);
for (i = 0; i < IOT_BRM_SUPPORT_NODE_MAX; i++) {
node = &ccb->node_ent[i];
if (!iot_mac_addr_valid(node->addr)) {
break;
}
iot_mac_addr_cpy(ass_info->ent[i].addr, node->addr);
ass_info->ent[i].pid = node->pid;
if (node->state == IOT_BRM_ASS_NODE_CHK_STATE_OK) {
/* if the node assembly check is ok, add it to the node list */
ass_info->ent[i].ass_ok = 1;
if (node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_3761
|| node->pid == IOT_BRM_PROTO_TYPE_645_2007_BY_69845
|| node->pid == IOT_BRM_PROTO_TYPE_69845_BY_3761
|| node->pid == IOT_BRM_PROTO_TYPE_69845_BY_69845)
creator = IOT_BRM_OPERATOR_EXT;
else
creator = IOT_BRM_OPERATOR_INTERNAL;
if (!ccb->is_plc_con) {
port = IOT_BRM_PORT_RS485;
} else if (ccb->plc_mode ==
PROTO_645_EXT_BRM_ASS_CHK_PLC_MODE_SAME) {
port = IOT_BRM_PORT_PLC_WEAK;
} else {
port = IOT_BRM_PORT_PLC;
}
iot_brm_node_add(node->addr, port, node->pid,
iot_brm_bid_to_baud(node->bid), NULL, NULL, NULL, creator);
} else if (node->state == IOT_BRM_ASS_NODE_CHK_STATE_NOT_OK) {
ass_info->ent[i].ass_ok = 0;
} else {
IOT_ASSERT(0);
}
ass_info->ent[i].bid = node->bid;
ass_info->ent[i].is_plc_chk = ccb->is_plc_con;
iot_brm_printf("|--ass addr %02x%02x%02x%02x%02x%02x %s, pid %lu, "
"baud %lu\n", node->addr[5], node->addr[4], node->addr[3],
node->addr[2], node->addr[1], node->addr[0],
ass_info->ent[i].ass_ok ? "yes" : "no",
node->pid, iot_brm_bid_to_baud((uint8_t)node->bid));
}
iot_brm_nv_update_section(iot_brm_nv_id_ass_chk_info);
iot_brm_sync_node_list_to_sg_app();
}
static uint8_t iot_brm_ass_other_check_is_done(void)
{
if (g_ass_ccb.wire_chk_state == IOT_BRM_ASS_WIRE_CHK_STATE_DONE) {
return 1;
}
return 0;
}
void iot_brm_ass_check_handle(iot_brm_ass_event_t event,
void *data)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
uint32_t ret, dur = 50, accept;
switch (ccb->state) {
case IOT_BRM_ASS_CHK_STATE_RECV_WAIT:
{
switch (event) {
case iot_brm_ass_event_data:
{
if (data)
iot_pkt_free((iot_pkt_t *)data);
goto out;
}
case iot_brm_ass_event_timer:
{
iot_brm_ass_check_proto_table_init();
/* start assembly check */
if (!ccb->is_plc_con)
ccb->state = IOT_BRM_ASS_CHK_STATE_NODE_READY;
else {
/* make the local device work in the fixed band */
iot_brm_set_fb(ccb->plc_band);
ccb->plc_cfg_cmd_cnt = 0;
ccb->state = IOT_BRM_ASS_CHK_STATE_NODE_PLC;
}
break;
}
default:
IOT_ASSERT(0);
break;
}
break;
}
case IOT_BRM_ASS_CHK_STATE_NODE_READY:
{
switch (event) {
case iot_brm_ass_event_data:
{
if (data)
iot_pkt_free((iot_pkt_t *)data);
goto out;
}
case iot_brm_ass_event_timer:
{
ret = iot_brm_ass_node_check_issue(&dur);
if (ret == ERR_OK) {
/* command issued success, set timeout, wait for command
* response.
*/
ccb->state = IOT_BRM_ASS_CHK_STATE_NODE_WAIT;
} else if (ret == ERR_NOT_EXIST) {
/* the node assembly check is done, and the results
* switch to other item check.
*/
ccb->state = IOT_BRM_ASS_CHK_STATE_WAIT_OTHER;
}
break;
}
default:
IOT_ASSERT(0);
break;
}
break;
}
case IOT_BRM_ASS_CHK_STATE_NODE_WAIT:
{
switch (event) {
case iot_brm_ass_event_data:
{
accept = iot_brm_ass_node_check_resp_handle(
(iot_pkt_t *)data);
if (!accept) {
goto out;
}
os_stop_timer(ccb->chk_timer);
iot_brm_clean_msg(IOT_BRM_MSG_TYPE_TIMER,
IOT_BRM_MSG_ID_TIMER_ASS_CHK);
ccb->state = IOT_BRM_ASS_CHK_STATE_NODE_READY;
break;
}
case iot_brm_ass_event_timer:
{
iot_brm_ass_node_check_resp_handle(NULL);
ccb->state = IOT_BRM_ASS_CHK_STATE_NODE_READY;
break;
}
default:
IOT_ASSERT(0);
break;
}
break;
}
case IOT_BRM_ASS_CHK_STATE_NODE_PLC:
{
switch (event) {
case iot_brm_ass_event_data:
{
iot_brm_ass_node_check_resp_handle((iot_pkt_t *)data);
goto out;
}
case iot_brm_ass_event_timer:
{
ret = iot_brm_ass_node_check_issue_on_plc();
if (ret == ERR_NOT_EXIST) {
/* the plc node assembly check is done, and the results
* switch to other item check.
*/
ccb->state = IOT_BRM_ASS_CHK_STATE_WAIT_OTHER;
}
dur = 1000;
break;
}
default:
IOT_ASSERT(0);
break;
}
break;
}
case IOT_BRM_ASS_CHK_STATE_WAIT_OTHER:
{
switch (event) {
case iot_brm_ass_event_data:
{
if (data)
iot_pkt_free((iot_pkt_t *)data);
goto out;
}
case iot_brm_ass_event_timer:
{
if (iot_brm_ass_other_check_is_done()) {
/* save assembly check results */
iot_brm_ass_check_result_save();
ccb->state = IOT_BRM_ASS_CHK_STATE_DONE;
dur = 10000;
break;
}
dur = 1000;
break;
}
default:
IOT_ASSERT(0);
break;
}
break;
}
case IOT_BRM_ASS_CHK_STATE_DONE:
{
switch (event) {
case iot_brm_ass_event_data:
{
iot_pkt_free((iot_pkt_t *)data);
goto out;
}
case iot_brm_ass_event_timer:
{
iot_system_restart(IOT_SYS_RST_REASON_CT2_REQ);
break;
}
default:
IOT_ASSERT(0);
break;
}
break;
}
default:
IOT_ASSERT(0);
goto out;
}
os_start_timer(ccb->chk_timer, dur);
out:
return;
}
static uint8_t iot_brm_ass_node_add(uint8_t *addr)
{
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
iot_brm_ass_node_entry_t *node = NULL;
uint8_t i;
if (!proto_645_pm_addr_valid(addr))
return ERR_INVAL;
for (i = 0; i < IOT_BRM_SUPPORT_NODE_MAX; i++) {
node = ccb->node_ent + i;
if (!iot_mac_addr_valid(node->addr)) {
break;
} else if (iot_mac_addr_cmp(node->addr, addr) == 1) {
return ERR_EXIST;
}
}
if (i == IOT_BRM_SUPPORT_NODE_MAX)
return ERR_NOMEM;
iot_mac_addr_cpy(node->addr, addr);
node->pid = IOT_BRM_PROTO_TYPE_TRANSPARENT;
node->bid = 0;
node->state = IOT_BRM_ASS_NODE_CHK_STATE_PEND;
return ERR_OK;
}
static uint8_t iot_brm_ass_proto_band_to_plc_lib_band(uint8_t band)
{
uint8_t plc_band;
switch (band) {
case PROTO_645_EXT_BRM_ASS_CHK_PLC_BAND_1:
plc_band = PLC_LIB_FREQ_BAND_1;
break;
case PROTO_645_EXT_BRM_ASS_CHK_PLC_BAND_8:
plc_band = PLC_LIB_FREQ_BAND_8;
break;
case PROTO_645_EXT_BRM_ASS_CHK_PLC_BAND_2:
default:
plc_band = PLC_LIB_FREQ_BAND_2;
break;
}
return plc_band;
}
uint8_t iot_brm_ass_handle_check_req(uint8_t *ds,
uint8_t len)
{
proto_645_ext_brm_ass_chk_req_t *req;
iot_time_tm_t tm;
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
uint8_t i, reason = 0;
iot_brm_terminal_cfg_t cfg;
if (len < sizeof(*req)) {
reason = 1;
goto out;
}
req = (proto_645_ext_brm_ass_chk_req_t *)ds;
len -= sizeof(*req);
if (len < req->cnt * sizeof(req->addr[0])) {
reason = 2;
goto out;
}
if (req->check_en_for_3761
&& iot_brm_get_terminal_cfg(&cfg) != ERR_OK) {
reason = 4;
goto out;
}
switch (ccb->state) {
case IOT_BRM_ASS_CHK_STATE_IDLE:
{
ccb->wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_DIRECT;
ccb->trans_port_lock = 0;
ccb->b_idx_lock = 0;
ccb->is_plc_con = req->is_plc_con;
ccb->check_en_for_3761 = req->check_en_for_3761;
ccb->plc_mode = req->plc_mode;
ccb->plc_band = iot_brm_ass_proto_band_to_plc_lib_band(req->band);
tm.tm_sec = iot_bcd_to_byte(req->tm.second);
tm.tm_min = iot_bcd_to_byte(req->tm.minute);
tm.tm_hour = iot_bcd_to_byte(req->tm.hour);
tm.tm_mday = iot_bcd_to_byte(req->tm.day);
tm.tm_mon = iot_bcd_to_byte(req->tm.month);
tm.tm_year = iot_bcd_to_byte(req->tm.year) + 2000;
iot_brm_rtc_set_time(&tm);
iot_brm_brk_correct_time((proto_645_corr_time_t*)&(req->tm));
iot_brm_set_gps_info((iot_brm_location_unit_t *)&req->latitude,
(iot_brm_location_unit_t *)&req->longitude);
iot_brm_printf("%s time %lu-%lu-%lu %lu:%lu:%lu, location [%c%1d%1d%1d."
"%1d%1d.%1d%1d%1d%1d, %c%1d%1d%1d.%1d%1d.%1d%1d%1d%1d], conn %lu, "
"ratio[%lu %lu]\n", __FUNCTION__, tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
req->latitude.symbol_3 ? 'S':'N', req->latitude.degree_2,
req->latitude.degree_1, req->latitude.degree_0,
req->latitude.min_1, req->latitude.min_0, req->latitude.sec_3,
req->latitude.sec_2, req->latitude.sec_1, req->latitude.sec_0,
req->longitude.symbol_3 ? 'W':'E', req->longitude.degree_2,
req->longitude.degree_1, req->longitude.degree_0,
req->longitude.min_1, req->longitude.min_0, req->longitude.sec_3,
req->longitude.sec_2, req->longitude.sec_1, req->longitude.sec_0,
req->is_plc_con, req->v_ratio, req->i_ratio);
ccb->start_ts = (uint32_t)(os_boot_time64() / 1000);
iot_brm_set_meter_ratio((uint32_t)req->v_ratio,
(uint32_t)req->i_ratio);
if ((ccb->is_plc_con && iot_brm_port_is_exist(IOT_BRM_PORT_PLC_WEAK))
|| (!ccb->is_plc_con && iot_brm_port_is_exist(IOT_BRM_PORT_RS485))) {
for (i = 0; i < req->cnt; i++) {
iot_brm_ass_node_add(req->addr[i]);
}
iot_brm_rs485_set_check_state();
if (ccb->is_plc_con) {
iot_brm_set_whitelist_enable();
}
ccb->state = IOT_BRM_ASS_CHK_STATE_RECV_WAIT;
} else {
ccb->state = IOT_BRM_ASS_CHK_STATE_WAIT_OTHER;
}
os_start_timer(ccb->chk_timer, IOT_BRM_ASS_REV_ASS_INFO_TIMEOUT);
iot_plc_led_request(IOT_PLC_LED_CHECK);
break;
}
case IOT_BRM_ASS_CHK_STATE_RECV_WAIT:
{
for (i = 0; i < req->cnt; i++) {
iot_brm_ass_node_add(req->addr[i]);
}
/* restart timer waiting for more assembly info */
os_start_timer(ccb->chk_timer, IOT_BRM_ASS_REV_ASS_INFO_TIMEOUT);
iot_brm_clean_msg(IOT_BRM_MSG_TYPE_TIMER, IOT_BRM_MSG_ID_TIMER_ASS_CHK);
break;
}
case IOT_BRM_ASS_CHK_STATE_NODE_READY:
case IOT_BRM_ASS_CHK_STATE_NODE_WAIT:
case IOT_BRM_ASS_CHK_STATE_NODE_PLC:
case IOT_BRM_ASS_CHK_STATE_WAIT_OTHER:
case IOT_BRM_ASS_CHK_STATE_DONE:
default:
{
reason = 3;
goto out;
}
}
out:
if (reason) {
iot_printf("%s fail, reason %lu\n", __FUNCTION__, reason);
return ERR_FAIL;
}
return ERR_OK;
}
uint8_t iot_brm_ass_check_fill_result(uint8_t *ds, uint8_t len,
uint8_t start_index, uint8_t query_cnt)
{
proto_645_ext_brm_ass_chk_rsp_t *rsp;
iot_time_tm_t tm;
iot_brm_ass_chk_ctrl_t *ccb = &g_ass_ccb;
uint8_t i, cnt = 0, week;
iot_brm_nv_ass_chk_info_t *ass_info;
rsp = (proto_645_ext_brm_ass_chk_rsp_t *)ds;
iot_brm_meter_info_t *m = iot_brm_load_meter_info();
os_mem_set(rsp, 0x0, sizeof(*rsp));
iot_brm_get_gps_info((iot_brm_location_unit_t*)&rsp->latitude,
(iot_brm_location_unit_t*)&rsp->longitude);
iot_brm_rtc_get_time(&tm, &week);
proto_645_rtctime_to_YYMMDDhhmmss(&tm, (uint8_t*)&rsp->tm);
len -= sizeof(*rsp);
switch (ccb->state) {
case IOT_BRM_ASS_CHK_STATE_NODE_READY:
case IOT_BRM_ASS_CHK_STATE_NODE_WAIT:
case IOT_BRM_ASS_CHK_STATE_WAIT_OTHER:
case IOT_BRM_ASS_CHK_STATE_NODE_PLC:
case IOT_BRM_ASS_CHK_STATE_RECV_WAIT:
{
/* checking in progress */
rsp->state = 1;
rsp->time_cons = (uint16_t)(((uint32_t)(os_boot_time64() / 1000)) -
ccb->start_ts);
goto out;
}
case IOT_BRM_ASS_CHK_STATE_IDLE:
case IOT_BRM_ASS_CHK_STATE_DONE:
{
break;
}
default:
IOT_ASSERT(0);
break;
}
ass_info = (iot_brm_nv_ass_chk_info_t *)
iot_brm_nv_get_section(iot_brm_nv_id_ass_chk_info);
if (ass_info->ts == 0
&& ass_info->time_cons == 0) {
goto out;
}
rsp->phase1_wire_st = ass_info->phase1_wire_st;
rsp->phase2_wire_st = ass_info->phase2_wire_st;
rsp->phase3_wire_st = ass_info->phase3_wire_st;
rsp->state = 2;
rsp->time_cons = ass_info->time_cons;
for (i = 0; i < IOT_BRM_SUPPORT_NODE_MAX; i++) {
if (!iot_mac_addr_valid(ass_info->ent[i].addr)) {
break;
}
rsp->total_cnt++;
if (i < start_index) {
continue;
}
if (len < sizeof(rsp->result[0]) || cnt >= query_cnt) {
rsp->more = 1;
continue;
}
len -= sizeof(rsp->result[0]);
iot_mac_addr_cpy(rsp->result[cnt].addr,
ass_info->ent[i].addr);
rsp->result[cnt].result = ass_info->ent[i].ass_ok;
if (ass_info->ent[i].pid == IOT_BRM_PROTO_TYPE_645_2007_BY_3761
|| ass_info->ent[i].pid == IOT_BRM_PROTO_TYPE_645_2007_BY_69845)
rsp->result[cnt].pid = IOT_BRM_PROTO_TYPE_645_2007;
else if (ass_info->ent[i].pid == IOT_BRM_PROTO_TYPE_69845_BY_3761
|| ass_info->ent[i].pid == IOT_BRM_PROTO_TYPE_69845_BY_69845)
rsp->result[cnt].pid = IOT_BRM_PROTO_TYPE_69845;
else
rsp->result[cnt].pid = ass_info->ent[i].pid ;
rsp->result[cnt].bid = ass_info->ent[i].bid;
rsp->result[cnt].rsvd = ass_info->ent[i].is_plc_chk;
cnt++;
}
out:
rsp->cnt = cnt;
rsp->i_ratio = (uint8_t)m->i_ratio;
rsp->v_ratio = (uint8_t)m->v_ratio;
return sizeof(*rsp) + cnt * sizeof(rsp->result[0]);
}
static void iot_brm_ass_check_timer_func(timer_id_t timer_id,
void *arg)
{
(void)timer_id;
iot_brm_post_msg(IOT_BRM_MSG_TYPE_TIMER,
IOT_BRM_MSG_ID_TIMER_ASS_CHK,
IOT_BRM_MSG_QUEUE_LP, arg, 0);
}
static void iot_brm_ass_plc_mr_recv(void *arg, uint16_t ext_sn,
uint8_t *addr, iot_pkt_t *pkt, uint16_t rsp_bm, iot_brm_mr_type_t type)
{
(void)arg;
(void)ext_sn;
(void)rsp_bm;
(void)type;
uint8_t *_addr;
if (!pkt) {
pkt = iot_pkt_alloc(IOT_MAC_ADDR_LEN, IOT_BRM_MID);
if (pkt == NULL) {
IOT_ASSERT(0);
}
iot_pkt_reserve(pkt, IOT_MAC_ADDR_LEN);
}
if (iot_pkt_block_len(pkt, IOT_PKT_BLOCK_HEAD) <
IOT_MAC_ADDR_LEN) {
IOT_ASSERT(0);
}
_addr = iot_pkt_data(pkt);
_addr -= IOT_MAC_ADDR_LEN;
iot_mac_addr_cpy(_addr, addr);
iot_brm_data_print("iot_brm_ass_plc_mr_recv addr", _addr, IOT_MAC_ADDR_LEN);
iot_brm_data_print("iot_brm_ass_plc_mr_recv data", iot_pkt_data(pkt),
iot_pkt_data_len(pkt));
iot_brm_ass_check_handle(iot_brm_ass_event_data, pkt);
}
void iot_brm_ass_init(void)
{
g_ass_ccb.chk_timer= os_create_timer(IOT_BRM_MID,
false, iot_brm_ass_check_timer_func, NULL);
if (g_ass_ccb.chk_timer == 0) {
goto out;
}
g_ass_ccb.custom_id = iot_brm_mr_buf_customer_register(
iot_brm_ass_plc_mr_recv, NULL);
if (g_ass_ccb.custom_id == IOT_BRM_MR_CUSTOMER_INVALID) {
goto err_1;
}
g_ass_ccb.state = IOT_BRM_ASS_CHK_STATE_IDLE;
g_ass_ccb.wire_chk_cnt = 0;
g_ass_ccb.wire_chk_state = IOT_BRM_ASS_WIRE_CHK_STATE_IDLE;
goto out;
err_1:
os_delete_timer(g_ass_ccb.chk_timer);
g_ass_ccb.chk_timer =0;
out:
return;
}
#endif /* IOT_BRM_ENABLE */