Modbus plugin development example

The southbound driver development mainly includes the following parts, the bottom layer is the protocol layer development, and the outermost layer is the driver layer development. | Modules | Documentation | Instructions | | —————————————- | ————————————————————- | ——————————————————————————————————————————- | | Protocol layer development | modbus.c modbus.h | Packing and unpacking of device protocol packets connected by plugins | | Protocol stack analysis | modbus_stack.c modbus_stack.h | Mainly used for protocol stack analysis | | Point processing | modbus_point.c modbus_point.h | The tag type in Neuron is converted to a more specific type required by the plugin | | Driver layer development | modbus_tcp.c modbus_req.c modbus_req.h | Implementation of plugin theme framework | | Plugin Settings File | modbus-tcp.json | Definition of Plugin Settings File |

Constant description

The main constants in Neuron are defined in the header file .

Constant Description
NEU_TAG_NAME_LEN Maximum length of point name, 32
NEU_TAG_ADDRESS_LEN Maximum length of point address, 64
NEU_TAG_DESCRIPTION_LEN Maximum length of point description string, 128
NEU_GROUP_NAME_LEN Maximum length of group name, 32
NEU_GROUP_INTERVAL_LIMIT Minimum interval for the group, 100
NEU_NODE_NAME_LEN Node name maximum length, 32
NEU_PLUGIN_NAME_LEN plugin name maximum length, 32
NEU_PLUGIN_LIBRARY_LEN Plug-in dynamic library file name maximum length, 32
NEU_PLUGIN_DESCRIPTION_LEN Plugin description string maximum length, 512
NEU_DRIVER_TAG_CACHE_EXPIRE_TIME Driving point cache expiration time, 30
NEU_APP_SUBSCRIBE_MSG_SIZE size of northbound subscription message, 4
NEU_TAG_FLOAG_PRECISION_MAX Maximum value of floating point type precision, 17
  1. typedef enum neu_plugin_kind {
  2. NEU_PLUGIN_KIND_STATIC = 0,
  3. NEU_PLUGIN_KIND_SYSTEM = 1,
  4. NEU_PLUGIN_KIND_CUSTOM = 2,
  5. } neu_plugin_kind_e;

Plugin type:

  • NEU_PLUGIN_KIND_STATIC, Neuron built-in plugin, no dynamic library file required;
  • NEU_PLUGIN_KIND_SYSTEM, system plugin, needs to be stored as a dynamic library file, which can be loaded by Neuron;
  • NEU_PLUGIN_KIND_CUSTOM, custom plugin, needs to be stored as a dynamic library file, which can be loaded by Neuron;
  1. typedef enum {
  2. NEU_NA_TYPE_DRIVER = 1,
  3. NEU_NA_TYPE_APP = 2,
  4. } neu_adapter_type_e,
  5. neu_node_type_e;

Node type, NEU_NA_TYPE_DRIVER represents the southbound node, NEU_NA_TYPE_APP represents the northbound node.

  1. typedef enum {
  2. NEU_NODE_LINK_STATE_DISCONNECTED = 0,
  3. NEU_NODE_LINK_STATE_CONNECTED = 1,
  4. } neu_node_link_state_e;

Node connection status, NEU_NODE_LINK_STATE_DISCONNECTED means disconnected, NEU_NODE_LINK_STATE_CONNECTED means connected.

  1. typedef enum {
  2. NEU_NODE_RUNNING_STATE_IDLE = 0,
  3. NEU_NODE_RUNNING_STATE_INIT = 1,
  4. NEU_NODE_RUNNING_STATE_READY = 2,
  5. NEU_NODE_RUNNING_STATE_RUNNING = 3,
  6. NEU_NODE_RUNNING_STATE_STOPPED = 4,
  7. } neu_node_running_state_e;

Node running status:

  • NEU_NODE_RUNNING_STATE_IDLE, in the driver configuration, enter the configuration;
  • NEU_NODE_RUNNING_STATE_INIT, after the node is created successfully, enter the initialization;
  • NEU_NODE_RUNNING_STATE_READY, after the driver configuration is completed, enter ready;
  • NEU_NODE_RUNNING_STATE_RUNNING, open the node working state and enter the running;
  • NEU_NODE_RUNNING_STATE_STOPPED, close the node working state and enter stop;

Step 1 Protocol layer development

This part mainly implements the implementation of the grouping and unpacking of the device protocol connected by the plug-in, and also includes the implementation of some structure definitions and related functions, among which modbus.h defines the basic message of the modbus protocol and the grouping of the message. Packing and unpacking functions, modbus.c implements the functions of packing and unpacking.

When the Modbus protocol is transmitted through TCP, the format of the read and write request message is shown in the table below.

Message Format Data Length Description
Header 6 bytes Application header, including transport ID, protocol ID, and byte length
Address code 1 byte Device address, also site number
Function code 1 byte Notify which operation to perform, different function codes correspond to different operations
Register start address 2 bytes Specify the start address of the register to be read, high byte first, low byte last
Number of registers 2 bytes Specify the number of registers to read, high byte first, low byte last
Data field N bytes Used in the write instruction, the value to be written to the register

Enumeration Description

Enumeration Description
modbus_function Modbus different function codes
modbus_area Different register types

modbus_code code analysis

modbus_header, modbus_code, modbus_address, modbus_data code implementation is similar, take modbus_header as an example for code analysis.

  1. // Pack modbus_header into protocol_pack_buf
  2. void modbus_header_wrap(neu_protocol_pack_buf_t *buf, uint16_t seq)
  3. {
  4. // Check if there is enough space in protocol_pack_buf to store the structure of modbus_header
  5. assert(neu_protocol_pack_buf_unused_size(buf) >=
  6. sizeof(struct modbus_header));
  7. // Allocate memory for modbus_header in protocol_pack_buf and return the starting address of modbus_header
  8. struct modbus_header *header =
  9. (struct modbus_header *) neu_protocol_pack_buf(
  10. buf, sizeof(struct modbus_header));
  11. // Assign value to header
  12. header->seq = htons(seq);
  13. header->protocol = 0x0;
  14. header->len = htons(neu_protocol_pack_buf_used_size(buf) -
  15. sizeof(struct modbus_header));
  16. }
  17. // Parse modbus_header from received protocol_unpack_buf
  18. int modbus_header_unwrap(neu_protocol_unpack_buf_t *buf,
  19. struct modbus_header * out_header)
  20. {
  21. // Get the start address of modbus_header from protocol_unpack_buf
  22. struct modbus_header *header =
  23. (struct modbus_header *) neu_protocol_unpack_buf(
  24. buf, sizeof(struct modbus_header));
  25. // When the data size in protocol_unpack_buf is less than the size of modbus_header, return 0
  26. if (header == NULL ||
  27. ntohs(header->len) > neu_protocol_unpack_buf_unused_size(buf)) {
  28. return 0;
  29. }
  30. // When the parsed packet does not conform to the protocol specification, return -1
  31. if (header->protocol != 0x0) {
  32. return -1;
  33. }
  34. *out_header = *header;
  35. out_header->len = ntohs(out_header->len);
  36. out_header->seq = ntohs(out_header->seq);
  37. // When parsing mmmodbus_header is successful, return the parsed data size
  38. return sizeof(struct modbus_header);
  39. }

Step 2 Protocol stack analysis

This part is mainly used for the analysis of the protocol stack, including the initialization of the protocol stack analysis and the release of resources, the generation of read and write request messages, the analysis of received read and write response messages, and data processing.

function name description
modbus_stack_create Mainly used for protocol stack parsing initialization.
modbus_stack_destroy Mainly used to release protocol stack parsing resources.
modbus_stack_recv Mainly used to pass the data received from tcp/udp/serial port to the protocol stack parser, and process and analyze the data.
modbus_stack_read Mainly used to generate the protocol message of the point to be read through the protocol parser.
modbus_stack_write Mainly used to generate the protocol message to be written to the point value through the protocol parser.

Step 3 Point processing

This part is mainly used to convert the tag information set in Neuron into the more specific information needed.

  • The address configured by the point in Neuron can be further parsed into the device address Slave ID, register type and register start address, and can check whether the address conforms to the plug-in specification.
  • Different registers support different data types. Here, you can judge whether the configured address supports this attribute according to the parsed register type and the attributes configured in Neuron.
  • Modbus supports batch reading of data. Neuron can aggregate and classify user-configured points. According to the classification results, the function of reading data in batches can be realized.

Step 4 Driver layer development

The modbus_req.c and modbus_req.h files define the specific function implementation and structure used in the modbus_tcp.c file.The modbus_tcp.c file is mainly the interface exported by the plugin, which is defined by plugin.h in neuron. The description of plugin_intf_funs is as follows.

