Files

339 lines
12 KiB
C

/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include "connection/mqtt_client.h"
#include "errcodes.h"
#include "version.h"
#include "json/neu_json_mqtt.h"
#include "json/neu_json_rw.h"
#include "parser/neu_json_group_config.h"
#include "parser/neu_json_node.h"
#include "parser/neu_json_tag.h"
#include "monitor.h"
#include "mqtt_handle.h"
static char *generate_heartbeat_json(neu_plugin_t *plugin, UT_array *states)
{
(void) plugin;
char * version = NEURON_VERSION;
neu_json_states_head_t header = { .version = version,
.timpstamp = global_timestamp };
neu_json_states_t json = { 0 };
char * json_str = NULL;
json.n_state = utarray_len(states);
json.states = calloc(json.n_state, sizeof(neu_json_node_state_t));
if (NULL == json.states) {
return NULL;
}
utarray_foreach(states, neu_nodes_state_t *, state)
{
int index = utarray_eltidx(states, state);
json.states[index].node = state->node;
json.states[index].link = state->state.link;
json.states[index].running = state->state.running;
}
neu_json_encode_with_mqtt(&json, neu_json_encode_states_resp, &header,
neu_json_encode_state_header_resp, &json_str);
free(json.states);
return json_str;
}
static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
void *data, char **topic_p)
{
char *json_str = NULL;
union {
neu_json_add_node_req_t add_node;
neu_json_del_node_req_t del_node;
neu_json_node_ctl_req_t node_ctl;
neu_json_node_setting_req_t node_setting;
neu_json_add_group_config_req_t add_grp;
neu_json_del_group_config_req_t del_grp;
neu_json_update_group_req_t update_grp;
neu_json_add_tags_req_t add_tags;
neu_json_del_tags_req_t del_tags;
} json_req = {};
switch (event) {
case NEU_REQ_ADD_NODE_EVENT: {
neu_req_add_node_t *add_node = data;
json_req.add_node.name = add_node->node;
json_req.add_node.plugin = add_node->plugin;
*topic_p = plugin->config->node_add_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_add_node_req,
&json_str);
break;
}
case NEU_REQ_DEL_NODE_EVENT: {
neu_req_del_node_t *del_node = data;
json_req.del_node.name = del_node->node;
*topic_p = plugin->config->node_del_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_del_node_req,
&json_str);
break;
}
case NEU_REQ_NODE_CTL_EVENT: {
neu_req_node_ctl_t *node_ctl = data;
json_req.node_ctl.node = node_ctl->node;
json_req.node_ctl.cmd = node_ctl->ctl;
*topic_p = plugin->config->node_ctl_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_node_ctl_req,
&json_str);
break;
}
case NEU_REQ_NODE_SETTING_EVENT: {
neu_req_node_setting_t *node_setting = data;
json_req.node_setting.node = node_setting->node;
json_req.node_setting.setting = node_setting->setting;
*topic_p = plugin->config->node_setting_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_node_setting_req,
&json_str);
break;
}
case NEU_REQ_ADD_GROUP_EVENT: {
neu_req_add_group_t *add_grp = data;
json_req.add_grp.node = add_grp->driver;
json_req.add_grp.group = add_grp->group;
json_req.add_grp.interval = add_grp->interval;
*topic_p = plugin->config->group_add_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_add_group_config_req,
&json_str);
break;
}
case NEU_REQ_DEL_GROUP_EVENT: {
neu_req_del_group_t *del_grp = data;
json_req.del_grp.node = del_grp->driver;
json_req.del_grp.group = del_grp->group;
*topic_p = plugin->config->group_del_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_del_group_config_req,
&json_str);
break;
}
case NEU_REQ_UPDATE_GROUP_EVENT: {
neu_req_update_group_t *update_grp = data;
json_req.update_grp.node = update_grp->driver;
json_req.update_grp.group = update_grp->group;
json_req.update_grp.interval = update_grp->interval;
*topic_p = plugin->config->group_update_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_add_group_config_req,
&json_str);
break;
}
case NEU_REQ_ADD_TAG_EVENT:
case NEU_REQ_UPDATE_TAG_EVENT: {
neu_req_add_tag_t *add_tag = data;
json_req.add_tags.node = add_tag->driver;
json_req.add_tags.group = add_tag->group;
json_req.add_tags.n_tag = add_tag->n_tag;
json_req.add_tags.tags =
calloc(add_tag->n_tag, sizeof(*json_req.add_tags.tags));
if (NULL == json_req.add_tags.tags) {
plog_error(plugin, "calloc fail");
break;
}
for (uint16_t i = 0; i < add_tag->n_tag; ++i) {
json_req.add_tags.tags[i].type = add_tag->tags[i].type;
json_req.add_tags.tags[i].name = add_tag->tags[i].name;
json_req.add_tags.tags[i].attribute = add_tag->tags[i].attribute;
json_req.add_tags.tags[i].address = add_tag->tags[i].address;
json_req.add_tags.tags[i].decimal = add_tag->tags[i].decimal;
json_req.add_tags.tags[i].precision = add_tag->tags[i].precision;
json_req.add_tags.tags[i].description =
add_tag->tags[i].description;
if (neu_tag_attribute_test(&add_tag->tags[i],
NEU_ATTRIBUTE_STATIC)) {
neu_tag_get_static_value_json(&add_tag->tags[i],
&json_req.add_tags.tags[i].t,
&json_req.add_tags.tags[i].value);
} else {
json_req.add_tags.tags[i].t = NEU_JSON_UNDEFINE;
}
}
*topic_p = NEU_REQ_ADD_TAG_EVENT == event
? plugin->config->tag_add_topic
: plugin->config->tag_update_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_add_tags_req,
&json_str);
free(json_req.add_tags.tags);
break;
}
case NEU_REQ_DEL_TAG_EVENT: {
neu_req_del_tag_t *del_tag = data;
json_req.del_tags.node = del_tag->driver;
json_req.del_tags.group = del_tag->group;
json_req.del_tags.n_tags = del_tag->n_tag;
json_req.del_tags.tags =
calloc(del_tag->n_tag, sizeof(*json_req.del_tags.tags));
if (NULL == json_req.del_tags.tags) {
plog_error(plugin, "calloc fail");
break;
}
for (uint16_t i = 0; i < del_tag->n_tag; ++i) {
json_req.del_tags.tags[i] = del_tag->tags[i];
}
*topic_p = plugin->config->tag_del_topic;
neu_json_encode_by_fn(&json_req, neu_json_encode_del_tags_req,
&json_str);
free(json_req.del_tags.tags);
break;
}
default: {
plog_error(plugin, "unsupported event:%s",
neu_reqresp_type_string(event));
return NULL;
}
}
return json_str;
}
static void publish_cb(int errcode, neu_mqtt_qos_e qos, char *topic,
uint8_t *payload, uint32_t len, void *data)
{
(void) qos;
(void) topic;
(void) len;
neu_plugin_t *plugin = data;
neu_adapter_update_metric_cb_t update_metric =
plugin->common.adapter_callbacks->update_metric;
if (0 == errcode) {
update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSGS_TOTAL, 1,
NULL);
} else {
update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSG_ERRORS_TOTAL,
1, NULL);
}
free(payload);
}
static inline int publish(neu_plugin_t *plugin, neu_mqtt_qos_e qos, char *topic,
char *payload, size_t payload_len)
{
neu_adapter_update_metric_cb_t update_metric =
plugin->common.adapter_callbacks->update_metric;
int rv = neu_mqtt_client_publish(
plugin->mqtt_client, qos, topic, (uint8_t *) payload,
(uint32_t) payload_len, plugin, publish_cb);
if (0 != rv) {
plog_error(plugin, "pub [%s, QoS%d] fail", topic, qos);
update_metric(plugin->common.adapter, NEU_METRIC_SEND_MSG_ERRORS_TOTAL,
1, NULL);
free(payload);
rv = NEU_ERR_MQTT_PUBLISH_FAILURE;
}
return rv;
}
int handle_nodes_state(neu_plugin_t *plugin, neu_reqresp_nodes_state_t *states)
{
int rv = 0;
char *json_str = NULL;
if (NULL == plugin->mqtt_client) {
rv = NEU_ERR_MQTT_IS_NULL;
goto end;
}
if (!neu_mqtt_client_is_connected(plugin->mqtt_client)) {
// cache disable and we are disconnected
rv = NEU_ERR_MQTT_FAILURE;
goto end;
}
json_str = generate_heartbeat_json(plugin, states->states);
if (NULL == json_str) {
plog_error(plugin, "generate heartbeat json fail");
rv = NEU_ERR_EINTERNAL;
goto end;
}
char * topic = plugin->config->heartbeat_topic;
neu_mqtt_qos_e qos = NEU_MQTT_QOS0;
rv = publish(plugin, qos, topic, json_str, strlen(json_str));
json_str = NULL;
end:
utarray_free(states->states);
return rv;
}
int handle_events(neu_plugin_t *plugin, neu_reqresp_type_e event, void *data)
{
int rv = 0;
char *json_str = NULL;
char *topic = NULL;
if (NULL == plugin->mqtt_client) {
rv = NEU_ERR_MQTT_IS_NULL;
goto end;
}
if (!neu_mqtt_client_is_connected(plugin->mqtt_client)) {
// cache disable and we are disconnected
rv = NEU_ERR_MQTT_FAILURE;
goto end;
}
json_str = generate_event_json(plugin, event, data, &topic);
if (NULL == json_str) {
plog_error(plugin, "generate event:%s json fail",
neu_reqresp_type_string(event));
rv = NEU_ERR_EINTERNAL;
goto end;
}
neu_mqtt_qos_e qos = NEU_MQTT_QOS0;
rv = publish(plugin, qos, topic, json_str, strlen(json_str));
json_str = NULL;
end:
if (NEU_REQ_NODE_SETTING_EVENT == event) {
free(((neu_req_node_setting_t *) data)->setting);
} else if (NEU_REQ_ADD_TAG_EVENT == event ||
NEU_REQ_UPDATE_TAG_EVENT == event) {
neu_req_add_tag_t *add_tag = data;
for (uint16_t i = 0; i < add_tag->n_tag; i++) {
neu_tag_fini(&add_tag->tags[i]);
}
free(add_tag->tags);
} else if (NEU_REQ_DEL_TAG_EVENT == event) {
neu_req_del_tag_t *del_tag = data;
for (uint16_t i = 0; i < del_tag->n_tag; i++) {
free(del_tag->tags[i]);
}
free(del_tag->tags);
}
return rv;
}