forked from espressif/esp-modbus
feature add custom fc handlers for master and slave v2
This commit is contained in:
1 parent
439c767aed
commit
8215e3d0d2
51 files changed
+1345
-300
No files matched your search
+6
-6
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "mb_config.h"
|
||||
#include "mb_types.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if __has_include("esp_idf_version.h")
|
||||
#include "esp_idf_version.h"
|
||||
@@ -39,7 +39,7 @@ typedef enum _mb_comm_mode mb_mode_type_t;
|
||||
|
||||
#include "driver/uart.h"
|
||||
|
||||
struct _port_serial_opts {
|
||||
struct port_serial_opts_s {
|
||||
mb_mode_type_t mode; /*!< Modbus communication mode */
|
||||
uart_port_t port; /*!< Modbus communication port (UART) number */
|
||||
uint8_t uid; /*!< Modbus slave address field (dummy for master) */
|
||||
@@ -51,7 +51,7 @@ struct _port_serial_opts {
|
||||
uart_parity_t parity; /*!< Modbus UART parity settings */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _port_serial_opts mb_serial_opts_t;
|
||||
typedef struct port_serial_opts_s mb_serial_opts_t;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -62,7 +62,7 @@ typedef enum _addr_type_enum {
|
||||
} mb_addr_type_t;
|
||||
|
||||
|
||||
struct _port_common_opts {
|
||||
struct port_common_opts_s {
|
||||
mb_mode_type_t mode; /*!< Modbus communication mode */
|
||||
uint16_t port; /*!< Modbus communication port (UART) number */
|
||||
uint8_t uid; /*!< Modbus slave address field (dummy for master) */
|
||||
@@ -70,7 +70,7 @@ struct _port_common_opts {
|
||||
uint64_t test_tout_us; /*!< Modbus test timeout (reserved) */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct _port_tcp_opts {
|
||||
struct port_tcp_opts_s {
|
||||
mb_mode_type_t mode; /*!< Modbus communication mode */
|
||||
uint16_t port; /*!< Modbus communication port (UART) number */
|
||||
uint8_t uid; /*!< Modbus slave address field (dummy for master) */
|
||||
@@ -83,7 +83,7 @@ struct _port_tcp_opts {
|
||||
bool start_disconnected; /*!< (Master only option) do not wait for connection to all nodes before polling */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
typedef struct _port_tcp_opts mb_tcp_opts_t;
|
||||
typedef struct port_tcp_opts_s mb_tcp_opts_t;
|
||||
|
||||
// The common object descriptor struture (common for mb, transport, port objects)
|
||||
struct _obj_descr {
|
||||
@@ -85,6 +85,26 @@ typedef enum _mb_event_enum {
|
||||
EV_MASTER_PROCESS_SUCCESS = 0x0400 /*!< Master error process. */
|
||||
} mb_event_enum_t;
|
||||
|
||||
/*! \ingroup modbus
|
||||
* \brief Modbus exception types used in the stack.
|
||||
*/
|
||||
typedef enum _mb_exception_enum
|
||||
{
|
||||
MB_EX_NONE = 0x00,
|
||||
MB_EX_ILLEGAL_FUNCTION = 0x01,
|
||||
MB_EX_ILLEGAL_DATA_ADDRESS = 0x02,
|
||||
MB_EX_ILLEGAL_DATA_VALUE = 0x03,
|
||||
MB_EX_SLAVE_DEVICE_FAILURE = 0x04,
|
||||
MB_EX_ACKNOWLEDGE = 0x05,
|
||||
MB_EX_SLAVE_BUSY = 0x06,
|
||||
MB_EX_MEMORY_PARITY_ERROR = 0x08,
|
||||
MB_EX_GATEWAY_PATH_FAILED = 0x0A,
|
||||
MB_EX_GATEWAY_TGT_FAILED = 0x0B,
|
||||
MB_EX_CRITICAL = 0xFF
|
||||
} mb_exception_t;
|
||||
|
||||
typedef mb_exception_t (*mb_fn_handler_fp)(void *, uint8_t *frame_ptr, uint16_t *len_buf);
|
||||
|
||||
/*! \ingroup modbus
|
||||
* \brief Error event type
|
||||
*/
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "mb_common.h"
|
||||
#include "mb_proto.h"
|
||||
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define MB_IS_VALID_FUNC_CODE(fc) ((fc) >= MB_FUNC_CODE_MIN && (fc) <= MB_FUNC_CODE_MAX)
|
||||
static const char TAG[] __attribute__((unused)) = "MB_FUNC_HANDLING";
|
||||
|
||||
mb_err_enum_t mb_set_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp phandler)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pdescriptor && phandler && pdescriptor->instance), MB_EINVAL, TAG, "invalid arguments.");
|
||||
MB_RETURN_ON_FALSE(MB_IS_VALID_FUNC_CODE(func_code), MB_EINVAL, TAG,
|
||||
"invalid function code (0x%x)", (int)func_code);
|
||||
|
||||
mb_command_entry_t *pitem = NULL;
|
||||
LIST_FOREACH(pitem, &pdescriptor->head, entries) {
|
||||
if (pitem && pitem->func_code == func_code) {
|
||||
// The handler for the function already exists, rewrite it.
|
||||
pitem->handler = phandler;
|
||||
ESP_LOGD(TAG, "Inst: %p, set handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
|
||||
return MB_ENOERR;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new handler entry into list
|
||||
if (pdescriptor->count >= MB_FUNC_HANDLERS_MAX) {
|
||||
return MB_ENORES;
|
||||
} else {
|
||||
pdescriptor->count += 1;
|
||||
}
|
||||
pitem = (mb_command_entry_t *) heap_caps_malloc(sizeof(mb_command_entry_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
|
||||
|
||||
MB_RETURN_ON_FALSE(pitem, MB_ENORES, TAG, "mb can not allocate memory for command handler 0x%x.", func_code);
|
||||
pitem->func_code = func_code;
|
||||
pitem->handler = phandler;
|
||||
LIST_INSERT_HEAD(&pdescriptor->head, pitem, entries);
|
||||
ESP_LOGD(TAG, "Inst: %p, add handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
|
||||
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
mb_err_enum_t mb_get_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp *phandler)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pdescriptor && phandler && pdescriptor->instance), MB_EINVAL, TAG, "invalid arguments.");
|
||||
MB_RETURN_ON_FALSE(MB_IS_VALID_FUNC_CODE(func_code), MB_EINVAL, TAG,
|
||||
"invalid function code (0x%x)", (int)func_code);
|
||||
|
||||
mb_command_entry_t *pitem = NULL;
|
||||
LIST_FOREACH(pitem, &pdescriptor->head, entries) {
|
||||
if (pitem && pitem->func_code == func_code) {
|
||||
*phandler = pitem->handler;
|
||||
ESP_LOGD(TAG, "Inst: %p, get handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
|
||||
return MB_ENOERR;
|
||||
}
|
||||
}
|
||||
return MB_ENORES;
|
||||
}
|
||||
|
||||
// Helper function to get handler
|
||||
mb_err_enum_t mb_delete_handler(handler_descriptor_t *pdescriptor, uint8_t func_code)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pdescriptor && pdescriptor->instance), MB_EINVAL, TAG, "invalid arguments.");
|
||||
MB_RETURN_ON_FALSE(MB_IS_VALID_FUNC_CODE(func_code), MB_EINVAL, TAG,
|
||||
"invalid function code (0x%x)", (int)func_code);
|
||||
|
||||
if (LIST_EMPTY(&pdescriptor->head)) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
|
||||
mb_command_entry_t *pitem = NULL;
|
||||
mb_command_entry_t *ptemp = NULL;
|
||||
LIST_FOREACH_SAFE(pitem, &pdescriptor->head, entries, ptemp) {
|
||||
if (pitem && pitem->func_code == func_code) {
|
||||
ESP_LOGD(TAG, "Inst: %p, remove handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
|
||||
LIST_REMOVE(pitem, entries);
|
||||
free(pitem);
|
||||
if (pdescriptor->count) {
|
||||
pdescriptor->count--;
|
||||
}
|
||||
return MB_ENOERR;
|
||||
}
|
||||
}
|
||||
|
||||
return MB_ENORES;
|
||||
}
|
||||
|
||||
// Helper function to close all registered handlers in the list
|
||||
mb_err_enum_t mb_delete_command_handlers(handler_descriptor_t *pdescriptor)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pdescriptor), MB_EINVAL, TAG, "invalid arguments.");
|
||||
|
||||
if (LIST_EMPTY(&pdescriptor->head)) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
|
||||
mb_command_entry_t *pitem = NULL;
|
||||
while ((pitem = LIST_FIRST(&pdescriptor->head))) {
|
||||
ESP_LOGD(TAG, "Inst: %p, close handler: 0x%x, %p", pdescriptor->instance, pitem->func_code, pitem->handler);
|
||||
LIST_REMOVE(pitem, entries);
|
||||
free(pitem);
|
||||
if (pdescriptor->count) {
|
||||
pdescriptor->count--;
|
||||
}
|
||||
}
|
||||
return MB_ENOERR;
|
||||
}
|
||||
@@ -44,6 +44,39 @@
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
mb_exception_t mb_error_to_exception(mb_err_enum_t error_code);
|
||||
|
||||
/**
|
||||
* This function will request read coil.
|
||||
*
|
||||
* @param uid slave address
|
||||
* @param fc custom function code
|
||||
* @param buf additional data to send
|
||||
* @param buf_size size of data to send
|
||||
* @param timeout timeout (-1 will waiting forever)
|
||||
*
|
||||
* @return error code (mb_err_enum_t)
|
||||
*/
|
||||
mb_err_enum_t mbm_rq_custom(mb_base_t *inst, uint8_t uid, uint8_t fc, uint8_t *buf, uint16_t buf_size, uint32_t tout)
|
||||
{
|
||||
uint8_t *mb_frame_ptr;
|
||||
if (!buf || (uid > MB_ADDRESS_MAX) || (buf_size >= (MB_BUFFER_SIZE - 2))) {
|
||||
return MB_EINVAL;
|
||||
}
|
||||
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
||||
return MB_EBUSY;
|
||||
}
|
||||
inst->get_send_buf(inst, &mb_frame_ptr);
|
||||
inst->set_dest_addr(inst, uid);
|
||||
|
||||
mb_frame_ptr[MB_PDU_FUNC_OFF] = fc;
|
||||
|
||||
memcpy(&mb_frame_ptr[MB_PDU_DATA_OFF], buf, buf_size);
|
||||
|
||||
inst->set_send_len(inst, MB_PDU_SIZE_MIN + buf_size);
|
||||
|
||||
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
||||
return mb_port_event_wait_req_finish(inst->port_obj);
|
||||
}
|
||||
|
||||
mb_err_enum_t mbm_rq_report_slave_id(mb_base_t *inst, uint8_t slave_addr, uint32_t timeout)
|
||||
{
|
||||
uint8_t *mb_frame_ptr = NULL;
|
||||
|
||||
@@ -28,10 +28,11 @@
|
||||
*
|
||||
* File: $Id: mbutils.c, v 1.6 2007/02/18 23:49:07 wolti Exp $
|
||||
*/
|
||||
#include <mb_common.h>
|
||||
#include <mb_proto.h>
|
||||
#include "mb_common.h"
|
||||
#include "mb_proto.h"
|
||||
/* ----------------------- Defines ------------------------------------------*/
|
||||
#define BITS_uint8_t 8U
|
||||
#define BITS_uint8_t (8U)
|
||||
static const char TAG[] __attribute__((unused)) = "MB_UTILS";
|
||||
|
||||
/* ----------------------- Start implementation -----------------------------*/
|
||||
void mb_util_set_bits(uint8_t *byte_buf, uint16_t bit_offset, uint8_t but_num, uint8_t value)
|
||||
@@ -125,5 +126,3 @@ mb_exception_t mb_error_to_exception(mb_err_enum_t error_code)
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "mb_config.h"
|
||||
#include "mb_frame.h"
|
||||
@@ -63,6 +64,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define MB_CAT_BUF_SIZE (100)
|
||||
#define MB_HANDLER_UNLOCK_TICKS (pdMS_TO_TICKS(5000))
|
||||
|
||||
#define SEMA_SECTION(sema, tout) for (int st = (int)xSemaphoreTake(sema, tout); (st > 0); xSemaphoreGive(sema), st = -1)
|
||||
|
||||
#define MB_STR_CAT(pref, message) (__extension__( \
|
||||
{ \
|
||||
@@ -104,6 +108,24 @@ extern "C" {
|
||||
} \
|
||||
))
|
||||
|
||||
/**
|
||||
* @brief Modbus function handlers entry
|
||||
*/
|
||||
typedef struct mb_command_entry_s {
|
||||
uint8_t func_code; /*!< function code */
|
||||
mb_fn_handler_fp handler; /*!< handler function pointer */
|
||||
LIST_ENTRY(mb_command_entry_s) entries; /*!< command handler entry */
|
||||
} mb_command_entry_t;
|
||||
|
||||
typedef LIST_HEAD(handler_head, mb_command_entry_s) handler_head_t;
|
||||
|
||||
typedef struct mb_handler_descriptor_s {
|
||||
SemaphoreHandle_t sema;
|
||||
void* instance;
|
||||
handler_head_t head;
|
||||
uint16_t count;
|
||||
} handler_descriptor_t;
|
||||
|
||||
typedef struct mb_base_t mb_base_t;
|
||||
typedef struct mb_trans_base_t mb_trans_base_t;
|
||||
typedef struct mb_port_base_t mb_port_base_t;
|
||||
@@ -152,19 +174,28 @@ struct mb_base_t
|
||||
mb_rw_callbacks_t rw_cbs;
|
||||
};
|
||||
|
||||
typedef struct _port_tcp_opts mb_tcp_opts_t;
|
||||
// Helper functions for command handlers registration
|
||||
mb_err_enum_t mb_set_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp phandler);
|
||||
mb_err_enum_t mb_get_handler(handler_descriptor_t *pdescriptor, uint8_t func_code, mb_fn_handler_fp *phandler);
|
||||
mb_err_enum_t mb_delete_handler(handler_descriptor_t *pdescriptor, uint8_t func_code);
|
||||
mb_err_enum_t mb_delete_command_handlers(handler_descriptor_t *pdescriptor);
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
|
||||
|
||||
typedef struct _port_serial_opts mb_serial_opts_t;
|
||||
typedef struct port_serial_opts_s mb_serial_opts_t;
|
||||
|
||||
mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
|
||||
mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj);
|
||||
|
||||
#endif
|
||||
|
||||
#if (CONFIG_FMB_COMM_MODE_TCP_EN)
|
||||
|
||||
typedef struct port_tcp_opts_s mb_tcp_opts_t;
|
||||
mb_err_enum_t mbs_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);
|
||||
|
||||
#endif
|
||||
|
||||
mb_err_enum_t mbs_delete(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_enable(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_disable(mb_base_t *inst);
|
||||
|
||||
@@ -115,7 +115,7 @@ extern "C" {
|
||||
* the sum of all enabled functions in this file and custom function
|
||||
* handlers. If set to small adding more functions will fail.
|
||||
*/
|
||||
#define MB_FUNC_HANDLERS_MAX (16)
|
||||
#define MB_FUNC_HANDLERS_MAX (CONFIG_FMB_FUNC_HANDLERS_MAX)
|
||||
|
||||
/*! \brief Number of bytes which should be allocated for the <em>Report Slave ID
|
||||
* </em>command.
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MB_FUNC_CODE_MIN (0x01)
|
||||
#define MB_FUNC_CODE_MAX (0x7F)
|
||||
|
||||
typedef struct mb_base_t mb_base_t;
|
||||
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct mb_base_t mb_base_t; /*!< Type of moddus object */
|
||||
typedef struct mb_base_t mb_base_t; /*!< Type of modbus object */
|
||||
|
||||
mb_err_enum_t mbm_rq_read_inp_reg(mb_base_t *inst, uint8_t snd_addr, uint16_t reg_addr, uint16_t reg_num, uint32_t tout);
|
||||
mb_err_enum_t mbm_rq_write_holding_reg(mb_base_t *inst, uint8_t snd_addr, uint16_t reg_addr, uint16_t reg_data, uint32_t tout);
|
||||
@@ -26,6 +26,7 @@ mb_err_enum_t mbm_rq_write_multi_coils(mb_base_t *inst, uint8_t snd_addr, uint16
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
mb_err_enum_t mbm_rq_report_slave_id(mb_base_t *inst, uint8_t slave_addr, uint32_t timeout);
|
||||
mb_exception_t mbm_fn_report_slave_id(mb_base_t *inst, uint8_t * pframe, uint16_t *usLen);
|
||||
mb_err_enum_t mbm_rq_custom(mb_base_t *inst, uint8_t uid, uint8_t fc, uint8_t *buf, uint16_t buf_size, uint32_t tout);
|
||||
|
||||
/*! \ingroup modbus_registers
|
||||
* \brief The common callback function used to transfer common data as bytes from command buffer in little endian format.
|
||||
@@ -44,6 +45,18 @@ mb_exception_t mbm_fn_report_slave_id(mb_base_t *inst, uint8_t * pframe, uint16_
|
||||
mb_err_enum_t mbc_reg_common_cb(mb_base_t *inst, uint8_t *pdata, uint16_t address, uint16_t bytes);
|
||||
#endif
|
||||
|
||||
// The function to register custom function handler for master
|
||||
mb_err_enum_t mbm_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler);
|
||||
|
||||
// The helper function to get custom function handler for master
|
||||
mb_err_enum_t mbm_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler);
|
||||
|
||||
// The helper function to delete custom function handler for master
|
||||
mb_err_enum_t mbm_delete_handler(mb_base_t *inst, uint8_t func_code);
|
||||
|
||||
// The helper function to get count of handlers for master
|
||||
mb_err_enum_t mbm_get_handler_count(mb_base_t *inst, uint16_t *pcount);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mb_func.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -33,27 +34,11 @@ typedef enum _mb_commands_enum
|
||||
MB_FUNC_DIAG_GET_COM_EVENT_CNT = ( 11 ),
|
||||
MB_FUNC_DIAG_GET_COM_EVENT_LOG = ( 12 ),
|
||||
MB_FUNC_OTHER_REPORT_SLAVEID = ( 17 ),
|
||||
MB_FUNC_ERROR = ( 128U ),
|
||||
MB_FUNC_ERROR = ( 0x80 )
|
||||
} mb_commands_t;
|
||||
|
||||
/* ----------------------- Type definitions ---------------------------------*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MB_EX_NONE = 0x00,
|
||||
MB_EX_ILLEGAL_FUNCTION = 0x01,
|
||||
MB_EX_ILLEGAL_DATA_ADDRESS = 0x02,
|
||||
MB_EX_ILLEGAL_DATA_VALUE = 0x03,
|
||||
MB_EX_SLAVE_DEVICE_FAILURE = 0x04,
|
||||
MB_EX_ACKNOWLEDGE = 0x05,
|
||||
MB_EX_SLAVE_BUSY = 0x06,
|
||||
MB_EX_MEMORY_PARITY_ERROR = 0x08,
|
||||
MB_EX_GATEWAY_PATH_FAILED = 0x0A,
|
||||
MB_EX_GATEWAY_TGT_FAILED = 0x0B
|
||||
} mb_exception_t;
|
||||
|
||||
typedef mb_exception_t (*mb_fn_handler_fp)(void *, uint8_t *frame_ptr, uint16_t *len_buf);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t func_code;
|
||||
|
||||
@@ -15,6 +15,18 @@ extern "C" {
|
||||
mb_exception_t mbs_fn_report_slave_id(mb_base_t *inst, uint8_t *frame_ptr, uint16_t *plen_buf);
|
||||
#endif
|
||||
|
||||
// The helper function to register custom function handler for slave
|
||||
mb_err_enum_t mbs_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler);
|
||||
|
||||
// The helper function to get custom function handler for slave
|
||||
mb_err_enum_t mbs_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler);
|
||||
|
||||
// The helper function to delete custom function handler for slave
|
||||
mb_err_enum_t mbs_delete_handler(mb_base_t *inst, uint8_t func_code);
|
||||
|
||||
// The helper function to get count of handlers for slave
|
||||
mb_err_enum_t mbs_get_handler_count(mb_base_t *inst, uint16_t *pcount);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+168
-70
@@ -16,48 +16,20 @@
|
||||
|
||||
static const char *TAG = "mb_object.master";
|
||||
|
||||
#if (MB_MASTER_ASCII_ENABLED || MB_MASTER_RTU_ENABLED || MB_MASTER_TCP_ENABLED)
|
||||
#if (MB_MASTER_ASCII_ENABLED || MB_MASTER_RTU_ENABLED)
|
||||
|
||||
typedef struct port_serial_opts_s mb_serial_opts_t;
|
||||
|
||||
static const mb_fn_handler_t master_handlers[MB_FUNC_HANDLERS_MAX] =
|
||||
{
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
{MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbm_fn_report_slave_id},
|
||||
#endif
|
||||
#if MB_FUNC_READ_INPUT_ENABLED
|
||||
{MB_FUNC_READ_INPUT_REGISTER, (void *)mbm_fn_read_inp_reg},
|
||||
#endif
|
||||
#if MB_FUNC_READ_HOLDING_ENABLED
|
||||
{MB_FUNC_READ_HOLDING_REGISTER, (void *)mbm_fn_read_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
|
||||
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_write_multi_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_HOLDING_ENABLED
|
||||
{MB_FUNC_WRITE_REGISTER, (void *)mbm_fn_write_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_READWRITE_HOLDING_ENABLED
|
||||
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_rw_multi_holding_regs},
|
||||
#endif
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
{MB_FUNC_READ_COILS, (void *)mbm_fn_read_coils},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_COIL_ENABLED
|
||||
{MB_FUNC_WRITE_SINGLE_COIL, (void *)mbm_fn_write_coil},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
|
||||
{MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbm_fn_write_multi_coils},
|
||||
#endif
|
||||
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
|
||||
{MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbm_fn_read_discrete_inputs},
|
||||
#endif
|
||||
};
|
||||
|
||||
#if (MB_MASTER_ASCII_ENABLED || MB_MASTER_RTU_ENABLED || MB_MASTER_TCP_ENABLED)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
mb_base_t base;
|
||||
|
||||
mb_comm_mode_t cur_mode;
|
||||
mb_state_enum_t cur_state;
|
||||
const mb_fn_handler_t *func_handlers;
|
||||
uint8_t *rcv_frame;
|
||||
uint8_t *snd_frame;
|
||||
uint16_t pdu_snd_len;
|
||||
@@ -67,6 +39,7 @@ typedef struct
|
||||
mb_exception_t exception;
|
||||
uint8_t master_dst_addr;
|
||||
uint64_t curr_trans_id;
|
||||
handler_descriptor_t handler_descriptor;
|
||||
} mbm_object_t;
|
||||
|
||||
mb_err_enum_t mbm_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj);
|
||||
@@ -82,7 +55,132 @@ static void mbm_set_dest_addr(mb_base_t *inst, uint8_t dest_addr);
|
||||
static uint8_t mbm_get_dest_addr(mb_base_t *inst);
|
||||
static void mbm_get_pdu_send_buf(mb_base_t *inst, uint8_t **pbuf);
|
||||
|
||||
typedef struct _port_serial_opts mb_serial_opts_t;
|
||||
mb_err_enum_t mbm_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_err_enum_t status = MB_EILLSTATE;
|
||||
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
status = mb_set_handler(&mbm_obj->handler_descriptor, func_code, phandler);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// The helper function to register custom function handler for master
|
||||
mb_err_enum_t mbm_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_err_enum_t status = MB_EILLSTATE;
|
||||
if (phandler) {
|
||||
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
status = mb_get_handler(&mbm_obj->handler_descriptor, func_code, phandler);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
mb_err_enum_t mbm_delete_handler(mb_base_t *inst, uint8_t func_code)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_err_enum_t status = MB_EILLSTATE;
|
||||
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
status = mb_delete_handler(&mbm_obj->handler_descriptor, func_code);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
mb_err_enum_t mbm_get_handler_count(mb_base_t *inst, uint16_t *pcount)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pcount && inst), MB_EINVAL, TAG, "get handler count wrong arguments.");
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
*pcount = mbm_obj->handler_descriptor.count;
|
||||
}
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
static mb_err_enum_t mbm_check_invoke_handler(mb_base_t *inst, uint8_t func_code, uint8_t *pbuf, uint16_t *plen)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_exception_t exception = MB_EX_ILLEGAL_FUNCTION;
|
||||
if (!func_code || !pbuf) {
|
||||
return MB_EX_ILLEGAL_FUNCTION;
|
||||
}
|
||||
if (func_code & MB_FUNC_ERROR) {
|
||||
exception = (mb_exception_t)pbuf[MB_PDU_DATA_OFF];
|
||||
return exception;
|
||||
}
|
||||
SEMA_SECTION(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
mb_fn_handler_fp phandler = NULL;
|
||||
mb_err_enum_t status = mb_get_handler(&mbm_obj->handler_descriptor, func_code, &phandler);
|
||||
if ((status == MB_ENOERR) && phandler) {
|
||||
exception = phandler(inst, pbuf, plen);
|
||||
}
|
||||
}
|
||||
return exception;
|
||||
}
|
||||
|
||||
static mb_err_enum_t mbm_register_default_handlers(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_err_enum_t err = MB_EILLSTATE;
|
||||
LIST_INIT(&mbm_obj->handler_descriptor.head);
|
||||
mbm_obj->handler_descriptor.sema = xSemaphoreCreateBinary();
|
||||
(void)xSemaphoreGive(mbm_obj->handler_descriptor.sema);
|
||||
mbm_obj->handler_descriptor.instance = (void *)inst->descr.parent;
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbm_fn_report_slave_id);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_INPUT_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_READ_INPUT_REGISTER, (void *)mbm_fn_read_inp_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_HOLDING_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_READ_HOLDING_REGISTER, (void *)mbm_fn_read_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_write_multi_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_HOLDING_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_WRITE_REGISTER, (void *)mbm_fn_write_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READWRITE_HOLDING_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbm_fn_rw_multi_holding_regs);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_READ_COILS, (void *)mbm_fn_read_coils);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_COIL_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_WRITE_SINGLE_COIL, (void *)mbm_fn_write_coil);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbm_fn_write_multi_coils);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
|
||||
err = mbm_set_handler(inst, MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbm_fn_read_discrete_inputs);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
static mb_err_enum_t mbm_unregister_handlers(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
(void)xSemaphoreTake(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
|
||||
ESP_LOGD(TAG, "Close %s command handlers.", mbm_obj->base.descr.parent_name);
|
||||
(void)mb_delete_command_handlers(&mbm_obj->handler_descriptor);
|
||||
mbm_obj->handler_descriptor.instance = NULL;
|
||||
(void)xSemaphoreGive(mbm_obj->handler_descriptor.sema);
|
||||
vSemaphoreDelete(mbm_obj->handler_descriptor.sema);
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
#if (MB_MASTER_RTU_ENABLED)
|
||||
|
||||
@@ -121,7 +219,6 @@ mb_err_enum_t mbm_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
ret = mbm_rtu_transp_create(ser_opts, (void **)&transp_obj);
|
||||
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
|
||||
TAG, "transport creation, err: %d", (int)ret);
|
||||
mbm_obj->func_handlers = master_handlers;
|
||||
mbm_obj->cur_mode = ser_opts->mode;
|
||||
mbm_obj->cur_state = STATE_DISABLED;
|
||||
transp_obj->get_tx_frm(transp_obj, (uint8_t **)&mbm_obj->snd_frame);
|
||||
@@ -129,6 +226,9 @@ mb_err_enum_t mbm_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
mbm_obj->curr_trans_id = 0;
|
||||
mbm_obj->base.port_obj = transp_obj->port_obj;
|
||||
mbm_obj->base.transp_obj = transp_obj;
|
||||
ret = mbm_register_default_handlers(&mbm_obj->base);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
|
||||
TAG, "default handlers registration fail, err: %d", (int)ret);
|
||||
*in_out_obj = (void *)&(mbm_obj->base);
|
||||
ESP_LOGD(TAG, "created object %s", mbm_obj->base.descr.parent_name);
|
||||
return MB_ENOERR;
|
||||
@@ -137,6 +237,7 @@ error:
|
||||
if (transp_obj) {
|
||||
mbm_rtu_transp_delete(transp_obj);
|
||||
}
|
||||
(void)mbm_unregister_handlers(&mbm_obj->base);
|
||||
free(mbm_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(mbm_obj->base.lock);
|
||||
free(mbm_obj);
|
||||
@@ -183,7 +284,9 @@ mb_err_enum_t mbm_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
ret = mbm_ascii_transp_create(ser_opts, (void **)&transp_obj);
|
||||
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
|
||||
TAG, "transport creation, err: %d", (int)ret);
|
||||
mbm_obj->func_handlers = master_handlers;
|
||||
ret = mbm_register_default_handlers(&mbm_obj->base);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
|
||||
TAG, "default handlers registration fail, err: %d", (int)ret);
|
||||
mbm_obj->cur_mode = ser_opts->mode;
|
||||
mbm_obj->cur_state = STATE_DISABLED;
|
||||
transp_obj->get_tx_frm(transp_obj, (uint8_t **)&mbm_obj->snd_frame);
|
||||
@@ -199,6 +302,7 @@ error:
|
||||
{
|
||||
mbm_ascii_transp_delete(transp_obj);
|
||||
}
|
||||
(void)mbm_unregister_handlers(&mbm_obj->base);
|
||||
free(mbm_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(mbm_obj->base.lock);
|
||||
free(mbm_obj);
|
||||
@@ -245,7 +349,9 @@ mb_err_enum_t mbm_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj)
|
||||
ret = mbm_tcp_transp_create(tcp_opts, (void **)&transp_obj);
|
||||
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
|
||||
TAG, "transport creation, err: %d", (int)ret);
|
||||
mbm_obj->func_handlers = master_handlers;
|
||||
ret = mbm_register_default_handlers(&mbm_obj->base);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
|
||||
TAG, "default handlers registration fail, err: %d", (int)ret);
|
||||
mbm_obj->cur_mode = tcp_opts->mode;
|
||||
mbm_obj->cur_state = STATE_DISABLED;
|
||||
transp_obj->get_tx_frm(transp_obj, (uint8_t **)&mbm_obj->snd_frame);
|
||||
@@ -260,6 +366,7 @@ error:
|
||||
if (transp_obj) {
|
||||
mbm_tcp_transp_delete(transp_obj);
|
||||
}
|
||||
(void)mbm_unregister_handlers(&mbm_obj->base);
|
||||
free(mbm_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(mbm_obj->base.lock);
|
||||
free(mbm_obj);
|
||||
@@ -271,7 +378,7 @@ error:
|
||||
|
||||
mb_err_enum_t mbm_delete(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
if (mbm_obj->cur_state == STATE_DISABLED) {
|
||||
if (MB_OBJ(mbm_obj->base.transp_obj)->frm_delete) {
|
||||
@@ -288,6 +395,7 @@ mb_err_enum_t mbm_delete(mb_base_t *inst)
|
||||
ESP_LOGW(TAG, "%p, Master object ID is not supported!", mbm_obj);
|
||||
}
|
||||
#endif
|
||||
(void)mbm_unregister_handlers(&mbm_obj->base);
|
||||
// delete the modbus instance
|
||||
free(mbm_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
@@ -303,7 +411,7 @@ mb_err_enum_t mbm_delete(mb_base_t *inst)
|
||||
|
||||
mb_err_enum_t mbm_enable(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
@@ -322,7 +430,10 @@ mb_err_enum_t mbm_enable(mb_base_t *inst)
|
||||
mb_err_enum_t mbm_disable(mb_base_t *inst)
|
||||
{
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
// Wait for function handler to be unlocked before disable the object
|
||||
(void)xSemaphoreTake(mbm_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
|
||||
(void)xSemaphoreGive(mbm_obj->handler_descriptor.sema);
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
if (mbm_obj->cur_state == STATE_ENABLED) {
|
||||
@@ -340,7 +451,7 @@ mb_err_enum_t mbm_disable(mb_base_t *inst)
|
||||
|
||||
static void mbm_get_pdu_send_buf(mb_base_t *inst, uint8_t **pbuf)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
MB_OBJ(mbm_obj->base.transp_obj)->get_tx_frm(mbm_obj->base.transp_obj, pbuf);
|
||||
}
|
||||
|
||||
@@ -353,7 +464,7 @@ static void mbm_get_pdu_recv_buf(mb_base_t *inst, uint8_t **pbuf)
|
||||
|
||||
static void mbm_set_pdu_send_length(mb_base_t *inst, uint16_t length)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
mbm_obj->pdu_snd_len = length;
|
||||
@@ -362,13 +473,13 @@ static void mbm_set_pdu_send_length(mb_base_t *inst, uint16_t length)
|
||||
|
||||
static uint16_t mbm_get_pdu_send_length(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
return mbm_obj->pdu_snd_len;
|
||||
}
|
||||
|
||||
static void mbm_set_dest_addr(mb_base_t *inst, uint8_t dest_addr)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
CRITICAL_SECTION(inst->lock)
|
||||
{
|
||||
mbm_obj->master_dst_addr = dest_addr;
|
||||
@@ -377,7 +488,7 @@ static void mbm_set_dest_addr(mb_base_t *inst, uint8_t dest_addr)
|
||||
|
||||
static uint8_t mbm_get_dest_addr(mb_base_t *inst)
|
||||
{
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);;
|
||||
mbm_object_t *mbm_obj = MB_GET_OBJ_CTX(inst, mbm_object_t, base);
|
||||
return mbm_obj->master_dst_addr;
|
||||
}
|
||||
|
||||
@@ -496,31 +607,18 @@ mb_err_enum_t mbm_poll(mb_base_t *inst)
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_EXECUTE", MB_OBJ_PARENT(inst));
|
||||
mbm_obj->func_code = mbm_obj->rcv_frame[MB_PDU_FUNC_OFF];
|
||||
exception = MB_EX_ILLEGAL_FUNCTION;
|
||||
/* If receive frame has exception. The receive function code highest bit is 1.*/
|
||||
if (mbm_obj->func_code & MB_FUNC_ERROR) {
|
||||
exception = (mb_exception_t)mbm_obj->rcv_frame[MB_PDU_DATA_OFF];
|
||||
} else {
|
||||
for (int i = 0; i < MB_FUNC_HANDLERS_MAX; i++) {
|
||||
/* No more function handlers registered. Abort. */
|
||||
if (mbm_obj->func_handlers[i].func_code == 0) {
|
||||
break;
|
||||
}
|
||||
if (mbm_obj->func_handlers[i].func_code == mbm_obj->func_code) {
|
||||
/* If master request is broadcast,
|
||||
* the master need execute function for all slave.
|
||||
*/
|
||||
if (MB_OBJ(inst->transp_obj)->frm_is_bcast(inst->transp_obj)) {
|
||||
length = mbm_obj->pdu_snd_len;
|
||||
for (int j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++) {
|
||||
mbm_set_dest_addr(inst, j);
|
||||
exception = mbm_obj->func_handlers[i].handler(inst, mbm_obj->rcv_frame, &length);
|
||||
}
|
||||
} else {
|
||||
exception = mbm_obj->func_handlers[i].handler(inst, mbm_obj->rcv_frame, &mbm_obj->pdu_rcv_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* If master request is broadcast,
|
||||
* the master needs to execute function for all slaves.
|
||||
*/
|
||||
if (MB_OBJ(inst->transp_obj)->frm_is_bcast(inst->transp_obj)) {
|
||||
length = mbm_obj->pdu_snd_len;
|
||||
for (int j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++) {
|
||||
mbm_set_dest_addr(inst, j);
|
||||
exception = mbm_check_invoke_handler(inst, mbm_obj->func_code, mbm_obj->rcv_frame, &length);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), invoke handler.", MB_OBJ_PARENT(inst), (int)mbm_obj->func_code);
|
||||
exception = mbm_check_invoke_handler(inst, mbm_obj->func_code, mbm_obj->rcv_frame, &mbm_obj->pdu_rcv_len);
|
||||
}
|
||||
/* If master has exception, will send error process event. Otherwise the master is idle.*/
|
||||
if (exception != MB_EX_NONE) {
|
||||
|
||||
+145
-54
@@ -20,7 +20,7 @@ static const char *TAG = "mb_object.slave";
|
||||
|
||||
#if (MB_SLAVE_ASCII_ENABLED || MB_SLAVE_RTU_ENABLED)
|
||||
|
||||
typedef struct _port_serial_opts mb_serial_opts_t;
|
||||
typedef struct port_serial_opts_s mb_serial_opts_t;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,7 +30,6 @@ typedef struct
|
||||
// here are slave object properties and methods
|
||||
uint8_t mb_address;
|
||||
mb_comm_mode_t cur_mode;
|
||||
const mb_fn_handler_t *func_handlers;
|
||||
mb_state_enum_t cur_state;
|
||||
uint8_t *frame;
|
||||
uint16_t length;
|
||||
@@ -38,47 +37,139 @@ typedef struct
|
||||
uint8_t rcv_addr;
|
||||
uint64_t curr_trans_id;
|
||||
volatile uint16_t *pdu_snd_len;
|
||||
handler_descriptor_t handler_descriptor;
|
||||
} mbs_object_t;
|
||||
|
||||
static mb_fn_handler_t slave_handlers[MB_FUNC_HANDLERS_MAX] =
|
||||
{
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
{MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbs_fn_report_slave_id},
|
||||
#endif
|
||||
#if MB_FUNC_READ_INPUT_ENABLED
|
||||
{MB_FUNC_READ_INPUT_REGISTER, (void *)mbs_fn_read_input_reg},
|
||||
#endif
|
||||
#if MB_FUNC_READ_HOLDING_ENABLED
|
||||
{MB_FUNC_READ_HOLDING_REGISTER, (void *)mbs_fn_read_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
|
||||
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_write_multi_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_HOLDING_ENABLED
|
||||
{MB_FUNC_WRITE_REGISTER, (void *)mbs_fn_write_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_READWRITE_HOLDING_ENABLED
|
||||
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_rw_multi_holding_reg},
|
||||
#endif
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
{MB_FUNC_READ_COILS, (void *)mbs_fn_read_coils},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_COIL_ENABLED
|
||||
{MB_FUNC_WRITE_SINGLE_COIL, (void *)mbs_fn_write_coil},
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
|
||||
{MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbs_fn_write_multi_coils},
|
||||
#endif
|
||||
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
|
||||
{MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbs_fn_read_discrete_inp},
|
||||
#endif
|
||||
};
|
||||
|
||||
mb_err_enum_t mbs_delete(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_enable(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_disable(mb_base_t *inst);
|
||||
mb_err_enum_t mbs_poll(mb_base_t *inst);
|
||||
|
||||
// The helper function to register custom function handler for slave
|
||||
mb_err_enum_t mbs_set_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp phandler)
|
||||
{
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_err_enum_t status = MB_EINVAL;
|
||||
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
status = mb_set_handler(&mbs_obj->handler_descriptor, func_code, phandler);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// The helper function to register custom function handler for slave
|
||||
mb_err_enum_t mbs_get_handler(mb_base_t *inst, uint8_t func_code, mb_fn_handler_fp *phandler)
|
||||
{
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_err_enum_t status = MB_EINVAL;
|
||||
if (phandler) {
|
||||
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
status = mb_get_handler(&mbs_obj->handler_descriptor, func_code, phandler);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
mb_err_enum_t mbs_delete_handler(mb_base_t *inst, uint8_t func_code)
|
||||
{
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_err_enum_t status = MB_EILLSTATE;
|
||||
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
status = mb_delete_handler(&mbs_obj->handler_descriptor, func_code);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
mb_err_enum_t mbs_get_handler_count(mb_base_t *inst, uint16_t *pcount)
|
||||
{
|
||||
MB_RETURN_ON_FALSE((pcount && inst), MB_EINVAL, TAG, "get handler count wrong arguments");
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
*pcount = mbs_obj->handler_descriptor.count;
|
||||
}
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
static mb_exception_t mbs_check_invoke_handler(mb_base_t *inst, uint8_t func_code, uint8_t *pbuf, uint16_t *plen)
|
||||
{
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_exception_t exception = MB_EX_ILLEGAL_FUNCTION;
|
||||
if (!func_code || (func_code & MB_FUNC_ERROR)) {
|
||||
return MB_EX_ILLEGAL_FUNCTION;
|
||||
}
|
||||
SEMA_SECTION(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS) {
|
||||
mb_fn_handler_fp phandler = NULL;
|
||||
mb_err_enum_t status = mb_get_handler(&mbs_obj->handler_descriptor, func_code, &phandler);
|
||||
if ((status == MB_ENOERR) && phandler) {
|
||||
exception = phandler(inst, pbuf, plen);
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), invoke handler %p.", MB_OBJ_PARENT(inst), (int)func_code, phandler);
|
||||
}
|
||||
}
|
||||
return exception;
|
||||
}
|
||||
|
||||
static mb_err_enum_t mbs_register_default_handlers(mb_base_t *inst)
|
||||
{
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
mb_err_enum_t err = MB_EILLSTATE;
|
||||
LIST_INIT(&mbs_obj->handler_descriptor.head);
|
||||
mbs_obj->handler_descriptor.sema = xSemaphoreCreateBinary();
|
||||
(void)xSemaphoreGive(mbs_obj->handler_descriptor.sema);
|
||||
mbs_obj->handler_descriptor.instance = (void *)inst->descr.parent;
|
||||
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_OTHER_REPORT_SLAVEID, (void *)mbs_fn_report_slave_id);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_INPUT_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_READ_INPUT_REGISTER, (void *)mbs_fn_read_input_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_HOLDING_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_READ_HOLDING_REGISTER, (void *)mbs_fn_read_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_write_multi_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_HOLDING_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_WRITE_REGISTER, (void *)mbs_fn_write_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READWRITE_HOLDING_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_READWRITE_MULTIPLE_REGISTERS, (void *)mbs_fn_rw_multi_holding_reg);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_COILS_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_READ_COILS, (void *)mbs_fn_read_coils);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_COIL_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_WRITE_SINGLE_COIL, (void *)mbs_fn_write_coil);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_WRITE_MULTIPLE_COILS, (void *)mbs_fn_write_multi_coils);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED
|
||||
err = mbs_set_handler(inst, MB_FUNC_READ_DISCRETE_INPUTS, (void *)mbs_fn_read_discrete_inp);
|
||||
MB_RETURN_ON_FALSE((err == MB_ENOERR), err, TAG, "handler registration error = (0x%x).", (int)err);
|
||||
#endif
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
static mb_err_enum_t mbs_unregister_handlers(mb_base_t *inst)
|
||||
{
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
(void)xSemaphoreTake(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
|
||||
ESP_LOGD(TAG, "Close %s command handlers.", mbs_obj->base.descr.parent_name);
|
||||
(void)mb_delete_command_handlers(&mbs_obj->handler_descriptor);
|
||||
mbs_obj->handler_descriptor.instance = NULL;
|
||||
(void)xSemaphoreGive(mbs_obj->handler_descriptor.sema);
|
||||
vSemaphoreDelete(mbs_obj->handler_descriptor.sema);
|
||||
return MB_ENOERR;
|
||||
}
|
||||
|
||||
#if (MB_SLAVE_RTU_ENABLED)
|
||||
|
||||
mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
@@ -111,7 +202,9 @@ mb_err_enum_t mbs_rtu_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
ret = mbs_rtu_transp_create(ser_opts, (void **)&transp_obj);
|
||||
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
|
||||
TAG, "transport creation, err: %d", (int)ret);
|
||||
mbs_obj->func_handlers = slave_handlers;
|
||||
ret = mbs_register_default_handlers(&mbs_obj->base);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
|
||||
TAG, "default handlers registration fail, err: %d", (int)ret);
|
||||
mbs_obj->cur_mode = ser_opts->mode;
|
||||
mbs_obj->mb_address = ser_opts->uid;
|
||||
mbs_obj->cur_state = STATE_DISABLED;
|
||||
@@ -126,6 +219,7 @@ error:
|
||||
if (transp_obj) {
|
||||
mbs_rtu_transp_delete(transp_obj);
|
||||
}
|
||||
(void)mbs_unregister_handlers(&mbs_obj->base);
|
||||
free(mbs_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(mbs_obj->base.lock);
|
||||
free(mbs_obj);
|
||||
@@ -166,7 +260,9 @@ mb_err_enum_t mbs_ascii_create(mb_serial_opts_t *ser_opts, void **in_out_obj)
|
||||
ret = mbs_ascii_transp_create(ser_opts, (void **)&transp_obj);
|
||||
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
|
||||
TAG, "transport creation, err: %d", (int)ret);
|
||||
mbs_obj->func_handlers = slave_handlers;
|
||||
ret = mbs_register_default_handlers(&mbs_obj->base);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
|
||||
TAG, "default handlers registration fail, err: %d", (int)ret);
|
||||
mbs_obj->cur_mode = ser_opts->mode;
|
||||
mbs_obj->mb_address = ser_opts->uid;
|
||||
mbs_obj->cur_state = STATE_DISABLED;
|
||||
@@ -181,6 +277,7 @@ error:
|
||||
if (transp_obj) {
|
||||
mbs_ascii_transp_delete(transp_obj);
|
||||
}
|
||||
(void)mbs_unregister_handlers(&mbs_obj->base);
|
||||
free(mbs_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(mbs_obj->base.lock);
|
||||
free(mbs_obj);
|
||||
@@ -222,7 +319,9 @@ mb_err_enum_t mbs_tcp_create(mb_tcp_opts_t *tcp_opts, void **in_out_obj)
|
||||
ret = mbs_tcp_transp_create(tcp_opts, (void **)&transp_obj);
|
||||
MB_GOTO_ON_FALSE((transp_obj && (ret == MB_ENOERR)), MB_EILLSTATE, error,
|
||||
TAG, "transport creation, err: %d", (int)ret);
|
||||
mbs_obj->func_handlers = slave_handlers;
|
||||
ret = mbs_register_default_handlers(&mbs_obj->base);
|
||||
MB_GOTO_ON_FALSE((ret == MB_ENOERR), MB_EILLSTATE, error,
|
||||
TAG, "default handlers registration fail, err: %d", (int)ret);
|
||||
mbs_obj->cur_mode = tcp_opts->mode;
|
||||
mbs_obj->mb_address = tcp_opts->uid;
|
||||
mbs_obj->cur_state = STATE_DISABLED;
|
||||
@@ -237,6 +336,7 @@ error:
|
||||
if (transp_obj) {
|
||||
mbs_tcp_transp_delete(transp_obj);
|
||||
}
|
||||
(void)mbs_unregister_handlers(&mbs_obj->base);
|
||||
free(mbs_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(mbs_obj->base.lock);
|
||||
free(mbs_obj);
|
||||
@@ -264,6 +364,7 @@ mb_err_enum_t mbs_delete(mb_base_t *inst)
|
||||
mbs_obj->base.obj_id_chunks = 0;
|
||||
}
|
||||
#endif
|
||||
(void)mbs_unregister_handlers(&mbs_obj->base);
|
||||
// delete the modbus instance
|
||||
free(mbs_obj->base.descr.parent_name);
|
||||
CRITICAL_SECTION_CLOSE(inst->lock);
|
||||
@@ -301,7 +402,10 @@ mb_err_enum_t mbs_enable(mb_base_t *inst)
|
||||
mb_err_enum_t mbs_disable(mb_base_t *inst)
|
||||
{
|
||||
mb_err_enum_t status = MB_ENOERR;
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);;
|
||||
mbs_object_t *mbs_obj = MB_GET_OBJ_CTX(inst, mbs_object_t, base);
|
||||
// Wait for handler to be unlocked before disable the object
|
||||
(void)xSemaphoreTake(mbs_obj->handler_descriptor.sema, MB_HANDLER_UNLOCK_TICKS);
|
||||
(void)xSemaphoreGive(mbs_obj->handler_descriptor.sema);
|
||||
CRITICAL_SECTION(inst->lock) {
|
||||
if (mbs_obj->cur_state == STATE_ENABLED) {
|
||||
MB_OBJ(mbs_obj->base.transp_obj)->frm_stop(mbs_obj->base.transp_obj);
|
||||
@@ -358,20 +462,7 @@ mb_err_enum_t mbs_poll(mb_base_t *inst)
|
||||
MB_RETURN_ON_FALSE(mbs_obj->frame, MB_EILLSTATE, TAG, "receive buffer fail.");
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT":EV_EXECUTE", MB_OBJ_PARENT(inst));
|
||||
mbs_obj->func_code = mbs_obj->frame[MB_PDU_FUNC_OFF];
|
||||
exception = MB_EX_ILLEGAL_FUNCTION;
|
||||
// If receive frame has exception. The receive function code highest bit is 1.
|
||||
for (int i = 0; (i < MB_FUNC_HANDLERS_MAX); i++) {
|
||||
// No more function handlers registered. Abort.
|
||||
if (mbs_obj->func_handlers[i].func_code == 0) {
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), handler is not found.", MB_OBJ_PARENT(inst), (int)mbs_obj->func_code);
|
||||
break;
|
||||
}
|
||||
if ((mbs_obj->func_handlers[i].func_code) == mbs_obj->func_code) {
|
||||
ESP_LOGD(TAG, MB_OBJ_FMT": function (0x%x), start handler.", MB_OBJ_PARENT(inst), (int)mbs_obj->func_code);
|
||||
exception = mbs_obj->func_handlers[i].handler(inst, mbs_obj->frame, &mbs_obj->length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
exception = mbs_check_invoke_handler(inst, mbs_obj->func_code, mbs_obj->frame, &mbs_obj->length);
|
||||
// If the request was not sent to the broadcast address, return a reply.
|
||||
if ((mbs_obj->rcv_addr != MB_ADDRESS_BROADCAST) || (mbs_obj->cur_mode == MB_TCP)) {
|
||||
if (exception != MB_EX_NONE) {
|
||||
|
||||
Reference in new issue
Block a user