open

Call the driver_open function, and create a structure struct neu_plugin defined by the plugin itself based on the first function called by neuron when the plugin creates a node. This structure is defined in modbus_req.h. It should be noted that the first member of the structure must be neu_plugin_common_t common, and other members can be added according to the specific implementation of the driver.

  1. static neu_plugin_t *driver_open(void)
  2. {
  3. neu_plugin_t *plugin = calloc(1, sizeof(neu_plugin_t));
  4. neu_plugin_common_init(&plugin->common);
  5. return plugin;
  6. }

close

Call the driver_close function, the last function called by neuron when a node is removed to free the neu_plugin_t created by open.

  1. static int driver_close(neu_plugin_t *plugin)
  2. {
  3. free(plugin);
  4. return 0;
  5. }

init

Call the driver_init function, when creating a node, the function that neuron calls immediately after open is called. This function is mainly used for some resources that need to be initialized in the plugin. The modbus plugin mainly initializes the modbus protocol stack analysis.The callback functions are implemented in the modbus_req file.

  1. static int driver_init(neu_plugin_t *plugin)
  2. {
  3. plog_info(plugin, "node: modbus init");
  4. return 0;
  5. }

uninit

Call the driver_uninit function, when deleting a node, the first function called by neuron, this function mainly releases some resources applied and initialized in init.

  1. static int driver_uninit(neu_plugin_t *plugin)
  2. {
  3. plog_info(plugin, "node: modbus uninit");
  4. return 0;
  5. }

start

Call the driver_start function, when the user clicks on the neuron node page, neuron will call this function, start does not do any processing, only returns 0, notifies the plugin to start running, and starts to connect to the device, etc. The processing of the connection status is placed in the tcp connection Processed in an asynchronous callback function.

modbus-start

stop

Call the driver_stop function, when the user clicks stop on the neuron node page, neuron will call this function, stop notifies the plug-in to stop running, close the connection with the device, and the driver.group_timer will not be triggered again.

modbus-stop

setting

Call the driver_config function, it is used by the user when setting on the neuron node setting page. The parameters set by the node will be presented in json mode, and neuron will notify the plugin to set through this function.The driver_config function first parses and saves the configuration information, and then establishes a tcp connection. The two callback functions modbus_conn_connected and modbus_conn_disconnected will be called when tcp establishes and closes the connection respectively, and the corresponding two functions will update the connection status of the plugin.

modbus-config

  1. static int driver_config(neu_plugin_t *plugin, const char *config)
  2. {
  3. plog_info(plugin, "config: %s", config);
  4. return 0;
  5. }

request

Call the driver_request function, the function is not used in the southbound driver.

driver.validate_tag

Call the driver_validate_tag function, when adding a tag to a node or updating a tag, neuron will use this function to notify the plug-in of the tag-related parameters. The plug-in checks whether the tag parameter meets the requirements of the plug-in according to their respective implementations. The function returns 0, which means success.

  1. static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag)
  2. {
  3. plog_info(plugin, "validate tag: %s", tag->name);
  4. return 0;
  5. }

driver.group_timer

Call the driver_group_timer function, after adding a group to the node and setting the node state to running, this function will be called regularly with the interval parameter of the group, mainly used to interact with the device and read device data.

neu_plugin_group_t structure parameter description.

Parameters Description
group_name The name of the group that triggered the timer
tags array of type neu_datatag_t
user_data User-defined information
group_free When this group is deleted, the callback function used to release user-defined information
  1. static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
  2. {
  3. (void) plugin;
  4. (void) group;
  5. plog_info(plugin, "timer....");
  6. return 0;
  7. }

driver.write_tag

Call the driver_write function, when using the write api, neuron will call this function to notify the plugin to write a specific value to the point tag.

  1. static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
  2. neu_value_u value)
  3. {
  4. (void) plugin;
  5. (void) req;
  6. (void) tag;
  7. (void) value;
  8. return 0;
  9. }

Step 5 Plugin settings file

The modbus-tcp.json file configures the plugin settings, among which tag_type and timeout are required items. Other parameters can be added according to the plugin configuration. The parameter descriptions are shown in the following table.

Parameters Description
tag_regex Regex for address configuration for drivers that support different data types
description A detailed description of the field
attribute The attribute of the field, there are only two optional and required options, namely required and optional
type The type of the field, currently int and string are commonly used
default The default value to fill in
valid The range that this field can be filled in