Edouard@1912: /* File generated by Beremiz (PlugGenerate_C method of modbus Plugin instance) */ Edouard@1912: Edouard@1912: /* Edouard@1912: * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt) Edouard@1912: * Edouard@1912: * This file is part of the Modbus library for Beremiz and matiec. Edouard@1912: * Edouard@1912: * This Modbus library is free software: you can redistribute it and/or modify Edouard@1912: * it under the terms of the GNU Lesser General Public License as published by Edouard@2019: * the Free Software Foundation, either version 2 of the License, or Edouard@1912: * (at your option) any later version. Edouard@1912: * Edouard@1912: * This program is distributed in the hope that it will be useful, but Edouard@1912: * WITHOUT ANY WARRANTY; without even the implied warranty of Edouard@1912: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser Edouard@1912: * General Public License for more details. Edouard@1912: * Edouard@1912: * You should have received a copy of the GNU Lesser General Public License Edouard@1912: * along with this Modbus library. If not, see . Edouard@1912: * Edouard@1912: * This code is made available on the understanding that it will not be Edouard@1912: * used in safety-critical situations without a full and competent review. Edouard@1912: */ Edouard@1912: Edouard@1912: #include "mb_addr.h" Edouard@1912: #include "mb_tcp_private.h" Edouard@1912: #include "mb_master_private.h" Edouard@1912: Edouard@1912: Edouard@1912: Edouard@1912: #define DEF_REQ_SEND_RETRIES 0 Edouard@1912: msousa@2654: msousa@2654: #define MODBUS_PARAM_STRING_SIZE 64 msousa@2654: msousa@2654: Edouard@1912: // Used by the Modbus server node Edouard@1912: #define MEM_AREA_SIZE 65536 Edouard@1912: typedef struct{ Edouard@1912: u16 ro_bits [MEM_AREA_SIZE]; Edouard@1912: u16 rw_bits [MEM_AREA_SIZE]; Edouard@1912: u16 ro_words[MEM_AREA_SIZE]; Edouard@1912: u16 rw_words[MEM_AREA_SIZE]; msousa@2721: /* Two flags to count the number of Modbus requests (read and write) we have msousa@2722: * successfully received from any remote Modbus master. msousa@2722: * Two boolean flags that are set whenever we successfully process a msousa@2722: * Modbus request sent from a remote client. msousa@2722: * These flags will be mapped onto located variables msousa@2721: * so the user's IEC 61131-3 code can check whether we are being msousa@2721: * polled by a Modbus master. msousa@2721: * The counters will roll over to 0 upon reaching maximum value. msousa@2722: * The user will probably periodically reset the boolean flags to false, msousa@2722: * and use this as a communication timeout msousa@2722: * (when it remains false in two consecutive periods) msousa@2722: * msousa@2722: * u8 for BOOL variable/flag msousa@2722: * u32 for UDINT variable/counter msousa@2721: */ msousa@2722: u8 flag_write_req_flag; msousa@2722: u8 flag_read_req_flag; msousa@2722: u32 flag_write_req_counter; msousa@2722: u32 flag_read_req_counter; Edouard@1912: } server_mem_t; Edouard@1912: msousa@2654: msousa@2654: /* msousa@2654: * Beremiz has a program to run on the PLC (Beremiz_service.py) msousa@2654: * to handle downloading of compiled programs, start/stop of PLC, etc. msousa@2654: * (see runtime/PLCObject.py for start/stop, loading, ...) msousa@2654: * msousa@2654: * This service also includes a web server to access PLC state (start/stop) msousa@2654: * and to change some basic confiuration parameters. msousa@2654: * (see runtime/NevowServer.py for the web server) msousa@2654: * msousa@2654: * The web server allows for extensions, where additional configuration msousa@2654: * parameters may be changed on the running/downloaded PLC. msousa@2654: * Modbus plugin also comes with an extension to the web server, through msousa@2654: * which the basic Modbus plugin configuration parameters may be changed msousa@2654: * msousa@2654: * This means that most values in the server_node_t and client_node_t msousa@2654: * may be changed after the co,piled code (.so file) is loaded into msousa@2654: * memory, and before the code starts executing. msousa@2654: * Since the we will also want to change the host and port (TCP) and the msousa@2654: * serial device (RTU) at this time, it is best if we allocate memory for msousa@2654: * these strings that may be overwritten by the web server (i.e., do not use msousa@2654: * const strings) in the server_node_t and client_node_t structures. msousa@2654: * msousa@2654: * The following structure members msousa@2654: * - node_addr_t.addr.tcp.host msousa@2654: * - node_addr_t.addr.tcp.service (i.e. the port) msousa@2654: * - node_addr_t.addr.rtu.device msousa@2654: * are all char *, and do not allocate memory for the strings. msousa@2654: * msousa@2654: * We therefore include two generic char arrays, str1 and str2, msousa@2654: * that will store the above strings, and the C code will initiliaze msousa@2654: * the node_addre_t.addr string pointers to these strings. msousa@2654: * i.e., either addr.rtu.device will point to str1, msousa@2654: * or msousa@2654: * addr.tcp.host and addr.tcp.service msousa@2654: * will point to str1 and str2 respectively msousa@2654: */ Edouard@1912: typedef struct{ Edouard@1912: const char *location; msousa@2654: const char *config_name; msousa@2655: char str1[MODBUS_PARAM_STRING_SIZE]; msousa@2655: char str2[MODBUS_PARAM_STRING_SIZE]; Edouard@1912: u8 slave_id; Edouard@1912: node_addr_t node_address; Edouard@1912: int mb_nd; // modbus library node used for this server Edouard@1912: int init_state; // store how far along the server's initialization has progressed msousa@2721: /* entries from this point forward are not statically initialized when the variable is declared */ msousa@2721: /* they will be initialized by the code itself in the init() function */ Edouard@1912: pthread_t thread_id; // thread handling this server Edouard@1912: server_mem_t mem_area; Edouard@1912: } server_node_t; Edouard@1912: Edouard@1912: Edouard@1912: // Used by the Modbus client node Edouard@1912: typedef struct{ Edouard@1912: const char *location; msousa@2654: const char *config_name; msousa@2654: char str1[MODBUS_PARAM_STRING_SIZE]; msousa@2654: char str2[MODBUS_PARAM_STRING_SIZE]; Edouard@1912: node_addr_t node_address; msousa@2647: int mb_nd; // modbus library node used for this client Edouard@1912: int init_state; // store how far along the client's initialization has progressed msousa@2647: u64 comm_period;// period to use when periodically sending requests to remote server Edouard@1912: int prev_error; // error code of the last printed error message (0 when no error) msousa@2647: pthread_t thread_id; // thread handling all communication for this client node Edouard@2683: pthread_t timer_thread_id; // thread handling periodical timer for this client node msousa@2647: pthread_mutex_t mutex; // mutex to be used with the following condition variable msousa@2647: pthread_cond_t condv; // used to signal the client thread when to start new modbus transactions msousa@2647: int execute_req; /* used, in association with condition variable, msousa@2647: * to signal when to send the modbus request to the server msousa@2647: * Note that we cannot simply rely on the condition variable to signal msousa@2647: * when to activate the client thread, as the call to msousa@2647: * pthread_cond_wait() may return without having been signaled! msousa@2647: * From the manual: msousa@2647: * Spurious wakeups from the msousa@2647: * pthread_cond_timedwait() or pthread_cond_wait() functions may occur. msousa@2647: * Since the return from pthread_cond_timedwait() or pthread_cond_wait() msousa@2647: * does not imply anything about the value of this predicate, the predi- msousa@2647: * cate should be re-evaluated upon such return. msousa@2647: */ msousa@2647: int periodic_act; /* (boolen) flag will be set when the client node's thread was activated msousa@2647: * (by signaling the above condition variable) by the periodic timer. msousa@2647: * Note that this same thread may also be activated (condition variable is signaled) msousa@2647: * by other sources, such as when the user program requests that a specific msousa@2647: * client MB transation be executed (flag_exec_req in client_request_t) msousa@2647: */ Edouard@1912: } client_node_t; Edouard@1912: Edouard@1912: Edouard@1912: // Used by the Modbus client plugin Edouard@1912: typedef enum { Edouard@1912: req_input, Edouard@1912: req_output, Edouard@1912: no_request /* just for tests to quickly disable a request */ Edouard@1912: } iotype_t; Edouard@1912: Edouard@1912: #define REQ_BUF_SIZE 2000 Edouard@1912: typedef struct{ Edouard@1912: const char *location; Edouard@1912: int client_node_id; Edouard@1912: u8 slave_id; Edouard@1912: iotype_t req_type; Edouard@1912: u8 mb_function; Edouard@1912: u16 address; Edouard@1912: u16 count; Edouard@1912: int retries; msousa@2713: u8 mb_error_code; // modbus error code (if any) of last executed request msousa@2713: u8 tn_error_code; // transaction error code (if any) of last executed request Edouard@1912: int prev_error; // error code of the last printed error message (0 when no error) Edouard@1912: struct timespec resp_timeout; msousa@2647: u8 write_on_change; // boolean flag. If true => execute MB request when data to send changes Edouard@1912: // buffer used to store located PLC variables Edouard@1912: u16 plcv_buffer[REQ_BUF_SIZE]; Edouard@1912: // buffer used to store data coming from / going to server Edouard@1912: u16 coms_buffer[REQ_BUF_SIZE]; Edouard@1912: pthread_mutex_t coms_buf_mutex; // mutex to access coms_buffer[] msousa@2647: /* boolean flag that will be mapped onto a (BOOL) located variable msousa@2715: * (u8 because IEC 61131-3 BOOL are mapped onto u8 in C code! ) msousa@2647: * -> allow PLC program to request when to start the MB transaction msousa@2647: * -> will be reset once the MB transaction has completed msousa@2647: */ msousa@2715: u8 flag_exec_req; msousa@2647: /* flag that works in conjunction with flag_exec_req msousa@2715: * (does not really need to be u8 as it is not mapped onto a located variable. ) msousa@2647: * -> used by internal logic to indicate that the client thread msousa@2647: * that will be executing the MB transaction msousa@2647: * requested by flag exec_req has already been activated. msousa@2647: * -> will be reset once the MB transaction has completed msousa@2647: */ msousa@2715: u8 flag_exec_started; msousa@2714: /* flag that will be mapped onto a (BYTE) located variable msousa@2714: * (u8 because the flag is a BYTE! ) msousa@2714: * -> will store the result of the last executed MB transaction msousa@2647: * 1 -> error accessing IP network, or serial interface msousa@2647: * 2 -> reply received from server was an invalid frame msousa@2647: * 3 -> server did not reply before timeout expired msousa@2713: * 4 -> server returned a valid Modbus error frame msousa@2647: * -> will be reset (set to 0) once this MB transaction has completed sucesfully msousa@2713: * msousa@2714: * In other words, this variable is a copy of tn_error_code, reset after each request attempt completes. msousa@2714: * We map this copy (instead of tn_error_code) onto a located variable in case the user program decides msousa@2714: * to overwrite its value and mess up the plugin logic. msousa@2714: */ msousa@2714: u8 flag_tn_error_code; msousa@2714: /* flag that will be mapped onto a (BYTE) located variable msousa@2714: * (u8 because the flag is a BYTE! ) msousa@2714: * -> if flag_tn_error_code is 4, this flag will store the MB error code returned by the MB server in a MB error frame msousa@2714: * -> will be reset (set to 0) once this MB transaction has completed succesfully msousa@2714: * msousa@2714: * In other words, this variable is a copy of mb_error_code, reset after each request attempt completes. msousa@2714: * We map this copy (instead of mb_error_code) onto a located variable in case the user program decides msousa@2714: * to overwrite its value and mess up the plugin logic. msousa@2714: */ msousa@2714: u8 flag_mb_error_code; Edouard@1912: } client_request_t; Edouard@1912: Edouard@1912: Edouard@1912: /* The total number of nodes, needed to support _all_ instances of the modbus plugin */ Edouard@1912: #define TOTAL_TCPNODE_COUNT %(total_tcpnode_count)s Edouard@1912: #define TOTAL_RTUNODE_COUNT %(total_rtunode_count)s Edouard@1912: #define TOTAL_ASCNODE_COUNT %(total_ascnode_count)s Edouard@1912: Edouard@1912: /* Values for instance %(locstr)s of the modbus plugin */ Edouard@1912: #define MAX_NUMBER_OF_TCPCLIENTS %(max_remote_tcpclient)s Edouard@1912: Edouard@1912: #define NUMBER_OF_TCPSERVER_NODES %(tcpserver_node_count)s Edouard@1912: #define NUMBER_OF_TCPCLIENT_NODES %(tcpclient_node_count)s Edouard@1912: #define NUMBER_OF_TCPCLIENT_REQTS %(tcpclient_reqs_count)s Edouard@1912: Edouard@1912: #define NUMBER_OF_RTUSERVER_NODES %(rtuserver_node_count)s Edouard@1912: #define NUMBER_OF_RTUCLIENT_NODES %(rtuclient_node_count)s Edouard@1912: #define NUMBER_OF_RTUCLIENT_REQTS %(rtuclient_reqs_count)s Edouard@1912: Edouard@1912: #define NUMBER_OF_ASCIISERVER_NODES %(ascserver_node_count)s Edouard@1912: #define NUMBER_OF_ASCIICLIENT_NODES %(ascclient_node_count)s Edouard@1912: #define NUMBER_OF_ASCIICLIENT_REQTS %(ascclient_reqs_count)s Edouard@1912: Edouard@1912: #define NUMBER_OF_SERVER_NODES (NUMBER_OF_TCPSERVER_NODES + \ Edouard@1912: NUMBER_OF_RTUSERVER_NODES + \ Edouard@1912: NUMBER_OF_ASCIISERVER_NODES) Edouard@1912: Edouard@1912: #define NUMBER_OF_CLIENT_NODES (NUMBER_OF_TCPCLIENT_NODES + \ Edouard@1912: NUMBER_OF_RTUCLIENT_NODES + \ Edouard@1912: NUMBER_OF_ASCIICLIENT_NODES) Edouard@1912: Edouard@1912: #define NUMBER_OF_CLIENT_REQTS (NUMBER_OF_TCPCLIENT_REQTS + \ Edouard@1912: NUMBER_OF_RTUCLIENT_REQTS + \ Edouard@1912: NUMBER_OF_ASCIICLIENT_REQTS) Edouard@1912: Edouard@1912: Edouard@1912: /*initialization following all parameters given by user in application*/ Edouard@1912: Edouard@1912: static client_node_t client_nodes[NUMBER_OF_CLIENT_NODES] = { Edouard@1912: %(client_nodes_params)s Edouard@1912: }; Edouard@1912: Edouard@1912: Edouard@1912: static client_request_t client_requests[NUMBER_OF_CLIENT_REQTS] = { Edouard@1912: %(client_req_params)s Edouard@1912: }; Edouard@1912: Edouard@1912: Edouard@1912: static server_node_t server_nodes[NUMBER_OF_SERVER_NODES] = { Edouard@1912: %(server_nodes_params)s Edouard@1912: } Edouard@1912: ; Edouard@1912: Edouard@1912: /*******************/ Edouard@1912: /*located variables*/ Edouard@1912: /*******************/ Edouard@1912: Edouard@1912: %(loc_vars)s Edouard@1912: