mirror of
https://github.com/emqx/neuron.git
synced 2026-10-18 05:36:19 +08:00
310 lines
9.8 KiB
C
310 lines
9.8 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 "modbus_point.h"
|
|
#include "modbus_stack.h"
|
|
|
|
#include "modbus_req.h"
|
|
|
|
struct modbus_group_data {
|
|
char * group;
|
|
UT_array * tags;
|
|
modbus_read_cmd_sort_t *cmd_sort;
|
|
};
|
|
|
|
static void plugin_group_free(neu_plugin_group_t *pgp);
|
|
static int process_protocol_buf(neu_plugin_t *plugin, uint16_t response_size);
|
|
|
|
void modbus_conn_connected(void *data, int fd)
|
|
{
|
|
struct neu_plugin *plugin = (struct neu_plugin *) data;
|
|
(void) fd;
|
|
|
|
plugin->common.link_state = NEU_NODE_LINK_STATE_CONNECTED;
|
|
}
|
|
|
|
void modbus_conn_disconnected(void *data, int fd)
|
|
{
|
|
struct neu_plugin *plugin = (struct neu_plugin *) data;
|
|
(void) fd;
|
|
|
|
plugin->common.link_state = NEU_NODE_LINK_STATE_DISCONNECTED;
|
|
}
|
|
|
|
int modbus_send_msg(void *ctx, uint16_t n_byte, uint8_t *bytes)
|
|
{
|
|
neu_plugin_t *plugin = (neu_plugin_t *) ctx;
|
|
|
|
plog_send_protocol(plugin, bytes, n_byte);
|
|
return neu_conn_send(plugin->conn, bytes, n_byte);
|
|
}
|
|
|
|
int modbus_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group,
|
|
uint16_t max_byte)
|
|
{
|
|
struct modbus_group_data *gd = NULL;
|
|
|
|
if (group->user_data == NULL) {
|
|
gd = calloc(1, sizeof(struct modbus_group_data));
|
|
|
|
group->user_data = gd;
|
|
group->group_free = plugin_group_free;
|
|
utarray_new(gd->tags, &ut_ptr_icd);
|
|
|
|
utarray_foreach(group->tags, neu_datatag_t *, tag)
|
|
{
|
|
modbus_point_t *p = calloc(1, sizeof(modbus_point_t));
|
|
int ret = modbus_tag_to_point(tag, p);
|
|
assert(ret == 0);
|
|
|
|
utarray_push_back(gd->tags, &p);
|
|
}
|
|
|
|
gd->group = strdup(group->group_name);
|
|
gd->cmd_sort = modbus_tag_sort(gd->tags, max_byte);
|
|
} else {
|
|
gd = (struct modbus_group_data *) group->user_data;
|
|
}
|
|
plugin->plugin_group_data = gd;
|
|
|
|
for (uint16_t i = 0; i < gd->cmd_sort->n_cmd; i++) {
|
|
uint16_t response_size = 0;
|
|
plugin->cmd_idx = i;
|
|
int ret = modbus_stack_read(
|
|
plugin->stack, gd->cmd_sort->cmd[i].slave_id,
|
|
gd->cmd_sort->cmd[i].area, gd->cmd_sort->cmd[i].start_address,
|
|
gd->cmd_sort->cmd[i].n_register, &response_size);
|
|
if (ret > 0) {
|
|
process_protocol_buf(plugin, response_size);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte,
|
|
uint8_t *bytes)
|
|
{
|
|
neu_plugin_t * plugin = (neu_plugin_t *) ctx;
|
|
struct modbus_group_data *gd =
|
|
(struct modbus_group_data *) plugin->plugin_group_data;
|
|
uint16_t start_address = gd->cmd_sort->cmd[plugin->cmd_idx].start_address;
|
|
uint16_t n_register = gd->cmd_sort->cmd[plugin->cmd_idx].n_register;
|
|
|
|
if (bytes == NULL) {
|
|
neu_dvalue_t dvalue = { 0 };
|
|
|
|
dvalue.type = NEU_TYPE_ERROR;
|
|
dvalue.value.i32 = NEU_ERR_PLUGIN_DISCONNECTED;
|
|
plugin->common.adapter_callbacks->driver.update(
|
|
plugin->common.adapter, gd->group, NULL, dvalue);
|
|
return 0;
|
|
}
|
|
|
|
utarray_foreach(gd->cmd_sort->cmd[plugin->cmd_idx].tags, modbus_point_t **,
|
|
p_tag)
|
|
{
|
|
neu_dvalue_t dvalue = { 0 };
|
|
|
|
if ((*p_tag)->start_address + (*p_tag)->n_register >
|
|
start_address + n_register ||
|
|
slave_id != (*p_tag)->slave_id) {
|
|
dvalue.type = NEU_TYPE_ERROR;
|
|
dvalue.value.i32 = NEU_ERR_PLUGIN_READ_FAILURE;
|
|
} else {
|
|
switch ((*p_tag)->area) {
|
|
case MODBUS_AREA_HOLD_REGISTER:
|
|
case MODBUS_AREA_INPUT_REGISTER:
|
|
if (n_byte >= ((*p_tag)->start_address - start_address) * 2 +
|
|
(*p_tag)->n_register * 2) {
|
|
memcpy(dvalue.value.bytes,
|
|
bytes +
|
|
((*p_tag)->start_address - start_address) * 2,
|
|
(*p_tag)->n_register * 2);
|
|
}
|
|
break;
|
|
case MODBUS_AREA_COIL:
|
|
case MODBUS_AREA_INPUT: {
|
|
uint16_t offset = (*p_tag)->start_address - start_address;
|
|
if (n_byte > offset / 8) {
|
|
neu_value8_u u8 = { .value = bytes[offset / 8] };
|
|
|
|
dvalue.value.u8 = neu_value8_get_bit(u8, offset % 8);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
dvalue.type = (*p_tag)->type;
|
|
switch ((*p_tag)->type) {
|
|
case NEU_TYPE_UINT16:
|
|
case NEU_TYPE_INT16:
|
|
dvalue.value.u16 = ntohs(dvalue.value.u16);
|
|
break;
|
|
case NEU_TYPE_FLOAT:
|
|
case NEU_TYPE_INT32:
|
|
case NEU_TYPE_UINT32:
|
|
dvalue.value.u32 = ntohl(dvalue.value.u32);
|
|
break;
|
|
case NEU_TYPE_BIT: {
|
|
switch ((*p_tag)->area) {
|
|
case MODBUS_AREA_HOLD_REGISTER:
|
|
case MODBUS_AREA_INPUT_REGISTER: {
|
|
neu_value16_u v16 = { 0 };
|
|
v16.value = htons(*(uint16_t *) dvalue.value.bytes);
|
|
dvalue.value.u8 =
|
|
neu_value16_get_bit(v16, (*p_tag)->option.bit.bit);
|
|
break;
|
|
}
|
|
case MODBUS_AREA_COIL:
|
|
case MODBUS_AREA_INPUT:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case NEU_TYPE_STRING: {
|
|
switch ((*p_tag)->option.string.type) {
|
|
case NEU_DATATAG_STRING_TYPE_H:
|
|
break;
|
|
case NEU_DATATAG_STRING_TYPE_L:
|
|
neu_datatag_string_ltoh(dvalue.value.str,
|
|
strlen(dvalue.value.str));
|
|
break;
|
|
case NEU_DATATAG_STRING_TYPE_D:
|
|
break;
|
|
case NEU_DATATAG_STRING_TYPE_E:
|
|
break;
|
|
}
|
|
|
|
if (!neu_datatag_string_is_utf8(dvalue.value.str,
|
|
strlen(dvalue.value.str))) {
|
|
dvalue.value.str[0] = '?';
|
|
dvalue.value.str[1] = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
plugin->common.adapter_callbacks->driver.update(
|
|
plugin->common.adapter, gd->group, (*p_tag)->name, dvalue);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
|
|
neu_value_u value)
|
|
{
|
|
modbus_point_t point = { 0 };
|
|
int ret = modbus_tag_to_point(tag, &point);
|
|
assert(ret == 0);
|
|
uint8_t n_byte = 0;
|
|
|
|
switch (tag->type) {
|
|
case NEU_TYPE_UINT16:
|
|
case NEU_TYPE_INT16: {
|
|
value.u16 = htons(value.u16);
|
|
n_byte = sizeof(uint16_t);
|
|
break;
|
|
}
|
|
case NEU_TYPE_UINT32:
|
|
case NEU_TYPE_INT32:
|
|
case NEU_TYPE_FLOAT: {
|
|
value.u32 = htonl(value.u32);
|
|
n_byte = sizeof(uint32_t);
|
|
break;
|
|
}
|
|
case NEU_TYPE_BIT: {
|
|
n_byte = sizeof(uint8_t);
|
|
break;
|
|
}
|
|
case NEU_TYPE_STRING: {
|
|
switch (point.option.string.type) {
|
|
case NEU_DATATAG_STRING_TYPE_H:
|
|
break;
|
|
case NEU_DATATAG_STRING_TYPE_L:
|
|
neu_datatag_string_ltoh(value.str, point.option.string.length);
|
|
break;
|
|
case NEU_DATATAG_STRING_TYPE_D:
|
|
break;
|
|
case NEU_DATATAG_STRING_TYPE_E:
|
|
break;
|
|
}
|
|
n_byte = point.option.string.length;
|
|
break;
|
|
}
|
|
default:
|
|
assert(1 == 0);
|
|
break;
|
|
}
|
|
|
|
uint16_t response_size = 0;
|
|
ret = modbus_stack_write(plugin->stack, req, point.slave_id, point.area,
|
|
point.start_address, point.n_register, value.bytes,
|
|
n_byte, &response_size);
|
|
if (ret > 0) {
|
|
process_protocol_buf(plugin, response_size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int modbus_write_resp(void *ctx, void *req, int error)
|
|
{
|
|
neu_plugin_t *plugin = (neu_plugin_t *) ctx;
|
|
|
|
plugin->common.adapter_callbacks->driver.write_response(
|
|
plugin->common.adapter, req, error);
|
|
return 0;
|
|
}
|
|
|
|
static void plugin_group_free(neu_plugin_group_t *pgp)
|
|
{
|
|
struct modbus_group_data *gd = (struct modbus_group_data *) pgp->user_data;
|
|
|
|
modbus_tag_sort_free(gd->cmd_sort);
|
|
|
|
utarray_foreach(gd->tags, modbus_point_t **, tag) { free(*tag); }
|
|
|
|
utarray_free(gd->tags);
|
|
free(gd->group);
|
|
|
|
free(gd);
|
|
}
|
|
|
|
static int process_protocol_buf(neu_plugin_t *plugin, uint16_t response_size)
|
|
{
|
|
uint8_t * recv_buf = calloc(256, 1);
|
|
neu_protocol_unpack_buf_t pbuf = { 0 };
|
|
ssize_t ret = 0;
|
|
|
|
ret = neu_conn_recv(plugin->conn, recv_buf, response_size);
|
|
|
|
if (ret > 0) {
|
|
neu_protocol_unpack_buf_init(&pbuf, recv_buf, ret);
|
|
plog_recv_protocol(plugin, recv_buf, ret);
|
|
ret = modbus_stack_recv(plugin->stack, &pbuf);
|
|
}
|
|
|
|
free(recv_buf);
|
|
return ret;
|
|
} |