Files
2025-06-26 19:05:07 -07:00

636 lines
20 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 <jansson.h>
#include "errcodes.h"
#include "plugin.h"
#include "type.h"
#include "datalayers_handle.h"
#include "datalayers_plugin.h"
#include "datalayers_plugin_intf.h"
void task_queue_init(task_queue_t *queue)
{
queue->head = NULL;
queue->tail = NULL;
queue->size = 0;
queue->max_size = 0;
}
static inline db_write_task_t *task_new()
{
db_write_task_t *task = malloc(sizeof(db_write_task_t));
if (!task) {
return NULL;
}
memset(task, 0, sizeof(db_write_task_t));
return task;
}
static inline void task_free(db_write_task_t *task)
{
if (task == NULL) {
return;
}
if (task->freed) {
return;
}
task->freed = true;
utarray_free(task->int_tags);
utarray_free(task->float_tags);
utarray_free(task->bool_tags);
utarray_free(task->string_tags);
free(task);
}
void tasks_free(task_queue_t *queue)
{
db_write_task_t *task = queue->head;
while (task) {
db_write_task_t *next = task->next;
task_free(task);
task = next;
}
queue->head = NULL;
queue->tail = NULL;
queue->size = 0;
}
void task_queue_push(neu_plugin_t *plugin, db_write_task_t *task)
{
if (plugin->task_queue.size >= MAX_QUEUE_SIZE) {
db_write_task_t *old_task = plugin->task_queue.head;
plugin->task_queue.head = plugin->task_queue.head->next;
task_free(old_task);
plugin->task_queue.size--;
NEU_PLUGIN_UPDATE_DISCARDED_MSGS_METRIC(plugin, 1);
}
if (plugin->task_queue.tail) {
plugin->task_queue.tail->next = task;
}
plugin->task_queue.tail = task;
if (plugin->task_queue.head == NULL) {
plugin->task_queue.head = task;
}
plugin->task_queue.size++;
if (plugin->task_queue.size > plugin->task_queue.max_size) {
plugin->task_queue.max_size = plugin->task_queue.size;
NEU_PLUGIN_UPDATE_MAX_CACHED_QUEUE_SIZE_METRIC(
plugin, plugin->task_queue.max_size);
}
NEU_PLUGIN_UPDATE_CACHED_QUEUE_SIZE_METRIC(plugin, plugin->task_queue.size);
}
db_write_task_t *task_queue_pop(neu_plugin_t *plugin, task_queue_t *queue)
{
if (queue == NULL || queue->head == NULL || queue->size == 0) {
return NULL;
}
db_write_task_t *task = queue->head;
queue->head = queue->head->next;
if (queue->head == NULL) {
queue->tail = NULL;
}
queue->size--;
NEU_PLUGIN_UPDATE_METRIC(plugin, NEU_METRIC_SEND_MSGS_TOTAL, 1, NULL);
NEU_PLUGIN_UPDATE_CACHED_QUEUE_SIZE_METRIC(plugin, plugin->task_queue.size);
return task;
}
static void db_write_task_cb(db_write_task_t *task, neu_plugin_t *plugin)
{
int ret;
if (utarray_len(task->int_tags) > 0) {
ret = client_insert(plugin->client, INT_TYPE,
utarray_eltptr(task->int_tags, 0),
utarray_len(task->int_tags));
if (ret != 0) {
plog_error(plugin, "Failed to insert INT_TYPE data, disconnected");
goto handle_error;
}
}
if (utarray_len(task->float_tags) > 0) {
ret = client_insert(plugin->client, FLOAT_TYPE,
utarray_eltptr(task->float_tags, 0),
utarray_len(task->float_tags));
if (ret != 0) {
plog_error(plugin,
"Failed to insert FLOAT_TYPE data, disconnected");
goto handle_error;
}
}
if (utarray_len(task->bool_tags) > 0) {
ret = client_insert(plugin->client, BOOL_TYPE,
utarray_eltptr(task->bool_tags, 0),
utarray_len(task->bool_tags));
if (ret != 0) {
plog_error(plugin, "Failed to insert BOOL_TYPE data, disconnected");
goto handle_error;
}
}
if (utarray_len(task->string_tags) > 0) {
ret = client_insert(plugin->client, STRING_TYPE,
utarray_eltptr(task->string_tags, 0),
utarray_len(task->string_tags));
if (ret != 0) {
plog_error(plugin,
"Failed to insert STRING_TYPE data, disconnected");
goto handle_error;
}
}
return;
handle_error:
pthread_rwlock_wrlock(&plugin->plugin_mutex);
if (plugin->client) {
client_destroy(plugin->client);
plugin->client = NULL;
}
pthread_rwlock_unlock(&plugin->plugin_mutex);
}
void db_write_task_consumer(neu_plugin_t *plugin)
{
db_write_task_t *task = NULL;
while (1) {
pthread_mutex_lock(&plugin->queue_mutex);
while (plugin->task_queue.size == 0 &&
!plugin->consumer_thread_stop_flag) {
pthread_cond_wait(&plugin->queue_not_empty, &plugin->queue_mutex);
}
if (plugin->consumer_thread_stop_flag && plugin->task_queue.size == 0) {
pthread_mutex_unlock(&plugin->queue_mutex);
break;
}
task = task_queue_pop(plugin, &plugin->task_queue);
pthread_mutex_unlock(&plugin->queue_mutex);
if (task) {
db_write_task_cb(task, plugin);
task_free(task);
}
}
plog_notice(plugin, "Consumer thread exiting gracefully.\n");
}
static void tag_array_copy(void *dst, const void *src)
{
const datatag *src_tag = (const datatag *) src;
datatag * dst_tag = (datatag *) dst;
dst_tag->node_name = src_tag->node_name ? strdup(src_tag->node_name) : NULL;
dst_tag->group_name =
src_tag->group_name ? strdup(src_tag->group_name) : NULL;
dst_tag->tag = src_tag->tag ? strdup(src_tag->tag) : NULL;
dst_tag->value_type = src_tag->value_type;
switch (src_tag->value_type) {
case STRING_TYPE:
dst_tag->value.string_value = src_tag->value.string_value
? strdup(src_tag->value.string_value)
: NULL;
break;
case INT_TYPE:
dst_tag->value.int_value = src_tag->value.int_value;
break;
case FLOAT_TYPE:
dst_tag->value.float_value = src_tag->value.float_value;
break;
case BOOL_TYPE:
dst_tag->value.bool_value = src_tag->value.bool_value;
break;
default:
break;
}
}
static void tag_array_free(void *_elt)
{
datatag *elt = (datatag *) _elt;
free((void *) elt->node_name);
free((void *) elt->group_name);
free((void *) elt->tag);
if (elt->value_type == STRING_TYPE) {
free((void *) elt->value.string_value);
}
}
static void tag_array_init(void *_elt)
{
datatag *elt = (datatag *) _elt;
memset(elt, 0, sizeof(datatag));
}
static UT_icd ut_datatag_icd = { sizeof(datatag), tag_array_init,
tag_array_copy, tag_array_free };
void process_array_to_json_string(json_t * array,
neu_resp_tag_value_meta_t *tag_meta,
UT_array * string_tags,
neu_reqresp_trans_data_t * trans_data)
{
switch (tag_meta->value.type) {
case NEU_TYPE_ARRAY_INT8:
for (size_t i = 0; i < tag_meta->value.value.i8s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.i8s.i8s[i]));
}
break;
case NEU_TYPE_ARRAY_UINT8:
for (size_t i = 0; i < tag_meta->value.value.u8s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.u8s.u8s[i]));
}
break;
case NEU_TYPE_ARRAY_INT16:
for (size_t i = 0; i < tag_meta->value.value.i16s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.i16s.i16s[i]));
}
break;
case NEU_TYPE_ARRAY_UINT16:
for (size_t i = 0; i < tag_meta->value.value.u16s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.u16s.u16s[i]));
}
break;
case NEU_TYPE_ARRAY_INT32:
for (size_t i = 0; i < tag_meta->value.value.i32s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.i32s.i32s[i]));
}
break;
case NEU_TYPE_ARRAY_UINT32:
for (size_t i = 0; i < tag_meta->value.value.u32s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.u32s.u32s[i]));
}
break;
case NEU_TYPE_ARRAY_INT64:
for (size_t i = 0; i < tag_meta->value.value.i64s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.i64s.i64s[i]));
}
break;
case NEU_TYPE_ARRAY_UINT64:
for (size_t i = 0; i < tag_meta->value.value.u64s.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.u64s.u64s[i]));
}
break;
case NEU_TYPE_ARRAY_FLOAT:
for (size_t i = 0; i < tag_meta->value.value.f32s.length; ++i) {
json_array_append_new(
array, json_real(tag_meta->value.value.f32s.f32s[i]));
}
break;
case NEU_TYPE_ARRAY_DOUBLE:
for (size_t i = 0; i < tag_meta->value.value.f64s.length; ++i) {
json_array_append_new(
array, json_real(tag_meta->value.value.f64s.f64s[i]));
}
break;
case NEU_TYPE_ARRAY_STRING:
for (size_t i = 0; i < tag_meta->value.value.strs.length; ++i) {
json_array_append_new(
array, json_string(tag_meta->value.value.strs.strs[i]));
}
break;
case NEU_TYPE_ARRAY_BOOL:
for (size_t i = 0; i < tag_meta->value.value.bools.length; ++i) {
json_array_append_new(
array, json_boolean(tag_meta->value.value.bools.bools[i]));
}
break;
case NEU_TYPE_BYTES:
for (size_t i = 0; i < tag_meta->value.value.bytes.length; ++i) {
json_array_append_new(
array, json_integer(tag_meta->value.value.bytes.bytes[i]));
}
break;
default:
break;
}
char *json_str = json_dumps(array, JSON_COMPACT);
json_decref(array);
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .string_value = json_str },
STRING_TYPE };
utarray_push_back(string_tags, &tag);
free(json_str);
}
int handle_trans_data(neu_plugin_t * plugin,
neu_reqresp_trans_data_t *trans_data)
{
int rv = 0;
pthread_rwlock_rdlock(&plugin->plugin_mutex);
if (NULL == plugin->client) {
pthread_rwlock_unlock(&plugin->plugin_mutex);
return NEU_ERR_DATALAYERS_IS_NULL;
}
if (plugin->common.link_state != NEU_NODE_LINK_STATE_CONNECTED) {
pthread_rwlock_unlock(&plugin->plugin_mutex);
return NEU_ERR_PLUGIN_NOT_RUNNING;
}
const route_entry_t *route = route_tbl_get(
&plugin->route_tbl, trans_data->driver, trans_data->group);
if (NULL == route) {
plog_error(plugin, "no route for driver:%s group:%s",
trans_data->driver, trans_data->group);
pthread_rwlock_unlock(&plugin->plugin_mutex);
return NEU_ERR_GROUP_NOT_SUBSCRIBE;
}
UT_array *int_tags = NULL, *float_tags = NULL, *bool_tags = NULL,
*string_tags = NULL;
utarray_new(int_tags, &ut_datatag_icd);
utarray_new(float_tags, &ut_datatag_icd);
utarray_new(bool_tags, &ut_datatag_icd);
utarray_new(string_tags, &ut_datatag_icd);
bool has_valid_tags = false;
utarray_foreach(trans_data->tags, neu_resp_tag_value_meta_t *, tag_meta)
{
if (tag_meta->value.type == NEU_TYPE_ERROR) {
continue;
}
has_valid_tags = true;
switch (tag_meta->value.type) {
case NEU_TYPE_BIT: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.u8 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_INT8: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.i8 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_UINT8: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.u8 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_INT16: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.i16 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_UINT16: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.u16 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_INT32: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.i32 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_UINT32: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.u32 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_INT64: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.i64 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_UINT64: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .int_value = tag_meta->value.value.u64 },
INT_TYPE };
utarray_push_back(int_tags, &tag);
} break;
case NEU_TYPE_FLOAT: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .float_value = tag_meta->value.value.f32 },
FLOAT_TYPE };
utarray_push_back(float_tags, &tag);
} break;
case NEU_TYPE_DOUBLE: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .float_value = tag_meta->value.value.d64 },
FLOAT_TYPE };
utarray_push_back(float_tags, &tag);
} break;
case NEU_TYPE_BOOL: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .bool_value = tag_meta->value.value.boolean },
BOOL_TYPE };
utarray_push_back(bool_tags, &tag);
} break;
case NEU_TYPE_STRING:
case NEU_TYPE_DATA_AND_TIME:
case NEU_TYPE_TIME: {
datatag tag = { trans_data->driver,
trans_data->group,
tag_meta->tag,
{ .string_value = tag_meta->value.value.str },
STRING_TYPE };
utarray_push_back(string_tags, &tag);
} break;
case NEU_TYPE_BYTES:
case NEU_TYPE_ARRAY_BOOL:
case NEU_TYPE_ARRAY_INT8:
case NEU_TYPE_ARRAY_UINT8:
case NEU_TYPE_ARRAY_INT16:
case NEU_TYPE_ARRAY_UINT16:
case NEU_TYPE_ARRAY_INT32:
case NEU_TYPE_ARRAY_UINT32:
case NEU_TYPE_ARRAY_INT64:
case NEU_TYPE_ARRAY_UINT64:
case NEU_TYPE_ARRAY_FLOAT:
case NEU_TYPE_ARRAY_DOUBLE: {
json_t *array = json_array();
process_array_to_json_string(array, tag_meta, string_tags,
trans_data);
} break;
default:
break;
}
}
if (!has_valid_tags) {
utarray_free(int_tags);
utarray_free(float_tags);
utarray_free(bool_tags);
utarray_free(string_tags);
pthread_rwlock_unlock(&plugin->plugin_mutex);
return rv;
}
db_write_task_t *task = task_new();
task->int_tags = int_tags;
task->float_tags = float_tags;
task->bool_tags = bool_tags;
task->string_tags = string_tags;
pthread_rwlock_unlock(&plugin->plugin_mutex);
pthread_mutex_lock(&plugin->queue_mutex);
task_queue_push(plugin, task);
pthread_cond_signal(&plugin->queue_not_empty);
pthread_mutex_unlock(&plugin->queue_mutex);
return rv;
}
int handle_subscribe_group(neu_plugin_t *plugin, neu_req_subscribe_t *sub_info)
{
int rv = 0;
rv = route_tbl_add_new(&plugin->route_tbl, sub_info->driver,
sub_info->group);
if (0 != rv) {
plog_error(plugin, "route driver:%s group:%s fail, `%s`",
sub_info->driver, sub_info->group, sub_info->params);
goto end;
}
plog_notice(plugin, "route driver:%s group:%s", sub_info->driver,
sub_info->group);
end:
free(sub_info->params);
return rv;
}
int handle_unsubscribe_group(neu_plugin_t * plugin,
neu_req_unsubscribe_t *unsub_info)
{
route_tbl_del(&plugin->route_tbl, unsub_info->driver, unsub_info->group);
plog_notice(plugin, "del route driver:%s group:%s", unsub_info->driver,
unsub_info->group);
return 0;
}
int handle_del_group(neu_plugin_t *plugin, neu_req_del_group_t *req)
{
route_tbl_del(&plugin->route_tbl, req->driver, req->group);
plog_notice(plugin, "del route driver:%s group:%s", req->driver,
req->group);
return 0;
}
int handle_update_group(neu_plugin_t *plugin, neu_req_update_group_t *req)
{
route_tbl_update_group(&plugin->route_tbl, req->driver, req->group,
req->new_name);
plog_notice(plugin, "update route driver:%s group:%s to %s", req->driver,
req->group, req->new_name);
return 0;
}
int handle_update_driver(neu_plugin_t *plugin, neu_req_update_node_t *req)
{
route_tbl_update_driver(&plugin->route_tbl, req->node, req->new_name);
plog_notice(plugin, "update route driver:%s to %s", req->node,
req->new_name);
return 0;
}
int handle_del_driver(neu_plugin_t *plugin, neu_reqresp_node_deleted_t *req)
{
route_tbl_del_driver(&plugin->route_tbl, req->node);
plog_notice(plugin, "delete route driver:%s", req->node);
return 0;
}