diff --git a/silecs-communication-cpp/Makefile b/silecs-communication-cpp/Makefile index 52cd4841a476a55b1363e6582998bdf224a29307..aae655d2fe51bd0923ad68a194c2f4479ffd6a73 100644 --- a/silecs-communication-cpp/Makefile +++ b/silecs-communication-cpp/Makefile @@ -33,5 +33,8 @@ COMPILER_FLAGS = -DMAJOR=$(MAJOR) -DMINOR=$(MINOR) -DPATCH=$(PATCH) # Comment IN/Out to enable NI-Support #COMPILER_FLAGS += -DNI_SUPPORT_ENABLED=TRUE +# Comment IN/Out to enable Modbus-Support +#COMPILER_FLAGS += -DMODBUS_SUPPORT_ENABLED=TRUE + # Include the generic make file include $(COMMON_MAKE_PATH)/Make.generic \ No newline at end of file diff --git a/silecs-communication-cpp/Makefile.dep b/silecs-communication-cpp/Makefile.dep index a0318fd8395f20d30bc860118669f08b8872dc1d..580e172e750328698c70864072fff0d9652f2886 100644 --- a/silecs-communication-cpp/Makefile.dep +++ b/silecs-communication-cpp/Makefile.dep @@ -4,6 +4,17 @@ LIBXML_PATH ?= /usr/include/libxml2/ SNAP7_BASE ?= ../snap7/snap7-full BOOST_HOME ?= /opt/gsi/3rdparty/boost/$(BOOST_VERSION) +ifdef MODBUS_SUPPORT_ENABLED + MODBUS_VERSION = 3.0.6 + MODBUS_ROOT=$(RELEASE_LOCATION)/$(CPU)/3rdparty/libmodbus-$(MODBUS_VERSION) + MODBUS_SRC=$(MODBUS_ROOT)/src + MODBUS_LIB=$(MODBUS_SRC)/.libs + MODBUS_RPATH=$(RELEASE_LOCATION)/$(CPU)/3rdparty/libmodbus-$(MODBUS_VERSION)/src/.libs +endif + +ifdef MODBUS_SUPPORT_ENABLED + DEPENDENT_COMPILER_OPTIONS += -I$(MODBUS_SRC) +endif DEPENDENT_COMPILER_OPTIONS += -I$(LIBXML_PATH) DEPENDENT_COMPILER_OPTIONS += -I$(SNAP7_BASE)/release/Wrappers/c-cpp DEPENDENT_COMPILER_OPTIONS += -I$(BOOST_HOME)/include \ No newline at end of file diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp index 5bf337428165e9bdf54137aac98b0d2d703f19a9..1711dfee6a0ef9aad69bd2624ce42bbf4e6cf68a 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp +++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.cpp @@ -12,7 +12,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. - +#ifdef MODBUS_SUPPORT_ENABLED #include <silecs-communication/interface/utility/SilecsLog.h> #include <silecs-communication/interface/utility/StringUtilities.h> #include <silecs-communication/interface/communication/SilecsConnection.h> @@ -26,6 +26,7 @@ namespace Silecs MBConnection::MBConnection(PLC* thePLC) : Connection(thePLC) { LOG(ALLOC) << "MBConnection (create): " << thePLC->getName(); + readCtx_ = writeCtx_ = NULL; } @@ -39,18 +40,19 @@ namespace Silecs bool MBConnection::open(PLC* thePLC) { //Open read and write Modbus channels (using IP address to limit the naming-server accesses) - readCh_ = IeMdbOpen(0, (char *) thePLC->getIPAddress().c_str(), (int)thePLC->getBaseAddress()); - writeCh_ = IeMdbOpen(0, (char *) thePLC->getIPAddress().c_str(), (int)thePLC->getBaseAddress()); - return ((readCh_ >= 0) && (writeCh_ >= 0)); + int readErr = IeMdbOpen(&readCtx_, NULL, (char *) thePLC->getIPAddress().c_str(), (int)thePLC->getBaseAddress()); + int writeErr = IeMdbOpen(&writeCtx_, NULL, (char *) thePLC->getIPAddress().c_str(), (int)thePLC->getBaseAddress()); + return ((readErr == 0) && (writeErr == 0)); } bool MBConnection::close(PLC* thePLC) { - int err = 0; - err |= IeMdbClose(readCh_); - err |= IeMdbClose(writeCh_); - return (err == 0); + int err = 0; + err |= IeMdbClose(readCtx_); + err |= IeMdbClose(writeCtx_); + readCtx_ = writeCtx_ = NULL; + return (err == 0); } @@ -78,7 +80,7 @@ namespace Silecs //DATA topic makes sense with RECV one if (RECV & Log::topics_) LOG(DATA) << "Read data, address: %MW" << addr << ", byte-size: " << size; - int error = IeMBreadData(readCh_, addr, (unsigned short)size, pBuffer); + int err = IeMBreadData(readCtx_, addr, (unsigned short)size, pBuffer); if(error) throw SilecsException(__FILE__, __LINE__, UNEXPECTED_ERROR," ModBus Error: " + string(IeGetErrorMessage(error))); } @@ -111,7 +113,7 @@ namespace Silecs //DATA topic makes sense with SEND one if (SEND & Log::topics_) LOG(DATA) << "Write data, address: %MW" << addr << ", byte-size: " << size; - int error = IeMBwriteData(writeCh_, (unsigned short)addr, (unsigned short)size, pBuffer); + int error = IeMBwriteData(writeCtx_, (unsigned short)addr, (unsigned short)size, pBuffer); if(error) throw SilecsException(__FILE__, __LINE__, UNEXPECTED_ERROR," ModBus Error: " + string(IeGetErrorMessage(error))); } @@ -119,3 +121,4 @@ namespace Silecs } } // namespace +#endif //MODBUS_SUPPORT_ENABLED diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h index 953cdd8d4c6b86d9bd6fd6bfd6f246fbe94c1900..9ea7f9cf8bebe1ea075d11259121a2cf0112bd8e 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h +++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/MBConnection.h @@ -12,7 +12,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. - +#ifdef MODBUS_SUPPORT_ENABLED #ifndef _MB_CONNECTION_H_ #define _MB_CONNECTION_H_ @@ -22,7 +22,6 @@ namespace Silecs { class PLC; - class Connection; /*! * \class MBConnection @@ -39,6 +38,8 @@ namespace Silecs int writeData(PLC* thePLC, unsigned long address, unsigned long offset, unsigned long size, unsigned char* pBuffer); private: + modbus_t* readCtx_; + modbus_t* writeCtx_; bool open(PLC* thePLC); bool close(PLC* thePLC); @@ -48,3 +49,4 @@ namespace Silecs #endif // _MB_CONNECTION_H_ +#endif //MODBUS_SUPPORT_ENABLED diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h index 1a86cc943d4a36b6bc29807a24309c38cc409d78..6e156f7cca3a93c330c5bae0126b341ae1c6dcb4 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h +++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SNAP7Connection.h @@ -17,12 +17,12 @@ #define _SNAP7_CONNECTION_H_ #include <silecs-communication/protocol/core/silecs.h> +#include <silecs-communication/interface/communication/SilecsConnection.h> #include <snap7.h> namespace Silecs { class PLC; - class Connection; /*! * \class SNAP7Connection diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.cpp b/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.cpp index 1cf18425468f6c6b07af9e89784d0896bff537dd..2ba89446a5c4d1e1c54c18f3cff4e565de920cda 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.cpp +++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.cpp @@ -22,7 +22,6 @@ namespace Silecs { // static definition - bool Connection::isInit_ = false; bool Connection::isAlive_ = false; const unsigned int Connection::numberConn_ = 3; @@ -42,12 +41,6 @@ namespace Silecs timeConn_ = NULL; delayConn_ = UrgentConnection; //initial reconnection delay remainConn_ = numberConn_; //initial number of attempt - - // Silecs initialization has to be invoked only once per process - if (isInit_== false) - { IeInit(); - isInit_ = true; - } } @@ -191,7 +184,6 @@ namespace Silecs { if (close(thePLC)) { - readCh_ = writeCh_ = 0; isConnected_ = false; //Connection status has changed: update the diagnostic variables diff --git a/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h b/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h index 0cc27ee12f37d9da1e33e31172d4ef4f14200e7c..3c6d04ccb9ec92b839f3c528cdc2f012c7539d71 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h +++ b/silecs-communication-cpp/src/silecs-communication/interface/communication/SilecsConnection.h @@ -147,10 +147,6 @@ namespace Silecs // flag used to enable/disable the transactions independently from the scheduling bool isEnabled_; - // Silecs descriptor for send/recv channels - ChannelID readCh_; - ChannelID writeCh_; - /* . read-channel and write channel are independent and can be accessed in parallel. * . Each channel must be protected against respective concurrent access. * . The global action (open,close,etc.) must be protected from any concurrent access. @@ -169,9 +165,6 @@ namespace Silecs static bool isAlive_; // PLC has repliyed to the ping: true/false bool isConnected_; // State of this particular connection: FEC/PLC/Class - // Silecs initialization has to be invoked only once per process - static bool isInit_; - // not copyable object Connection(const Connection&); Connection& operator=(const Connection&); diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp index d4773eb52c16d1fb381327cbb42aca53b7d035f3..e94d7956bd781dbe3c1d8e3b57d5c46811b31db8 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp +++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/PLCRegister.cpp @@ -294,7 +294,9 @@ namespace Silecs break; case Schneider: case Digi: +#ifdef MODBUS_SUPPORT_ENABLED ((double *)pRecvValue_)[i*dimension2_+j] = IeMdbGetTime(pData); +#endif //MODBUS_SUPPORT_ENABLED break; case Ni: //TODO @@ -372,7 +374,9 @@ namespace Silecs break; case Schneider: case Digi: +#ifdef MODBUS_SUPPORT_ENABLED IeMdbSetTime(pData, epoch); +#endif //MODBUS_SUPPORT_ENABLED break; case Ni: //TODO diff --git a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp index d7808c453ae07b04a76ad7d10b3a09cf086b77b0..acd4e217c7650ddad5de5edbf6348b0392486be6 100644 --- a/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp +++ b/silecs-communication-cpp/src/silecs-communication/interface/equipment/SilecsPLC.cpp @@ -450,7 +450,9 @@ namespace Silecs case S7Protocol: theConn = new SNAP7Connection(this); break; case MBProtocol: +#ifdef MODBUS_SUPPORT_ENABLED theConn = new MBConnection(this); break; +#endif case CNVProtocol: #ifdef NI_SUPPORT_ENABLED theConn = new CNVConnection(this);break; diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.cpp b/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.cpp index def46b4105ec9ae7d223b18e473354dbc523544d..bf98923c37f4825e417fb27987c2911226cb3ebe 100644 --- a/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.cpp +++ b/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.cpp @@ -39,10 +39,6 @@ #include <silecs-communication/protocol/core/silecs.h> -#ifdef __cplusplus -extern "C" { -#endif - /* ---------------------------------------------------------*/ /* MACRO DEFINITION */ /* ---------------------------------------------------------*/ @@ -321,40 +317,3 @@ char *IeGetErrorMessage(int err) return ("Unknown IE error"); } - -/*----------------------------------------------------------*/ -/* This function initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at first. - */ -int IeInit (void) -{ - int err=0; - - err |= IeMdbInit(); - - return (err); -} - -/*----------------------------------------------------------*/ -/* This function de-initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at end. - */ -int IeExit (void) -{ - int err=0; - - err |= IeMdbExit(); - - return (err); -} - -#ifdef __cplusplus -} -#endif - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.h b/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.h index 782495b2baea04d37276163140d6428c6daad085..2edf5b1665b94f81189b7c03384814c6d9fdd14c 100644 --- a/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.h +++ b/silecs-communication-cpp/src/silecs-communication/protocol/core/silecs.h @@ -33,10 +33,6 @@ * */ -#ifdef __cplusplus -extern "C" { -#endif - /* ---------------------------------------------------------*/ /* INCLUDE FILES */ /* ---------------------------------------------------------*/ @@ -45,7 +41,7 @@ extern "C" { /* ---------------------------------------------------------*/ /* PROTOTYPE DEFINITIONS */ /* ---------------------------------------------------------*/ -#include <silecs-communication/protocol/modbus/iemdb.h> /* modbus interface */ +#include <silecs-communication/protocol/modbus/iemdb.h> /*----------------------------------------------------------*/ /* Time funtion @@ -89,26 +85,4 @@ int IeRfcPing (char *hostName, char *plcIP); */ char *IeGetErrorMessage(int err); -/*----------------------------------------------------------*/ -/* This function initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at first. - */ -int IeInit (void); - -/*----------------------------------------------------------*/ -/* This function initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at end. - */ -int IeExit (void); - -#ifdef __cplusplus -} -#endif - #endif /* _SILECS_H_ */ diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.cpp b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.cpp index 94a55b1c559895c45034b8bbaec2001c330a616d..0ae7c925704e420522bcefe7366986a4355b2ea8 100644 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.cpp +++ b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.cpp @@ -1,128 +1,12 @@ -// Copyright 2016 CERN and GSI -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -/* - * Revision : v1.0 - * Date : 08/2003 - F.Locci - * Objet : original version for PS7-SCHNEIDER PLC - * Action : - * - * Revision : v1.1 - * Date : 05/2004 - F.Locci - * Objet : .remove when data size = 0 - * .set err on read/write error - * Look at v1.1 comments - * - * Revision : v1.2 - * Date : 11/2004 - F.Locci - * Objet : .move mb_master_init() call to IeMdbInit() function - * to make modbus code reentrant. - * Look at v1.2 comments - * - * Revision : v1.3 - * Date : 10/2004 - F.Locci - * Objet : .Allow absolute offset for read/write action - * .Allow variable size for read/write action - * => use offset sign convention for absolute/relative - * => use application size instead of config.table one - * Look at v1.3 comments - * - * Revision : v1.4 - * Date : 03/2006 - F.Locci - * Objet : .Extend the number of possible connection (7 --> 32) - * in order to allow more than 7 connections with - * DIFFERENT PLC (SPS/EA project in particular). - * We keep 7 connection for SIEMENS (F/W) protocole - * to respect material constraint (14 ISO-ON-TCP max.) - * - * Revision : v1.5 - * Date : 01/2007 - F.Locci - * Objet : implement BLOCK mode - * Action : .Add IeMdbGetBlock() and IeMdbSetBlock() function - * .Upgrade mdbFindEqp() function - * Look at v1.5 comments - * - * Revision : v1.6 - * Date : 03/2007 - F.Locci - * Objet : Protect Get data buffer overflow - * Look at v1.5 comments - * - * Revision : v1.7 - * Date : 04/2007 - F.Locci - * Objet : Direct byte swapping into MdbSetTime function - * Look at v1.5 comments - */ - -/* - * This library was developped to provide communication between - * Linux/LynxOS active host (client) and a PLC passive server. - * Based on MODBUS std. protocole, it defines generic - * set and get functions to read and write data into/from PLC - * memory. - */ - -/* ---------------------------------------------------------*/ -/* INCLUDE FILES */ -/* ---------------------------------------------------------*/ -#include <silecs-communication/protocol/core/ietype.h> -#include "mb_addr.h" -#include "mb_master.h" +#ifdef MODBUS_SUPPORT_ENABLED +#include "iemdb.h" -#ifdef __cplusplus -extern "C" { -#endif +//Modbus max data size definition (byte counting) +#define MAX_WRITE_DATA_SIZE MODBUS_MAX_WRITE_REGISTERS*2 +#define MAX_READ_DATA_SIZE MODBUS_MAX_READ_REGISTERS*2 /* ---------------------------------------------------------*/ -/* LOCAL CONSTANT DEFINITIONS */ -/* ---------------------------------------------------------*/ - -/* For Modbus protocol */ -#define MODBUS_MAX_CONNECTION 64 /*v1.4*/ -#define MODBUS_SENDRETRIES_DEF 1 -#define MODBUS_TIMEOUT_DEF 2 /* in seconds */ - -/*Config.Table entries offset - SCHNEIDER memory implementation - word alignment*/ -#define FEC_CMD_OFS 0 /* Command from FEC: first entry of cfg.table*/ -#define PARAMS_CMD_OFS 2/2 /* Params of FEC command */ -#define CURRENT_DATE_OFS 10/2 /* PLC current dat&time */ -#define SYS_INFO_OFS 18/2 /* HW/SW info variables */ -#define DIAG_INFO_OFS 78/2 /* PLC SW/HW diagnostic */ -#define NB_EQP_OFS 132/2 /*nb table row */ -#define ADDR_TBL_OFS 134/2 /*table data (addr/size)*/ - -#define swap16(w) ((((unsigned short)w & 0x00ff)<<8)+(((unsigned short)w & 0xff00)>>8)) - -/* ---------------------------------------------------------*/ -/* TYPE DEFINITIONS */ -/* ---------------------------------------------------------*/ -/* an entry in the network array */ -typedef struct -{ - const char *name; - node_addr_t addr; - int nd; /* the node descriptor... */ - struct timespec timeout; - u32 send_retries; -} network_t; - -/* ---------------------------------------------------------*/ -/* GLOBAL DECLARATIONS */ -/* ---------------------------------------------------------*/ - -/* ---------------------------------------------------------*/ -/* FUNCTIONS CODE */ +/* FUNCTIONS CODE */ /* ---------------------------------------------------------*/ /*----------------------------------------------------------*/ @@ -225,41 +109,6 @@ double IeMdbGetTime(unsigned char *dt) // from PLC return((double)plctm + ms); } -/*----------------------------------------------------------*/ -/* This function initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at first. - */ -int IeMdbInit (void) -{ - /*v1.2*/ - if (mb_master_init(MODBUS_MAX_CONNECTION) < 0) - { - #ifdef DEBUG - /* could not init network! */ - printf("\n#Error: mb_master_init() failed"); - #endif - return (IE_SYS_ERROR); - } - - return 0; -} - -/*----------------------------------------------------------*/ -/* This function de-initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at end. - */ -int IeMdbExit (void) -{ - mb_master_done(); - return 0; -} - /*----------------------------------------------------------*/ /* This function creates a synchronous OSI-ON-TCP socket * channel used for sending/receiving communication with PLC. @@ -272,150 +121,20 @@ int IeMdbExit (void) * Only one synchronous channel per PLC at the same time * supported in that version. */ -int IeMdbOpen (char *hostName, char *plcIP, int baseAddress) +int IeMdbOpen (modbus_t** ctx, char *hostName, char *plcIP, int baseAddress) { - network_t net; - char *ipstr = plcIP; - _WORD dummy; - - // use hostName reference in priority else plcIP - if (hostName) ipstr = hostName; + *ctx = modbus_new_tcp(plcIP, MODBUS_TCP_DEFAULT_PORT /*=502*/); + //modbus_set_debug(*ctx, TRUE); - if (ipstr == NULL) + if (modbus_connect(*ctx) == -1) { - printf("#Error: ipstr NULL\n"); - return(IE_PARAM_ERROR); + modbus_free(*ctx); + return IE_CONNECT_ERROR; } - /* init network .........................................*/ - /*net.name = not used here*/ - net.addr.naf = naf_tcp; - net.addr.addr.tcp.host = ipstr; - net.addr.addr.tcp.service = "502"; //specific modbus - net.addr.addr.tcp.close_on_silence = 1; // TRUE - net.timeout = d_to_timespec(MODBUS_TIMEOUT_DEF); - net.send_retries = MODBUS_SENDRETRIES_DEF; - - /* connection ......................................*/ - if ( (net.nd = mb_master_connect(net.addr)) >= 0) - { - /*Try to access PLC memory, just to confirm modbus connection (for example, - read config. table address. - */ - if (read_bdata( - 1, /*int slave,*/ - baseAddress/2, /*int start_addr (/2 is used to convert the byte alignment to 16bit alignment) */ - sizeof(dummy), /*int byte count,*/ - (_BYTE *)&dummy, /*uchar *dest,*/ - net.nd, /*int ttyfd,*/ - net.send_retries, /*int send_retries,*/ - &net.timeout /*const struct timespec *response_timeout*/ - ) >= 0) - { - /* node opened successfully! */ - #ifdef DEBUG - printf("Connecting to network %s: OK\n", net.addr.addr.tcp.host); - #endif - - return (net.nd); - } - #ifdef DEBUG - else - { - /* could not read registers from slave! */ - printf("#Error: could not establish connection to %s\n", net.addr.addr.tcp.host); - } - #endif - - mb_master_close(net.nd); - } - #ifdef DEBUG - else - { - /* could not connect to slave! */ - printf("#Error: could not establish connection to %s\n", net.addr.addr.tcp.host); - } - #endif - - return(IE_CONNECT_ERROR); + return 0; } -/*----------------------------------------------------------*/ -int IeMBwriteData(int cid, _WORD dataOfs, _WORD dataSize, _BYTE* dataBuffer) -{ - int err; - network_t net; - - /* init network .........................................*/ - net.timeout = d_to_timespec(MODBUS_TIMEOUT_DEF); - net.send_retries = MODBUS_SENDRETRIES_DEF; - - /*then SET&DATE cmd into command word*/ - if (write_bdata(1, /*int slave,*/ - dataOfs, /*int start_addr (!16 bits alignment)*/ - dataSize, /*int byte count */ - dataBuffer, /*uchar *src */ - cid, /*int connection id */ - net.send_retries, /*int send_retries,*/ - &net.timeout /*const struct timespec *response_timeout*/ - ) >= 0) - { - #ifdef DEBUG - printf("IeMBwriteData has been done successfully\n"); - #endif - err = 0; /*no problem*/ - } - else - { - #ifdef DEBUG - printf("#Error on IeMBwriteData\n"); - #endif - err = IE_EPIPE_ERROR; - } - - return err; -} - -/*----------------------------------------------------------*/ -int IeMBreadData(int cid, _WORD dataOfs, _WORD dataSize, _BYTE* dataBuffer) -{ - int err; - network_t net; - - /* init network .........................................*/ - net.timeout = d_to_timespec(MODBUS_TIMEOUT_DEF); - net.send_retries = MODBUS_SENDRETRIES_DEF; - - /*unusefull to read empty data buffer*/ - if (dataSize > 0) - { - err = read_bdata(1, /*int slave,*/ - dataOfs, /*int start_addr (!16 bits alignment)*/ - dataSize, /*int byte count */ - dataBuffer, /*uchar *src */ - cid, /*int connection id */ - net.send_retries, /*int send_retries,*/ - &net.timeout /*const struct timespec *response_timeout*/ - ); - } - - if ((dataSize == 0) || (err >= 0)) - { - #ifdef DEBUG - printf("IeMBreadData has been done successfully\n"); - #endif - err = 0; /*no problem*/ - } - else - { - #ifdef DEBUG - printf("#Error on IeMBreadData\n"); - #endif - err = IE_EPIPE_ERROR; - } - - return err; -} /*----------------------------------------------------------*/ /* This function closes the synchronous OSI-ON-TCP socket @@ -425,17 +144,72 @@ int IeMBreadData(int cid, _WORD dataOfs, _WORD dataSize, _BYTE* dataBuffer) * * Details: */ -int IeMdbClose(int cid) +int IeMdbClose(modbus_t* ctx) { - int err = 0; + /* Close the connection */ + modbus_close(ctx); + modbus_free(ctx); - err |= mb_master_close(cid); - - if (err != 0) err = IE_DISCONNECT_ERROR; - return(err); + return 0; } -#ifdef __cplusplus +/*----------------------------------------------------------*/ +int IeMBwriteData(modbus_t* ctx, uint16_t start_addr, uint16_t count, uint8_t* data) +{ + uint16_t *srcp = (uint16_t*)data; + int word_count, byte_count; + + while(count) + { + word_count = (count > MAX_WRITE_DATA_SIZE) ? MAX_WRITE_DATA_SIZE/2 : (count/2)+(count%2); + byte_count = (word_count * 2); + + if (modbus_write_registers(ctx, start_addr, word_count, srcp) != word_count) + { + return IE_EPIPE_ERROR; + } + + //srcp += byte_count-(count%2); + srcp += word_count; + start_addr += word_count; + count -= (byte_count - (count%2)); + } + + return 0; } -#endif +/*----------------------------------------------------------*/ +int IeMBreadData(modbus_t* ctx, uint16_t start_addr, uint16_t count, uint8_t* data) +{ + uint16_t *destp = (uint16_t*)data; + int word_count, byte_count; + + while(count) { + + if (count > MAX_READ_DATA_SIZE) + { + /*'word_count' is a word counter*/ + word_count = MAX_READ_DATA_SIZE/2; + } + else + { + /*'word_count' is a word counter*/ + word_count = (count/2) + (count%2); + } + + byte_count = (word_count * 2); + + if (modbus_read_registers(ctx, start_addr, word_count, destp) != word_count) + { + return IE_EPIPE_ERROR; + } + + //destp += (byte_count-(count%2)); + destp += word_count; + start_addr += word_count; + count -= (byte_count - (count%2)); + } + + return 0; +} +#endif //MODBUS_SUPPORT_ENABLED diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.h index 8a055c727d0b3eb87ff1f8bd142658c962c8f2bc..6fcc3cea5e933a835e83bb2b1e47dbb57807bb47 100644 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.h +++ b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/iemdb.h @@ -1,58 +1,9 @@ -// Copyright 2016 CERN and GSI -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -/* - * Revision : v1.0 - * Date : 08/2003 - F.Locci - * Objet : original version for PS7-SCHNEIDER PLC - * Action : - * - * Revision : v1.5 - * Date : 01/2007 - F.Locci - * Objet : implement BLOCK mode - * Action : Add IeMdbGetBlock() and IeMdbSetBlock() function - * Look at v1.5 comments - * - */ - +#ifdef MODBUS_SUPPORT_ENABLED #ifndef _IEMDB_H_ #define _IEMDB_H_ -/* - * This library was developped to provide communication between - * Linux/LynxOS active host (client) and a PLC passive server. - * Based on MODBUS std. protocole, it defines generic - * set and get functions to read and write data into/from PLC - * memory. - */ - -/* ---------------------------------------------------------*/ -/* LOCAL CONSTANT DEFINITIONS */ -/* ---------------------------------------------------------*/ - -/* ---------------------------------------------------------*/ -/* TYPE DEFINITIONS */ -/* ---------------------------------------------------------*/ - -/* ---------------------------------------------------------*/ -/* PROTOTYPE DEFINITIONS */ -/* ---------------------------------------------------------*/ - -#ifdef __cplusplus -extern "C" { -#endif +#include <silecs-communication/protocol/core/ietype.h> +#include <modbus.h> /*----------------------------------------------------------*/ /* Time funtion @@ -64,7 +15,7 @@ extern "C" { * format: * * 0 1 2 3 4 5 6 7 - * SC -- HH MN MM DD YY YY + * SC -- HH MN MM DD YY YY * * byte 1 is used by gateway to synchronize time setting. */ @@ -80,29 +31,11 @@ void IeMdbSetTime(unsigned char *dt, time_t epoch); // to PLC * format: * * 0 1 2 3 4 5 6 7 - * SC SC/100 HH MN MM DD YY YY + * SC SC/100 HH MN MM DD YY YY * */ double IeMdbGetTime(unsigned char *dt); // from PLC -/*----------------------------------------------------------*/ -/* This function initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at first. - */ -int IeMdbInit (void); - -/*----------------------------------------------------------*/ -/* This function initializes the DSC-PLC communication. - * return 0 or <0 on error (see constant error) - * - * Details: - * Must be executed at end. - */ -int IeMdbExit (void); - /*----------------------------------------------------------*/ /* This function creates a synchronous OSI-ON-TCP socket * channel used for sending/receiving communication with PLC. @@ -115,29 +48,29 @@ int IeMdbExit (void); * Only one synchronous channel per PLC at the same time * supported in that version. */ -int IeMdbOpen (char *hostName, char *plcIP, int baseAddress); +int IeMdbOpen (modbus_t** ctx, char *hostName, char *plcIP, int baseAddress); /*----------------------------------------------------------*/ /* This function send a data segment to the PLC using the * MODBUS protocole. - * 'cid': write channel id of the MB connection - * 'dataOfs': target adress of the data segment within the PLC memory + * 'ctx': write channel id of the MB connection + * 'dataAddr': target address of the data segment within the PLC memory * 'dataSize': byte size of the data segment to be sent * 'dataBuffer': buffer of the data to be sent * return 0 or <0 on error (see constant error) */ -int IeMBwriteData(int cid, _WORD dataOfs, _WORD dataSize, _BYTE* dataBuffer); +int IeMBwriteData(modbus_t* ctx, uint16_t dataAddr, uint16_t dataSize, uint8_t* dataBuffer); /*----------------------------------------------------------*/ /* This function read a data segment from the PLC using the * MODBUS protocole. - * 'cid': read channel id of the MB connection - * 'dataOfs': adress of the data segment within the PLC memory + * 'ctx': read channel id of the MB connection + * 'dataAddr': address of the data segment within the PLC memory * 'dataSize': byte size of the data segment to be read * 'dataBuffer': buffer to store the data * return 0 or <0 on error (see constant error) */ -int IeMBreadData(int cid, _WORD dataOfs, _WORD dataSize, _BYTE* dataBuffer); +int IeMBreadData(modbus_t* ctx, uint16_t dataAddr, uint16_t dataSize, uint8_t* dataBuffer); /*----------------------------------------------------------*/ /* This function closes the synchronous OSI-ON-TCP socket @@ -147,10 +80,7 @@ int IeMBreadData(int cid, _WORD dataOfs, _WORD dataSize, _BYTE* dataBuffer); * * Details: */ -int IeMdbClose(int cid); - -#ifdef __cplusplus -} -#endif +int IeMdbClose(modbus_t* ctx); #endif /* _IEMDB_H_ */ +#endif //MODBUS_SUPPORT_ENABLED diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_addr.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_addr.h deleted file mode 100644 index 353639638d859a159680a1a6dcdc310c448b9dec..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_addr.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * (c) 2002 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -#ifndef MODBUS_LAYER2_H -#define MODBUS_LAYER2_H - -/*F.Locci #include <plc.h>*/ /* get the plc data types */ -#include <time.h> /* struct timespec data type */ - -/*F.Locci - 28/10/2003*/ -/*types only defined with linux version: <asm/types.h>*/ -#ifndef __Lynx__ -#include <asm/types.h> - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#else - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#ifndef __ppc4__ /*new <socket.h> with ppc4 environment*/ -/*F.Locci - 03/11/2004, add x86 platform support*/ -#ifndef __x86__ -typedef unsigned short int sa_family_t; - -/* The following constants should be used for the second parameter of - `shutdown'. */ -enum -{ - SHUT_RD = 0, /* No more receptions. */ -#define SHUT_RD SHUT_RD - SHUT_WR, /* No more transmissions. */ -#define SHUT_WR SHUT_WR - SHUT_RDWR /* No more receptions or transmissions. */ -#define SHUT_RDWR SHUT_RDWR -}; -#endif -#endif - -/*constant conversion for LynxOS*/ -#define AF_ROSE 11 -#define AF_X25 9 -#define AF_AX25 3 -#define SOL_TCP 6 -#define SOL_IP 0 -#endif - -typedef enum {optimize_speed, optimize_size} optimization_t; - - -typedef enum { - naf_ascii, - naf_rtu, - naf_tcp, - } node_addr_family_t; - -typedef struct { - const char *host; - const char *service; - int close_on_silence; - } node_addr_tcp_t; - -typedef struct { - const char *device; - int baud; /* plain baud rate, eg 2400; zero for the default 9600 */ - int parity; /* 0 for none, 1 for odd, 2 for even */ - int data_bits; - int stop_bits; - int ignore_echo; /* 1 => ignore echo; 0 => do not ignore echo */ - } node_addr_rtu_t; - -typedef node_addr_rtu_t node_addr_ascii_t; - -typedef union { - node_addr_ascii_t ascii; - node_addr_rtu_t rtu; - node_addr_tcp_t tcp; - } node_addr_common_t; - -typedef struct { - node_addr_family_t naf; - node_addr_common_t addr; - } node_addr_t; - -#endif /* MODBUS_LAYER2_H */ - - - - - - - - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_layer1.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_layer1.h deleted file mode 100644 index 9a335086ee36b44c6dc794a06c0608d095d66722..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_layer1.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * (c) 2001 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - * - * Revision : v1.0 - * Date : 11/2004 - F.Locci - * Objet : move global recv_buf_ to local one into read/write_bdata() - * to make module reentrant. - * Look at v1.0 comment - */ - - -#ifndef MODBUS_LAYER1_H -#define MODBUS_LAYER1_H - -#ifdef __cplusplus -extern "C" { -#endif - -/*F.Locci #include <plc.h>*/ /* get the plc data types */ -#include <time.h> /* struct timespec data type */ - -#include "mb_addr.h" /* get definitions of common variable types */ - - - /* write a modbus frame */ - /* WARNING: when calling this functio, the *frame_data buffer - * must be allocated with an extra *extra_bytes - * beyond those required for the frame_length. - * This is because the extra bytes will be used - * to store the crc before sending the frame. - * - * The *extra_bytes value will be returned by the - * modbus_init() function call. - */ - /* NOTE: calling this function will flush the input stream, - * which means any frames that may have arrived - * but have not yet been read using modbus_read() - * will be permanently lost... - */ -int modbus_write(int nd, - u8 *frame_data, - size_t frame_length, - u16 transaction_id); - - /* read a modbus frame */ - /* NOTE: calling modbus_write() will flush the input stream, - * which means any frames that may have arrived - * but have not yet been read using modbus_read() - * will be permanently lost... - * - * NOTE: Ususal select semantics for (a: recv_timeout == NULL) and - * (b: *recv_timeout == 0) also apply. - * - * (a) Indefinite timeout - * (b) Try once, and and quit if no data available. - */ -int modbus_read(int *nd, /* node descriptor */ - u8 *recv_data_ptr/*v1.0*/, - u16 *transaction_id, - u8 *send_data, /* ignored ! */ - int send_length, /* ignored ! */ - const struct timespec *recv_timeout); - - - /* init the library */ -int modbus_init(int nd_count, /* maximum number of nodes... */ - optimization_t opt, - int *extra_bytes); - - /* shutdown the library...*/ -int modbus_done(void); - - -/* Open a node for master / slave operation. - * Returns the node descriptor, or -1 on error. - */ -int modbus_connect(node_addr_t node_addr); -int modbus_listen(node_addr_t node_addr); - -/* Close a node, needs a node descriptor as argument... */ -int modbus_close(int nd); - -/* Tell the library that the user will probably not be communicating - * for some time... - * This will allow the library to release any resources it will not - * be needing during the silence. - * NOTE: This is onlyused by the TCP version to close down tcp connections - * when the silence will going to be longer than second. - */ -int modbus_silence_init(void); - - /* determine the minmum acceptable timeout... */ - /* NOTE: timeout values passed to modbus_read() lower than the value returned - * by this function may result in frames being aborted midway, since they - * take at least modbus_get_min_timeout() seconds to transmit. - */ -double modbus_get_min_timeout(int baud, - int parity, - int data_bits, - int stop_bits); - - -#ifdef __cplusplus -} -#endif - -#endif /* MODBUS_LAYER1_H */ - - - - - - - - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master.cpp b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master.cpp deleted file mode 100644 index e4436018cebf12f1ccd478f9aef63cfc5a3e501b..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* mb_master.c - - By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au - - These library of functions are designed to enable a program send and - receive data from a device that communicates using the Modbus protocol. - - Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - - The functions included here have been derived from the - Modicon Modbus Protocol Reference Guide - which can be obtained from Schneider at www.schneiderautomation.com. - - This code has its origins with - paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) - who wrote a small program to read 100 registers from a modbus slave. - - I have used his code as a catalist to produce this more functional set - of functions. Thanks paul. - - - - 10/2001 - Mario de Sousa - Slightly re-organized the code. - Miscelaneous cleanups - Removed layer1 functions (i.e. write and read to /dev/ttySx) - Added support for transaction id - - TODO - add retries at layer2 (i.e. in this file) - check response frame for correctness -* -* -* Revision : v1.0 -* Date : 11/2004 - F.Locci -* Objet : .remove static prefix into modbus_tcp_write() declaration -* to make code reentrant. -* .move global recv_buf_ to local one into read/write_bdata() -* Look at v1.0 comments -* -* Revision : v2.0 -* Date : 11/2006 - F.Locci -* Objet : swap16 bits from SILECS client instead of application level -* Look at v2.0 comments -*/ - -#include <fcntl.h> /* File control definitions */ -#include <stdio.h> /* Standard input/output */ -#include <string.h> -#include <stdlib.h> -#include <termio.h> /* POSIX terminal control definitions */ -#include <time.h> /* Time structures for select() */ -#include <unistd.h> /* POSIX Symbolic Constants */ -#include <errno.h> /* Error definitions */ - -#include <netinet/in.h> /* required for htons() and ntohs() */ -#include "mb_layer1.h" -#include "mb_master.h" -#include "mb_master_private.h" -/*v1.0*/ -#include "mb_tcp_private.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* #define DEBUG */ /* uncomment to see the data sent and received */ - - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - - -/******************************************/ -/******************************************/ -/** **/ -/** Global Variables... **/ -/** **/ -/******************************************/ -/******************************************/ - -/*v1.0 -static u8 *query_buffer_ = NULL; -*/ -#ifndef __Lynx__ -#if RECV_BUFFER_SIZE < 5 //TCP_HEADER_LENGTH -#error The receive buffer is smaller than the frame header length. -#endif -#endif -/**/ - -/******************************************/ -/******************************************/ -/** **/ -/** Local Utility functions... **/ -/** **/ -/******************************************/ -/******************************************/ - - -/* - * Function to determine next transaction id. - * - * We use a library wide transaction id, which means that we - * use a new transaction id no matter what slave to which we will - * be sending the request... - */ -static inline u16 next_transaction_id(void) { - - static u16 next_id = 0; - - return next_id++; -} - - -/* - * Functions to convert u16 variables - * between network and host byte order - * - * NOTE: Modbus uses MSByte first, just like - * tcp/ip, so we use the htons() and - * ntoh() functions to guarantee - * code portability. - */ - -static inline u16 mb_hton(u16 h_value) { -/* return h_value; */ - return htons(h_value); -} - -static inline u16 mb_ntoh(u16 m_value) { -/* return m_value; */ - return ntohs(m_value); -} - - - -static inline u8 msb(u16 value) { -/* return Most Significant Byte of value; */ - return (value >> 8) & 0xFF; -} - -static inline u8 lsb(u16 value) { -/* return Least Significant Byte of value; */ - return value & 0xFF; -} - -#define u16_v(char_ptr) (*((u16 *)(&(char_ptr)))) - - -/*********************************************************************** - - The following functions construct the required query into - a modbus query packet. - -***********************************************************************/ - -static inline int build_query_packet(u8 slave, - u8 function, - u16 start_addr, - u16 count, - u8 *packet) -{ - packet[ 0 ] = slave, - packet[ 1 ] = function; - /* NOTE: - * Modbus uses high level addressing starting off from 1, but - * this is sent as 0 on the wire! - */ - u16_v(packet[2]) = mb_hton(start_addr - 1); - u16_v(packet[4]) = mb_hton(count); - - return 6; -} - - -/************************************************************************ - - read_bdata - - F.Locci: just read raw byte array. Take care of frame which exceeds - modbus frame std. length. - -*************************************************************************/ -/*v2.0*/ -#define swap16(w) ((((unsigned short)w & 0x00ff)<<8)+(((unsigned short)w & 0xff00)>>8)) - -int read_bdata(int slave, /* slave id */ - int start_addr, /* word pointer addr. */ - int count, /* byte count to read */ - u8 *dest, /* byte array */ - int ttyfd, /* connection id */ - int send_retries, /* number of request retries */ - const struct timespec *response_timeout) /* timeout value */ -{ - /*v1.0*/ - u8 data[RECV_BUFFER_SIZE]; - u8 packet[QUERY_BUFFER_SIZE]; - /**/ - int rl, response_length=0; - int query_length; - u16 send_transaction_id, recv_transaction_id; - u8 *destp = dest; - int global_length, length; /* word count for read */ - - /*Attention! ModBus travaille sur une zone memoire [1..n] tandis que l'espace memoire - du PLC est definie [0..n-1] => start_addr = start_addr +1 - start_addr est exprime en word pour le PLC (alignement 16bits). - */ - start_addr+=1; - - - /*'global_length' is a word counter*/ - global_length = (count/2) + (count%2); - - while(count) { - - if (count > MAX_READ_BDATA) - /*'length' is a word counter*/ - length = MAX_READ_BDATA/2; - else - /*'length' is a word counter*/ - length = (count/2) + (count%2); - - /* We must also initialize the recv_transaction_id with the same value, - * since some layer 1 protocols do not support transaction id's, so - * simply return the recv_transaction_id variable without any changes... - */ - send_transaction_id = recv_transaction_id = next_transaction_id(); - - query_length = build_query_packet(slave, 0x03, /*function*/ - start_addr, length, - packet); - if (query_length < 0) - return -1; - - if (modbus_write(ttyfd, packet, query_length, send_transaction_id) < 0 ) - return PORT_FAILURE; - - rl = modbus_read(&ttyfd, data/*v1.0*/, &recv_transaction_id, - packet, query_length, - response_timeout); - - if( rl <= 0 ) return PORT_FAILURE; - - /* first check whther we have correct transaction id */ - if (send_transaction_id != recv_transaction_id) - return PORT_FAILURE; - - if( rl > 0 ) - { rl -= 3; /*F.Locci, bug: 2->3*/ - if (count<MAX_READ_BDATA) - rl -= (count%2); - } - - if( rl > 0 ) { - memcpy(destp, (u8 *)&(data[3]), rl); - destp += rl; - start_addr += length; /*F.Locci, bug if rl = 0 */ - } - - /* start_addr += length; F.Locci, bug if rl = 0 */ - response_length += rl; - count -= rl; - } - - /*v2.0: - Independamment de son mapping memoire (Little-endian), le PLC SCHNEIDER - swap chaque mot de 16bits qu'il transmet. Cette ligne permet de redresser - les donnees pour transmettre l'image exacte du mapping memoire. Charge - a l'application ensuite d'appliquer le swapping necessaire relatif a son - "Endianness". - */ - { - int j; - u16 *wdestp = (u16 *)dest; /* for swapping 16bit */ - for(j=0; j<global_length; j++){ wdestp[j] = swap16(wdestp[j]); } - } - /**/ - - #ifdef DEBUG - { int i; - printf("read_bdata: "); - for(i=0; i<response_length; printf("%02x ", dest[i++])); - printf("\n"); - } - #endif - - return response_length; -} - -/************************************************************************* - - write_bdata - - F.Locci: just write raw byte array. Take care of frame which exceeds - modbus frame std. length. - !!byte count MUST be even value - 16 bits alignment in that PLC - -***************************************************************************/ - -int write_bdata(int slave, /* slave id */ - int start_addr, /* word pointer addr. */ - int count, /* byte count to write - MUST be even value */ - u8 *data, /* byte array */ - int ttyfd, /* connection id */ - int send_retries, /* number of request retries */ - const struct timespec *response_timeout) /* timeout value */ -{ - int byte_count; - int i, rl, response_length=0; - int query_length; - /*v1.0*/ - u8 rdata[RECV_BUFFER_SIZE]; - u8 packet[QUERY_BUFFER_SIZE]; - u16 send_transaction_id, recv_transaction_id; - int length; /* word count */ - u8 *destp, *srcp = data; - - /*Attention! ModBus travaille sur une zone memoire [1..n] tandis que l'espace memoire - du PLC est definie [0..n-1] => start_addr = start_addr +1 - start_addr est exprime en word pour le PLC (alignement 16bits). - */ - start_addr+=1; - - while(count) { - - if (count > MAX_WRITE_BDATA) - { - /*'length' is a word counter*/ - length = MAX_WRITE_BDATA/2; - } - else - { - /*'length' is a word counter*/ - length = (count/2) + (count%2); - } - - /* We must also initialize the recv_transaction_id with the same value, - * since some layer 1 protocols do not support transaction id's, so - * simply return the recv_transaction_id variable without any changes... - */ - send_transaction_id = recv_transaction_id = next_transaction_id(); - - query_length = build_query_packet(slave, 0x10 /* function */, - start_addr, length, - packet); - if (query_length < 0) - return -1; - - byte_count = (length * 2); - packet[ query_length ] = byte_count; - - destp = &(packet[ ++query_length ]); - memcpy(destp, srcp, byte_count-(count%2)); - - /*v2.0: - Independamment de son mapping memoire (Little-endian), le PLC SCHNEIDER - swap chaque mot de 16bits qu'il transmet. Cette ligne permet de redresser - les donnees pour transmettre l'image exacte du mapping memoire. Charge - a l'application ensuite d'appliquer le swapping necessaire relatif a son - "Endianness". - */ - { - u16 *wdestp = (u16 *)destp; - for(i=0; i<length; i++){ wdestp[i] = swap16(wdestp[i]); } - } - /**/ - - srcp += byte_count-(count%2); - query_length += byte_count; - - if( modbus_write( ttyfd, packet, query_length, send_transaction_id) < 0 ) - return PORT_FAILURE; - - rl = modbus_read(&ttyfd, rdata/*v1.0*/, &recv_transaction_id, - packet, query_length, response_timeout); - - if( rl <= 0 ) return PORT_FAILURE; - - /* first check whther we have correct transaction id */ - if (send_transaction_id != recv_transaction_id) - return PORT_FAILURE; - - start_addr += length; - response_length += byte_count; - count -= (byte_count - (count%2)); - } - - return response_length; -} - -/************************************************************************ - - initialise / shutdown the library - - These functions sets up/shut down the library state - (allocate memory for buffers, initialise data strcutures, etc) - -**************************************************************************/ - -int mb_master_init(int nd_count) { - int extra_bytes; - -#ifdef DEBUG - fprintf( stderr, "mb_master_init()\n"); - fprintf( stderr, "creating %d nodes\n", nd_count); -#endif - - /* initialise layer 1 library */ - if (modbus_init(nd_count, DEF_OPTIMIZATION, &extra_bytes) - < 0) - goto error_exit_0; - - /*v1.0 initialise send buffer - query_buffer_ = (u8 *)malloc(QUERY_BUFFER_SIZE + extra_bytes); - if (query_buffer_ == NULL) - goto error_exit_1; - */ - - return 0; - -error_exit_0: - return -1; -} - - -int mb_master_done(void) { -/*v1.0 - free(query_buffer_); - query_buffer_ = NULL; -*/ - return modbus_done(); -} - -/************************************************************************ - - open/close master connection - - This function opens/closes a connection to the remote slave. - -**************************************************************************/ - -int mb_master_connect(node_addr_t node_addr) { -#ifdef DEBUG - fprintf( stderr, "mb_master_connect()\n"); -#endif - /* call layer 1 library */ - return modbus_connect(node_addr); -} - - -int mb_master_close(int nd) { -#ifdef DEBUG - fprintf( stderr, "mb_master_close(): nd = %d\n", nd); -#endif - /* call layer 1 library */ - return modbus_close(nd); -} - -#ifdef __cplusplus -} -#endif diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master.h deleted file mode 100644 index 774598539ed57421c7a5f035cd0ac7350cd8eee1..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master.h +++ /dev/null @@ -1,157 +0,0 @@ -/* modbus_rtu.h - - By P.Costigan email: phil@pcscada.com.au http://pcscada.com.au - - These library of functions are designed to enable a program send and - receive data from a device that communicates using the Modbus protocol. - - Copyright (C) 2000 Philip Costigan P.C. SCADA LINK PTY. LTD. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - - - - The functions included here have been derived from the - Modicon Modbus Protocol Reference Guide - which can be obtained from Schneider at www.schneiderautomation.com. - - This code has its origins with - paul@pmcrae.freeserve.co.uk (http://www.pmcrae.freeserve.co.uk) - who wrote a small program to read 100 registers from a modbus slave. - - I have used his code as a catalist to produce this more functional set - of functions. Thanks paul. - - - */ - - -#ifndef MODBUS_MASTER_H -#define MODBUS_MASTER_H - -#ifdef __cplusplus -extern "C" { -#endif - -/*F.Locci #include <plc.h>*/ /* get the plc data types */ -#include <time.h> /* struct timespec data structure */ - -#include "mb_addr.h" /* get definition of common variable types */ - - - -/*********************************************************************** - - Note: All functions used for sending or receiving data via - modbus return these return values. - - - Returns: string_length if OK - 0 if failed - Less than 0 for exception errors - -***********************************************************************/ - - -/*F.Locci: error constants adjustement to be SILECS conformed -#define COMMS_FAILURE 0 -#define ILLEGAL_FUNCTION -1 -#define ILLEGAL_DATA_ADDRESS -2 -#define ILLEGAL_DATA_VALUE -3 -#define SLAVE_DEVICE_FAILURE -4 -#define ACKNOWLEDGE -5 -#define SLAVE_DEVICE_BUSY -6 -#define NEGATIVE_ACKNOWLEDGE -7 -#define MEMORY_PARITY_ERROR -8 - -#define PORT_FAILURE -11 -*/ -#define PORT_FAILURE -3 - - -/************************************************************************ - - read_bdata - - F.Locci: just read raw byte array. Take care of frame longer than - 125 words. - -*************************************************************************/ - -#define MAX_READ_BDATA 200 - -int read_bdata(int slave, /* slave id */ - int start_addr, /* word pointer addr. */ - int count, /* byte count to read */ - u8 *dest, /* byte array */ - int ttyfd, /* connection id */ - int send_retries, /* number of request retries */ - const struct timespec *response_timeout); /* timeout value */ - - -/************************************************************************* - - write_bdata - - F.Locci: just write raw byte array. Take care of frame which exceeds - modbus frame std. length. - !!byte count MUST be even value - 16 bits alignment in that PLC - -***************************************************************************/ - -#define MAX_WRITE_BDATA 200 - -int write_bdata(int slave, /* slave id */ - int start_addr, /* word pointer addr. */ - int count, /* byte count to write - MUST be even value */ - u8 *data, /* byte array */ - int ttyfd, /* connection id */ - int send_retries, /* number of request retries */ - const struct timespec *response_timeout); /* timeout value */ - - -/************************************************************************ - - initialise - - This function sets up the libraries - (allocate memory for buffers, initialise data strcutures, etc) - -**************************************************************************/ -int mb_master_init(int nd_count); - -int mb_master_done(void); - - -/*************************************************************************** - - set_up_comms - - This function sets up (closes) a connection to a remote modbus - slave. - - -***************************************************************************/ -int mb_master_connect(node_addr_t node_addr); - -int mb_master_close(int nd); - -#ifdef __cplusplus -} -#endif - -#endif /* MODBUS_MASTER_H */ - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master_private.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master_private.h deleted file mode 100644 index 5f9d4b7f63fa40160e90ad9597ab0fbddf8c6366..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_master_private.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * (c) 2002 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -#ifndef MODBUS_MASTER_PRIVATE_H -#define MODBUS_MASTER_PRIVATE_H - -/*F.Locci #include <plc.h>*/ /* get the plc data types */ -#include "mb_util.h" -#include "mb_master.h" - - -#define QUERY_BUFFER_SIZE MAX_L2_FRAME_LENGTH - -#define DEF_LAYER2_SEND_RETRIES 1 - -#define DEF_IGNORE_ECHO 0 - -#define DEF_OPTIMIZATION optimize_speed - - -#endif /* MODBUS_MASTER_PRIVATE_H */ - - - - - - - - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_tcp.cpp b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_tcp.cpp deleted file mode 100644 index f918409981c6121c59e24e1c930489b3b755c63d..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_tcp.cpp +++ /dev/null @@ -1,1407 +0,0 @@ -/* - * (c) 2002 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - * - * - * Revision : v1.0 - * Date : 11/2004 - F.Locci - * Objet : .remove static prefix into modbus_tcp_write() declaration - * to make code reentrant. - * .move global recv_buf_ to local one into read/write_bdata() - * Look at v1.0 comments - * - * Revision : v1.1 - * Date : 12/2006 - F.Locci - * Objet : .modify code (pointing things) to be Lces platform compatible - * Look at v1.1 comments - */ - - -/* TODO: - * - clean up the code (access to nw_array_, etc...) - * - closing sockets that will be idle for more than 1 second... - */ - - -#include <fcntl.h> /* File control definitions */ -#include <stdio.h> /* Standard input/output */ -#include <string.h> -#include <stdlib.h> -#include <termio.h> /* POSIX terminal control definitions */ -#include <unistd.h> /* POSIX Symbolic Constants */ -#include <assert.h> -#include <errno.h> /* Error definitions */ -#include <time.h> /* clock_gettime() */ -#include <sys/types.h> -#ifdef __Lynx__ -#include <bsd/in_systm.h> -#include <uio.h> -#endif -#include <sys/socket.h> -#include <netinet/in.h> /* required for htons() and ntohs() */ -#include <netinet/tcp.h> /* TCP level socket options */ -#include <netinet/ip.h> /* IP level socket options */ - -/*F.Locci, -#include <plc.h> -#include <misc/sin_util.h> -*/ -#include <silecs-communication/protocol/core/ietype.h> - -#include "sin_util.h" -/*F.Locci end*/ - -#include "mb_layer1.h" /* The public interface this file implements... */ -#include "mb_tcp_private.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* #define DEBUG */ /* uncomment to see the data sent and received */ - - -#define modbus_tcp_write modbus_write -#define modbus_tcp_read modbus_read -#define modbus_tcp_init modbus_init -#define modbus_tcp_done modbus_done -#define modbus_tcp_connect modbus_connect -#define modbus_tcp_listen modbus_listen -#define modbus_tcp_close modbus_close -#define modbus_tcp_silence_init modbus_silence_init -#define modbus_tcp_get_min_timeout modbus_get_min_timeout - - - - -/************************************/ -/** **/ -/** Include common code... **/ -/** **/ -/************************************/ - -#include "mb_time_util.h" - - - - -/**************************************************************/ -/**************************************************************/ -/**** ****/ -/**** ****/ -/**** Forward Declarations ****/ -/**** and Defaults ****/ -/**** ****/ -/**************************************************************/ -/**************************************************************/ - - - /* A Node Descriptor metadata, - * Due to the fact that modbus TCP is connection oriented, - * and that if the client detects an error the connection - * must be shut down and re-established automatically, - * the modbus TCP layer needs to keep the address of the remote server. - * - * We do this by implementing a node descriptor table, in which each - * entry will have the remote address, and the file descriptor - * of the socket currently in use. - * - * We do not pass the file descriptor up to the next higher layer. We - * send them the node descriptor instead... - */ -#define MB_MASTER_NODE AF_INET /* This MUST be set to AF_INET !! */ -#define MB_LISTEN_NODE AF_AX25 /* actually any value will do, as long as it is != AF_INET */ -#define MB_SLAVE_NODE AF_X25 /* actually any value will do, as long as it is != AF_INET */ -#define MB_FREE_NODE AF_ROSE /* actually any value will do, as long as it is != AF_INET */ -typedef sa_family_t nd_type_t; - -typedef struct { - int fd; /* socket descriptor == file descriptor */ - /* NOTE: - * Modbus TCP says that on error, we should close - * a connection and retry with a new connection. - * Since it takes time for a socket to close - * a connection if the remote server is down, - * we close the connection on the socket, close the - * socket itself, and create a new one for the new - * connection. There will be times when the node will - * not have any valid socket, and it will have to - * be created on the fly. - * When the node does not have a valid socket, - * fd will be set to -1 - */ - struct sockaddr_in addr; /* NOTE: - * We re-use the addr.sin_family member to define the - * state of the node descriptor. - * If addr.sin_family == MB_MASTER_NODE - * The node descriptor was initialised by the - * modbus_connect() function. - * The node descriptor is being used by a master - * device, and the addr contains the address of the slave. - * Remember that in this case fd may be >= 0 while - * we have a valid connection, or it may be < 0 when - * the connection needs to be reset. - * If addr.sin_family == MB_LISTEN_NODE - * The node descriptor was initialised by the - * modbus_listen() function. - * The node is merely used to accept() new connection - * requests. The new slave connections will use another - * node to transfer data. - * In this case fd must be >= 0. - * fd < 0 is an ilegal state and should never occur. - * If addr.sin_family == MB_SLAVE_NODE - * The node descriptor was initialised when a new - * connection request arrived on a MB_LISTEN type node. - * The node descriptor is being used by a slave device, - * and is currently being used to connect to a master. - * In this case fd must be >= 0. - * fd < 0 is an ilegal state and should never occur. - * If addr.sin_family == FREE_ND - * The node descriptor is currently not being used. - * In this case fd is set to -1, but is really irrelevant. - */ - int close_on_silence; /* A flag used only by Master Nodes. - * When (close_on_silence > 0), then the connection to the - * slave device will be shut down whenever the - * modbus_tcp_silence_init() function is called. - * Remember that the connection will be automatically - * re-established the next time the user wishes to communicate - * with the same slave (using this same node descripto). - * If the user wishes to comply with the sugestion - * in the OpenModbus Spec, (s)he should set this flag - * if a silence interval longer than 1 second is expected. - */ -} nd_entry_t; - - -static void nd_entry_init(nd_entry_t *nde) { - nde->addr.sin_family = MB_FREE_NODE; - nde->fd = -1; /* not currently connected... */ -} - - - - -typedef struct { - /* the array of node descriptors, and current size... */ - nd_entry_t *node; - int node_count; /* total number of nodes in the node[] array */ - int free_node_count; /* number of free nodes in the node[] array */ - /* fd set with all the fd in the node array already set - * This saves time in not having to re-initialize a fd_set every time - * the modbus_read() function is called... - */ - fd_set all_fds; - /* the highest fd in the fds set... This is used for the select() call */ - int all_fd_high; - /* fd set with all the fd belonging to MB_MASTER_NODE and MB_SLAVE_NODE - * type nodes already set. - * This saves time in not having to re-initialize a fd_set every time - * the modbus_read() function is called... - */ - fd_set ms_fds; - /* the highest fd in the fds set... This is used for the select() call */ - int ms_fd_high; -} nd_table_t; - - -/*F.Locci, add connection timeout - look at <rfcsock.c> file*/ -extern int connect_nonb(int sockfd, struct sockaddr *saptr, socklen_t salen, int nsec); - - -static int nd_table_init(nd_table_t *ndt, int nd_count) { - int count; - - /* initialise the node table with default values... */ - ndt->node = NULL; - ndt->node_count = 0; - ndt->free_node_count = 0; - FD_ZERO(&(ndt->all_fds)); - ndt->all_fd_high = -1; - FD_ZERO(&(ndt->ms_fds)); - ndt->ms_fd_high = -1; - - /* initialise the node descriptor metadata array... */ - ndt->node = (nd_entry_t*) malloc(sizeof(nd_entry_t) * nd_count); - if (ndt->node == NULL) { - /*F.Locci - plc_log_errmsg(1, "Out of memory: error initializing node address buffer"); - */ - printf("\nOut of memory: error initializing node address buffer\n"); - return -1; - } - ndt->node_count = nd_count; - ndt->free_node_count = nd_count; - - /* initialise the state of each node in the array... */ - for (count = 0; count < nd_count; count++) { - nd_entry_init(&ndt->node[count]); - } /* for() */ - - return nd_count; /* number of succesfully created nodes! */ -} - - - -static int nd_table_get_free_node(nd_table_t *ndt, nd_type_t nd_type) { - int count; - - /* check for free nodes... */ - if (ndt->free_node_count <= 0) - /* no free nodes... */ - return -1; - - /* Decrement the free node counter...*/ - ndt->free_node_count--; - - /* search for a free node... */ - for (count = 0; count < ndt->node_count; count++) { - if(ndt->node[count].addr.sin_family == MB_FREE_NODE) { - /* found one!! */ - ndt->node[count].addr.sin_family = nd_type; - return count; - } - } /* for() */ - - /* Strange... We should have free nodes, but we didn't finda any! */ - /* Let's try to get into a consistent state, and return an error! */ - ndt->free_node_count = 0; - return -1; -} - - - -static void nd_table_close_node(nd_table_t *ndt, int nd) { - - if(ndt->node[nd].addr.sin_family == MB_FREE_NODE) - /* Node already free... */ - return; - - /* Increment the free node counter...*/ - ndt->free_node_count++; - /* Mark the node as being free. */ - ndt->node[nd].addr.sin_family = MB_FREE_NODE; - - return; -} - - - -/**************************************************************/ -/**************************************************************/ -/**** ****/ -/**** ****/ -/**** Global Library State ****/ -/**** ****/ -/**** ****/ -/**************************************************************/ -/**************************************************************/ - - /* The receive buffer... */ - /* NOTE: The library supports multiple simultaneous connections, - * but will only read a message from one at a time. - * (i.e. once it starts receiving a message from one node, it - * will read that message to the end, even though another message - * may start arriving later on another node, but finish arriving - * earlier than the first. The second message will be ignored until - * the first has been completely received, or a timeout occurs...) - * This means we can re-use the same buffer for all the nodes. - * It doesn't seem to be worth the hassle and increased memory - * use to implement it properly with one buffer per node... - v1.0 -static u8 *recv_buf_ = NULL; -*/ - - /* The node descriptor table... */ - /* NOTE: This variable is also used to check whether the library - * has been previously initialised. - * If == NULL, �> library not yet initialised... - */ -static nd_table_t *nd_table_ = NULL; - - - -/**************************************************************/ -/**************************************************************/ -/**** ****/ -/**** ****/ -/**** Local Utility functions... ****/ -/**** ****/ -/**** ****/ -/**************************************************************/ -/**************************************************************/ - - -#define min(a,b) ((a<b)?a:b) -#define max(a,b) ((a>b)?a:b) - -/************************************/ -/** **/ -/** Configure socket for Modbus **/ -/** **/ -/************************************/ - - -static int configure_socket(int socket_id) { - - /* configure the socket */ - /* set the TCP no delay flag. */ - {int bool_opt = 1; - if (setsockopt(socket_id, SOL_TCP, TCP_NODELAY, - (const void *)&bool_opt, sizeof(bool_opt)) - < 0) - return -1; - } - - /* set the IP low delay option. */ - {int priority_opt = IPTOS_LOWDELAY; - if (setsockopt(socket_id, SOL_IP, IP_TOS, - (const void *)&priority_opt, sizeof(priority_opt)) - < 0) - return -1; - } - -#if 0 - /* send buffer */ - /* NOTE: For slave devices, that may be receiving multiple - * requests before they have a chance to reply to the first, - * it probably is a good idea to have a larger receive buffer. - * - * For the send buffer, it probably does not make sense to - * waste time asking for a smaller buffer, since the larger - * default buffer has already been allocated (the socket has already - * been created!) - * - * We might just as well leave out the configuration of the socket - * buffer size... - */ -#define SOCK_BUF_SIZE 300 /* The size proposed in the Modbus TCP spec. */ - {int sock_buf_size; - sock_buf_size = SOCK_BUF_SIZE; - if (setsockopt(socket_id, SOL_SOCKET, SO_SNDBUF, - (const void *)&sock_buf_size, sizeof(sock_buf_size)) - < 0) - return -1; - /* recv buffer */ - sock_buf_size = SOCK_BUF_SIZE; - if (setsockopt(socket_id, SOL_SOCKET, SO_RCVBUF, - (const void *)&sock_buf_size, sizeof(sock_buf_size)) - < 0) - return -1; - } -#endif - - return 0; -} - - -/************************************/ -/** **/ -/** Connect socket to remote host **/ -/** **/ -/************************************/ - -/* This function will create a new socket, and connect it to a remote host... */ -static inline int open_connection(int nd) { - int socket_id; - - if (nd_table_->node[nd].fd >= 0) - /* nd already connected) */ - return nd_table_->node[nd].fd; - - if (nd_table_->node[nd].addr.sin_family != AF_INET) - /* invalid remote address, or invalid nd */ - return -1; - - /* lets try to connect... */ - /* create the socket */ - if ((socket_id = socket(PF_INET, DEF_TYPE, 0 /* protocol_num */)) < 0) - return -1; - - /* configure the socket */ - if (configure_socket(socket_id) < 0) { - close(socket_id); - return -1; - }; - - /* establish the connection to remote host */ - if (connect_nonb(socket_id, - (struct sockaddr *)&(nd_table_->node[nd].addr), - sizeof(nd_table_->node[nd].addr), MAX_CONNECT_TIMEOUT) < 0) { - close(socket_id); - return -1; - } - - nd_table_->node[nd].fd = socket_id; - FD_SET(socket_id, &(nd_table_->all_fds)); - nd_table_->all_fd_high = max(nd_table_->all_fd_high, socket_id); - FD_SET(socket_id, &(nd_table_->ms_fds)); - nd_table_->ms_fd_high = max(nd_table_->ms_fd_high, socket_id); - - return socket_id; -} - - -/* This function will accept a new connection request, and attribute it to a new node... */ -static inline int accept_connection(int nd) { - int socket_id, new_nd; - - if ((new_nd = nd_table_get_free_node(nd_table_, MB_SLAVE_NODE)) < 0) - /* no available free nodes for the new connection... */ - return -1; - - /* lets accept new connection request... */ - if ((socket_id = accept(nd_table_->node[nd].fd, NULL, NULL)) < 0) { - /* error establishing new connection... */ - nd_table_close_node(nd_table_, new_nd); /* first free up the un-used node. */ - return -1; - } - - /* configure the socket */ - if (configure_socket(socket_id) < 0) { - nd_table_close_node(nd_table_, new_nd); /* first free up the un-used node. */ - close(socket_id); - return -1; - }; - - /* set up the node entry and update the fd sets */ - nd_table_->node[new_nd].fd = socket_id; - FD_SET(socket_id, &(nd_table_->all_fds)); - nd_table_->all_fd_high = max(nd_table_->all_fd_high, socket_id); - FD_SET(socket_id, &(nd_table_->ms_fds)); - nd_table_->ms_fd_high = max(nd_table_->ms_fd_high, socket_id); - - return new_nd; -} - - -static inline void close_connection(int nd) { - if (nd_table_->node[nd].fd < 0) - /* nd already disconnected */ - return; - - int res = shutdown(nd_table_->node[nd].fd, SHUT_RDWR); -#ifdef DEBUG -{ - /* could not read registers from slave! */ - printf("shutdown() returned %d\n",res); -} -#endif - - close(nd_table_->node[nd].fd); - FD_CLR(nd_table_->node[nd].fd, &nd_table_->all_fds); - FD_CLR(nd_table_->node[nd].fd, &nd_table_->ms_fds); - nd_table_->node[nd].fd = -1; -} - - - -/************************************/ -/** **/ -/** Data format conversion **/ -/** **/ -/************************************/ - -/* - * Functions to convert u16 variables - * between network and host byte order - * - * NOTE: Modbus uses MSByte first, just like - * tcp/ip, so we use the htons() and - * ntoh() functions to guarantee - * code portability. - */ - -static inline u16 mb_hton(u16 h_value) { -/* return h_value; */ - return htons(h_value); -} - -static inline u16 mb_ntoh(u16 m_value) { -/* return m_value; */ - return ntohs(m_value); -} - -static inline u8 msb(u16 value) { -/* return Most Significant Byte of value; */ - return (value >> 8) & 0xFF; -} - -static inline u8 lsb(u16 value) { -/* return Least Significant Byte of value; */ - return value & 0xFF; -} - -#define u16_v(char_ptr) (*((u16 *)(&(char_ptr)))) - - -/************************************/ -/** **/ -/** Build/Check a frame header **/ -/** **/ -/************************************/ - -/* A modbus TCP frame header has 6 bytes... - * header[0-1] -> transaction id - * header[2-3] -> must be 0 - * header[4-5] -> frame data length (must be <= 255) - */ -#if TCP_HEADER_LENGTH < 6 -#error This code assumes a header size of 6 bytes, but TCP_HEADER_LENGTH < 6 -#endif - -static inline void build_header(u8 *header, - u16 transaction_id, - u16 byte_count) -{ - u16_v(header[0]) = mb_hton(transaction_id); - header[2] = 0; - header[3] = 0; - u16_v(header[4]) = mb_hton(byte_count); -} - - -static inline int check_header(u8 *header, - u16 *transaction_id, - u16 *byte_count) -{ - if ((header[2] != 0) || (header[3] != 0)) - return -1; - - *transaction_id = mb_ntoh(*(u16 *)(header + 0)); - *byte_count = mb_ntoh(*(u16 *)(header + 4)); - - if (*byte_count > MAX_L2_FRAME_LENGTH) - return -1; - - return 0; -} - - - - - -/**************************************************************/ -/**************************************************************/ -/**** ****/ -/**** ****/ -/**** Sending of Modbus TCP Frames ****/ -/**** ****/ -/**** ****/ -/**************************************************************/ -/**************************************************************/ - -int modbus_tcp_write(int nd, /* node descriptor */ - u8 *data, - size_t data_length, - u16 transaction_id) -{ -#define data_vector_size 2 - - /* NOTE: The following variables do not have to be static for the code to work - * correctly. It simply does not make sense to re-initialize these - * structures with the same values every time the function is called. - */ - /*v1.0*/ - u8 header[TCP_HEADER_LENGTH], *ptriovec/*v1.1*/; - struct iovec data_vector[data_vector_size] = {{(void *)header, TCP_HEADER_LENGTH},{NULL, 0}}; - struct msghdr msg = {NULL, 0, data_vector, data_vector_size, NULL, 0, 0}; - int res, bytes_sent; - -#ifdef DEBUG - printf("modbus_tcp_write(): called... nd=%d\n", nd); -#endif - - if ((nd >= nd_table_->node_count) || (nd < 0)) - /* invalid node descriptor... */ - return -1; - - /************************* - * prepare the header... * - *************************/ - build_header(header, transaction_id, data_length); -#ifdef DEBUG -/* Print the hex value of each character that is about to be - * sent over the bus. - */ - { int i; - printf("modbus_tcp_write(): sending data...\n"); - for(i = 0; i < TCP_HEADER_LENGTH; i++) - printf("[0x%2X]", header[i]); - for(i = 0; i < data_length; i++) - printf("[0x%2X]", data[i]); - printf("\n"); - } -#endif - - /****************************************** - * do we need to re-establish connection? * - ******************************************/ - if (open_connection(nd) < 0) { -#ifdef DEBUG - printf("modbus_tcp_write(): could not establish connection...\n"); -#endif - return -1; - } - - /********************** - * write to output... * - **********************/ - /* We are optimising for the most likely case, and in doing that - * we are making the least likely case have worse behaviour! - * Read on for an explanation... - * - * - The optimised behaviour for the most likely case: - * We have set the NO_DELAY flag on the socket, so the IP datagram - * is not delayed and is therefore sent as soon as any data is written to - * the socket. - * In order to send the whole message in a single IP datagram, we have to - * write both the the header and the data with a single call to write() - * In order to not to have to copy the data around just to add the - * message header, we use sendmsg() instead of write()! - * - * - The worse behaviour for the least likely case: - * If for some reason only part of the data is sent with the first call to - * write(), a datagram is sent right away, and the subsequent data will - * be sent in another datagram. :-( - */ - data_vector[data_vector_size - 1].iov_base = data; - data_vector[data_vector_size - 1].iov_len = data_length; - bytes_sent = 0; - while (1) { - /* Please see the comment just above the main loop!! */ - res = sendmsg(nd_table_->node[nd].fd, &msg, 0); - if (res < 0) { - if ((errno != EAGAIN ) && (errno != EINTR )) { - /* error sending message... */ - close_connection(nd); - return -1; - } else { - continue; - } - } else { - /* res >= 0 */ - bytes_sent += res; - if (bytes_sent >= data_length + TCP_HEADER_LENGTH) { - /* query succesfully sent! */ -#ifdef DEBUG - printf("modbus_tcp_write(): sent %d bytes\n", bytes_sent); -#endif - return data_length; - } - - /* adjust the data_vector... */ - /*v1.1*/ - if (res < data_vector[0].iov_len) { - data_vector[0].iov_len -= res; - ptriovec = (u8 *)data_vector[0].iov_base; - ptriovec += res; - data_vector[0].iov_base = (void *)ptriovec; - } else { - res -= data_vector[0].iov_len; - data_vector[0].iov_len = 0; - data_vector[1].iov_len -= res; - ptriovec = (u8 *)data_vector[1].iov_base; - ptriovec += res; - data_vector[1].iov_base = (void *)ptriovec; - } - } - } /* while (1) */ - - /* humour the compiler... */ - return -1; -} - - - -/**************************************************************/ -/**************************************************************/ -/**** ****/ -/**** ****/ -/**** Receiving Modbus TCP Frames ****/ -/**** ****/ -/**** ****/ -/**************************************************************/ -/**************************************************************/ - - -/* A helper function to modbus_tcp_read() - * - * WARNING: The semantics of this function are not what you would expect! - * - * if (data_already_available != 0) - * It assumes that select() has already been called before - * this function got called, and we are therefore guaranteed - * to have at least one byte to read off the socket (the fd). - * - * if (data_already_available == 0) - * it starts off by calling select()! - * - * - * NOTE: Ususal select semantics for (a: end_time == NULL) and - * (b: *end_time == 0) also apply. - * - * (a) Indefinite timeout - * (b) Try once, and and quit if no data available. - */ -static int read_bytes(int fd, - u8 *data, - int max_data_count, - const struct timespec *end_time, - int data_already_available) -{ - fd_set rfds; - int res, data_count; - - data_count = 0; - while (data_count < max_data_count) { - /*============================* - * wait for data availability * - *============================*/ - if (data_already_available == 0) { - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - if (my_select(fd + 1, &rfds, end_time) < 0) - return -1; - } - - /*============================* - * read the available data... * - *============================*/ - res = read(fd, data + data_count, max_data_count - data_count); - if (res == 0) { - /* We are guaranteed to have data to read off the fd since we called - * select(), but read() returned 0 bytes. - * This means that the remote process has closed down the connection, - * so we return 0. - */ - return 0; - } - - if (res < 0) { - if (errno != EINTR) - return -1; - else - res = 0; - } - #ifdef DEBUG - {/* display the hex code of each character received */ - int i; - for (i=0; i < res; i++) - printf("%2X ", *(data + data_count + i)); - } - #endif - data_count += res; - data_already_available = 0; - } /* while ()*/ - - /* data read succesfully... */ - return data_count; -} - - - - -/************************************/ -/** **/ -/** Read a Modbus TCP frame **/ -/** **/ -/************************************/ - -/* The public function that reads a valid modbus frame. - * The frame is read from: - * - the node descriptor nd, if nd >= 0 - * - any valid and initialised node descriptor, if nd = -1 - * In this case, the node where the data is eventually read from - * is returned in *nd. - * - * The send_data and send_length parameters are ignored... - * - * return value: The length (in bytes) of the valid frame, - * -1 on error - * - * NOTE: Ususal select semantics for (a: recv_timeout == NULL) and - * (b: *recv_timeout == 0) also apply. - * - * (a) Indefinite timeout - * (b) Try once, and and quit if no data available. - */ - -/* NOTES: - * - We re-use the recv_buf_ to load the frame header, so we have to make - * sure that the buffer is large enough to take it... - */ -/*v1.0 -#ifndef __Lynx__ -#if RECV_BUFFER_SIZE < 5 //TCP_HEADER_LENGTH -#error The receive buffer is smaller than the frame header length. -#endif -#endif -*/ - -int modbus_tcp_read(int *nd, /* node descriptor */ - u8 *recv_data_ptr/*v1.0*/, - u16 *transaction_id, - u8 *send_data, /* ignored ! */ - int send_length, /* ignored ! */ - const struct timespec *recv_timeout) { - - struct timespec cur_time, end_time, *ts_ptr; - /*v1.0 - u8 *local_recv_data_ptr; - */ - u16 local_transaction_id = 0; - u16 frame_length; - int orig_nd, fd, fd_high, data_already_available; - -#ifdef DEBUG - printf("modbus_tcp_read(): called... nd=%d\n", *nd); -#endif - - if (nd == NULL) - return -1; - - if (*nd >= nd_table_->node_count) - /* invalid *nd */ - /* remember that *nd < 0 is valid!! */ - return -1; - - /*v1.0 - if (recv_data_ptr == NULL) - recv_data_ptr = &local_recv_data_ptr; - */ - if (transaction_id == NULL) - transaction_id = &local_transaction_id; - - /* We will potentially call read() multiple times to read in a single frame. - * We therefore determine the absolute time_out, and use this as a parameter - * for each call to read_bytes() instead of using a relative timeout. - * - * NOTE: see also the timeout related comment in the read_bytes() function! - * - * NOTE: clock_gettime() is rather expensive, between 7000 and 7500 clock - * cycles (measured with rdtsc on an Intel Pentium) - * gettimeofday() is half as expensive (3000 to 3500 clock cycles), - * but is not POSIX compliant... :-( - * Nevertheless this is peanuts (20 us on a 350 MHz cpu) compared to - * the timescales required to read a modbus frame over a serial bus - * (aprox. 10 ms for a 10 byte frame on a 9600 baud bus!) - */ - /* get the current time... */ - if (recv_timeout == NULL) { - ts_ptr = NULL; - } else { - ts_ptr = &end_time; - if ((recv_timeout->tv_sec == 0) && (recv_timeout->tv_nsec == 0)) { - end_time = *recv_timeout; - } else { - if (clock_gettime(CLOCK_REALTIME, &cur_time) < 0) - return -1; - end_time = timespec_add(cur_time, *recv_timeout); - } - } - - /* We will loop forever... - * We jump out of the loop and return from the function as soon as: - * - we receive a valid modbus message; - * OR - * - we time out. - */ - orig_nd = *nd; - while(1) { - *nd = orig_nd; - - /* If we must read off a single node... */ - if (*nd >= 0) - /* but the node does not have a valid fd */ - if ((nd_table_->node[*nd].addr.sin_family == MB_FREE_NODE) || - (nd_table_->node[*nd].fd < 0)) - /* then we return an error... */ - return -1; - - /* We want to call the read_bytes() function only after we are sure there - * is data waiting to be read on the socket, so we call select here. - * - * We only call select() here for the case when (*nd >= 0), - * since for the other case select() will be called later on... - */ - data_already_available = 1; - if (*nd >= 0) - data_already_available = 0; - - /* if *nd < 0, we will be: - * - reading off any valid node descriptor!! - * - and accepting new connection requests, if we have free nodes available!! - */ - while (*nd < 0) { - int nd_count; - fd_set rfds; - - if (nd_table_->free_node_count > 0) { - /* We have free nodes, so we will also listen on nodes - * that will accept new connections requests... - */ - rfds = nd_table_->all_fds; - fd_high = nd_table_->all_fd_high; - } else { - /* We do not have free nodes, so we will only listen on nodes - * that do not accept new connections requests... - */ - rfds = nd_table_->ms_fds; - fd_high = nd_table_->ms_fd_high; - } - - if (my_select(fd_high + 1, &rfds, ts_ptr) < 0) - return -1; - - /* figure out which nd is ready to be read... */ - for (nd_count = 0; nd_count < nd_table_->node_count; nd_count++) { - if (nd_table_->node[nd_count].addr.sin_family != MB_FREE_NODE) { - if (FD_ISSET(nd_table_->node[nd_count].fd, &rfds)) { - /* Found the node descriptor... */ - if (nd_table_->node[nd_count].addr.sin_family == MB_LISTEN_NODE) { - /* We must accept a new connection... - * No need to check for errors. - * If one occurs, there is nothing we can do... - */ - *nd = accept_connection(nd_count); -#ifdef DEBUG - printf("modbus_tcp_read(): new connection request on nd=%d\n", nd_count); - printf("modbus_tcp_read(): new connection accepted on nd=%d", *nd); -#endif - *nd = -1; /* this will keep us in the while loop */ - } else { - /* We will read a frame off this nd */ - *nd = nd_count; /* this will get us out of the while loop later on. */ - } - /* we have found the node descriptor, so let's jump out of the for(;;) loop */ - break; - } - } - } /* for(;;) */ - } /* while (*nd < 0) */ - - /* Just a consistency check... */ - if (*nd < 0) - /* If the code is correct, this should never occur... */ - return -1; - -#ifdef DEBUG - printf("modbus_tcp_read(): reading off nd=%d\n", *nd); -#endif - /*=========================* - * read a Modbus TCP frame * - *=========================*/ - /* assume error... */ - fd = nd_table_->node[*nd].fd; - /*v1.0 - *recv_data_ptr = NULL; - */ - - /*-------------* - * read header * - *-------------*/ - if (read_bytes(fd, recv_data_ptr/*v1.0*/, TCP_HEADER_LENGTH, ts_ptr, data_already_available) - == TCP_HEADER_LENGTH) { - /* let's check for header consistency... */ - if (check_header(recv_data_ptr/*v1.0*/, transaction_id, &frame_length) < 0) { -#ifdef DEBUG - printf("modbus_tcp_read(): frame with non valid header...\n"); -#endif - /* We let the code fall through to the error handler, that will - * close the connection! - * This is exactly what the modbus TCP protocol specifies! - */ - } else { - /*-----------* - * read data * - *-----------*/ - /* NOTE: the function read_bytes() assumes that there is data ready to be - * read off the socket, so we have to call select() ourselves - * before calling it. - */ - - data_already_available = 0; - if (read_bytes(fd, recv_data_ptr/*v1.0*/, frame_length, ts_ptr, data_already_available) - == frame_length) { - /* frame received succesfully... */ -#ifdef DEBUG - printf("\n"); -#endif - /*v1.0 - *recv_data_ptr = recv_buf_; - */ - return frame_length; - } - } - } - - /* We had an error reading the frame... - * We handle it by closing the connection, as specified by - * the modbus TCP protocol! - */ - -#ifdef DEBUG - printf("modbus_tcp_read(): error reading frame. Closing connection...\n"); -#endif - /* We close the socket... */ - close_connection(*nd); - - /* If it is a slave, we free the node... */ - if(nd_table_->node[*nd].addr.sin_family == MB_SLAVE_NODE) - nd_table_close_node(nd_table_, *nd); - - /* We try to read another frame... */ - } /* while(1) */ - - /* humour the compiler... */ - return -1; -} - - - - - - -/**************************************************************/ -/**************************************************************/ -/**** ****/ -/**** ****/ -/**** Initialising and Shutting Down Library ****/ -/**** ****/ -/**** ****/ -/**************************************************************/ -/**************************************************************/ - - -/******************************/ -/** **/ -/** Load Default Values **/ -/** **/ -/******************************/ - -static void set_defaults(const char **service) { - /* Set the default values, if required... */ - if (*service == NULL) - *service = DEF_SERVICE; -} - - -/******************************/ -/** **/ -/** Initialise Library **/ -/** **/ -/******************************/ -/* returns the number of nodes succesfully initialised... - * returns -1 on error. - */ -int modbus_tcp_init(int nd_count, - optimization_t opt /* ignored... */, - int *extra_bytes) { -#ifdef DEBUG - printf("modbus_tcp_init(): called...\n"); - printf("creating %d nodes:\n", nd_count); -#endif - - if (nd_table_ != NULL) - /* library already previously initialised! */ - return -1; - - if (nd_count <= 0) - /* invalid node count... */ - goto error_exit_0; - - /* set the extra_bytes value... */ - /* Please see note before the modbus_rtu_write() function for a - * better understanding of this extremely ugly hack... This will be - * in the mb_rtu.c file!! - * - * The number of extra bytes that must be allocated to the data buffer - * before calling modbus_tcp_write() - */ - if (extra_bytes != NULL) - *extra_bytes = 0; - - /* V1.0: initialise recv buffer - recv_buf_ = malloc(sizeof(u8) * RECV_BUFFER_SIZE); - if (recv_buf_ == NULL) { - //plc_log_errmsg(1, "Out of memory: error initializing receive buffer"); - printf("\nOut of memory: error initializing receive buffer\n"); - goto error_exit_0; - } - */ - - /* create the node table structure... */ - nd_table_ = (nd_table_t*) malloc(sizeof(nd_table_t)); - if (nd_table_ == NULL) { - /*F.Locci - plc_log_errmsg(1, "Out of memory: error initializing node table"); - */ - printf("\nOut of memory: error initializing node table\n"); - goto error_exit_1; - } - - /* initialise the node table... */ - if (nd_table_init(nd_table_, nd_count) < 0) - goto error_exit_2; - -#ifdef DEBUG - printf("modbus_tcp_init(): %d node(s) opened succesfully\n", nd_count); -#endif - return nd_count; /* number of succesfully created nodes! */ - -/* -error_exit_3: - free(nd_table_->node); - nd_table_->node = NULL; - nd_table_->count = 0; -*/ -error_exit_2: - free(nd_table_); nd_table_ = NULL; -error_exit_1: - /*V1.0 - free(recv_buf_); recv_buf_ = NULL; - */ -error_exit_0: - return -1; -} - - - - - - -/******************************/ -/** **/ -/** Open a Master Node **/ -/** **/ -/******************************/ - -int modbus_tcp_connect(node_addr_t node_addr) { - int node_descriptor; - struct sockaddr_in tmp_addr; - -#ifdef DEBUG - printf("modbus_tcp_connect(): called...\n"); - printf(" %s:%s\n", - node_addr.addr.tcp.host, - node_addr.addr.tcp.service); -#endif - - /* Check for valid address family */ - if (node_addr.naf != naf_tcp) - /* wrong address type... */ - return -1; - - /* set the default values... */ - set_defaults(&(node_addr.addr.tcp.service)); - - /* Check the parameters we were passed... */ - if(sin_init_addr(&tmp_addr, - node_addr.addr.tcp.host, - node_addr.addr.tcp.service, - DEF_PROTOCOL) - < 0) { - /* - plc_log_wrnmsg(1, "Error parsing/resolving address %s:%s", - node_addr.addr.tcp.host, - node_addr.addr.tcp.service); - */ - return -1; - } - - /* find a free node descriptor */ - /* NOTE: The following code line works beacuse MB_MASTER_NODE is set to AF_INET! */ -#if MB_MASTER_NODE != AF_INET -#error The code only works if MB_MASTER_NODE == AF_INET, which they are not! -#endif - if ((node_descriptor = nd_table_get_free_node(nd_table_, MB_MASTER_NODE)) < 0) - /* if no free nodes to initialize, then we are finished... */ - return -1; - - nd_table_->node[node_descriptor].addr = tmp_addr; - nd_table_->node[node_descriptor].fd = -1; /* not currently connected... */ - nd_table_->node[node_descriptor].close_on_silence = node_addr.addr.tcp.close_on_silence; - - if (nd_table_->node[node_descriptor].close_on_silence < 0) - nd_table_->node[node_descriptor].close_on_silence = DEF_CLOSE_ON_SILENCE; - -#ifdef DEBUG - printf("modbus_tcp_connect(): returning nd=%d\n", node_descriptor); -#endif - return node_descriptor; -} - - - - -/******************************/ -/** **/ -/** Open a Slave Node **/ -/** **/ -/******************************/ - -int modbus_tcp_listen(node_addr_t node_addr) { - int fd, nd; - -#ifdef DEBUG - printf("modbus_tcp_listen(): called...\n"); - printf(" %s:%s\n", - node_addr.addr.tcp.host, - node_addr.addr.tcp.service); -#endif - - /* Check for valid address family */ - if (node_addr.naf != naf_tcp) - /* wrong address type... */ - goto error_exit_0; - - /* set the default values... */ - set_defaults(&(node_addr.addr.tcp.service)); - - /* create a socket and bind it to the appropriate port... */ - fd = bind_socket(node_addr.addr.tcp.host, - node_addr.addr.tcp.service, - DEF_PROTOCOL); - if (fd < 0) - goto error_exit_0; - if (listen(fd, DEF_MAX_PENDING_CONNECTION_REQUESTS) < 0) - goto error_exit_0; - - /* find a free node descriptor */ - if ((nd = nd_table_get_free_node(nd_table_, MB_LISTEN_NODE)) < 0) { - /* if no free nodes to initialize, then we are finished... */ - goto error_exit_1; - } - - /* nd_table_->node[nd].addr = tmp_addr; */ /* does not apply for MB_LISTEN_NODE */ - nd_table_->node[nd].fd = fd; /* not currently connected... */ - - /* update the fd sets...*/ - FD_SET(fd, &(nd_table_->all_fds)); - nd_table_->all_fd_high = max(nd_table_->all_fd_high, fd); - -#ifdef DEBUG - printf("modbus_tcp_listen(): returning nd=%d\n", nd); -#endif - return nd; - -error_exit_1: - close(fd); -error_exit_0: - return -1; -} - - - -/******************************/ -/** **/ -/** Close a node **/ -/** **/ -/******************************/ - -int modbus_tcp_close(int nd) { -#ifdef DEBUG - printf("modbus_tcp_close(): called... nd=%d\n", nd); -#endif - -/*F.Locci*/ - if (nd_table_ == NULL) return -1; -/**/ - - if ((nd < 0) || (nd >= nd_table_->node_count)) - /* invalid nd */ - return -1; - - if (nd_table_->node[nd].addr.sin_family == MB_FREE_NODE) - /* already free node */ - return 0; - - close_connection(nd); - - nd_table_->node[nd].addr.sin_family = MB_FREE_NODE; - nd_table_->free_node_count++; - - return 0; -} - - - -/**********************************/ -/** **/ -/** Close all open connections **/ -/** **/ -/**********************************/ - -int modbus_tcp_silence_init(void) { - int nd; - -#ifdef DEBUG - printf("modbus_tcp_silence_init(): called...\n"); -#endif - - /* close all master connections that remain open... */ - for (nd = 0; nd < nd_table_->node_count; nd++) - if (nd_table_->node[nd].addr.sin_family == MB_MASTER_NODE) - if (nd_table_->node[nd].close_on_silence > 0) - /* node is is being used for a master device, - * and wishes to be closed... ...so we close it! - */ - close_connection(nd); - - return 0; -} - - - -/******************************/ -/** **/ -/** Shutdown the Library **/ -/** **/ -/******************************/ - -int modbus_tcp_done(void) { - int i; - -/*F.Locci*/ - if (nd_table_ == NULL) return -1; -/**/ - - /* close all the connections... */ - for (i = 0; i < nd_table_->node_count; i++) - modbus_tcp_close(i); - - /* Free memory... */ - free(nd_table_->node); - /*V1.0 - free(recv_buf_); recv_buf_ = NULL; - */ - free(nd_table_); nd_table_ = NULL; - - return 0; -} - - - - -double modbus_tcp_get_min_timeout(int baud, - int parity, - int data_bits, - int stop_bits) { - return 0; -} - -#ifdef __cplusplus -} -#endif diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_tcp_private.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_tcp_private.h deleted file mode 100644 index b05ca22cc8800608d9d67ab8d3c43a6beea0fb26..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_tcp_private.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * (c) 2002 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -#ifndef MODBUS_TCP_PRIVATE_H -#define MODBUS_TCP_PRIVATE_H - -/*F.Locci #include <plc.h>*/ /* get the plc data types */ -#include "mb_util.h" - - -/* tcp port default configuration... */ -#define DEF_SERVICE "502" /* port used by modbus */ -#define DEF_PROTOCOL "tcp" /* protocol used by modbus tcp */ -#define DEF_TYPE SOCK_STREAM /* Quality of service required of the socket... */ -#define DEF_MAX_PENDING_CONNECTION_REQUESTS 5 - /* maximum number of pending connection requests - * that have not yet been accept()'ed - */ -#define DEF_CLOSE_ON_SILENCE 1 /* Used only by master nodes. - * Flag indicating whether, by default, the connection - * to the slave device should be closed whenever the - * modbus_tcp_silence_init() function is called. - * - * 0 -> do not close connection - * >0 -> close connection - * - * The spec sugests that connections that will not - * be used for longer than 1 second should be closed. - * Even though we expect most connections to have - * silence intervals much shorted than 1 second, we - * decide to use the default of shuting down the - * connections because it is safer, and most other - * implementations seem to do the same. - * If we do not close we risk using up all the possible - * connections that the slave can simultaneouly handle, - * effectively locking out every other master that - * wishes to communicate with that same slave. - */ - - /* Since the receive buffer is also re-used to store the frame header, - * we set it to the larger of the two. - */ -#ifndef __Lynx__ - -#if TCP_HEADER_LENGTH > MAX_L2_FRAME_LENGTH -#define RECV_BUFFER_SIZE TCP_HEADER_LENGTH -#else -#define RECV_BUFFER_SIZE MAX_L2_FRAME_LENGTH -#endif - -#else - -#if defined(TCP_HEADER_LENGTH) > defined(MAX_L2_FRAME_LENGTH) -#define RECV_BUFFER_SIZE TCP_HEADER_LENGTH -#else -#define RECV_BUFFER_SIZE MAX_L2_FRAME_LENGTH -#endif - -#endif - - -#endif /* MODBUS_TCP_PRIVATE_H */ - - - - - - - - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_time_util.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_time_util.h deleted file mode 100644 index e17dc5291874284712bbac99529a309af53c88da..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_time_util.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * (c) 2002 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - - /* Time handling functions used by the modbus protocols... */ -#include <time.h> - -#ifndef __MODBUS_TIME_UTIL_H -#define __MODBUS_TIME_UTIL_H - - -/************************************/ -/** **/ -/** Time format conversion **/ -/** **/ -/************************************/ - -/* Function to load a struct timeval correctly - * from a double. - */ -static inline struct timeval d_to_timeval(double time) { - struct timeval tmp; - - tmp.tv_sec = time; - tmp.tv_usec = 1e6*(time - tmp.tv_sec); - return tmp; -} - -/* Function to ... */ -static inline struct timespec timespec_dif(struct timespec ts1, struct timespec ts2) { - struct timespec ts; - - ts.tv_sec = ts1.tv_sec - ts2.tv_sec; - if(ts1.tv_nsec > ts2.tv_nsec) { - ts.tv_nsec = ts1.tv_nsec - ts2.tv_nsec; - } else { - ts.tv_nsec = 1000000000 + ts1.tv_nsec - ts2.tv_nsec; - ts.tv_sec--; - } - - if (ts.tv_sec < 0) - ts.tv_sec = ts.tv_nsec = 0; - - return ts; -} - -/* Function to ... */ -static inline struct timespec timespec_add(struct timespec ts1, struct timespec ts2) { - struct timespec ts; - - ts.tv_sec = ts1.tv_sec + ts2.tv_sec; - ts.tv_nsec = ts1.tv_nsec + ts2.tv_nsec; - ts.tv_sec += ts.tv_nsec / 1000000000; - ts.tv_nsec = ts.tv_nsec % 1000000000; - return ts; -} - -/* Function to convert a struct timespec to a struct timeval. */ -static inline struct timeval timespec_to_timeval(struct timespec ts) { - struct timeval tv; - - tv.tv_sec = ts.tv_sec; - tv.tv_usec = ts.tv_nsec/1000; - return tv; -} - - - -/************************************/ -/** **/ -/** select() with absolute timeout **/ -/** **/ -/************************************/ - - -/* My private version of select using an absolute timeout, instead of the - * usual relative timeout. - * - * NOTE: Ususal select semantics for (a: end_time == NULL) and - * (b: *end_time == 0) also apply. - * - * (a) Indefinite timeout - * (b) Try once, and and quit if no data available. - */ -static int my_select(int fd, fd_set *rfds, const struct timespec *end_time) { - - int res; - struct timespec cur_time; - struct timeval timeout, *tv_ptr; - fd_set tmp_fds; - - /*============================* - * wait for data availability * - *============================*/ - do { - tmp_fds = *rfds; - /* NOTE: To do the timeout correctly we would have to revert to timers - * and asociated signals. That is not very thread friendly, and is - * probably too much of a hassle trying to figure out which signal - * to use. What if we don't have any free signals? - * - * The following solution is not correct, as it includes a race - * condition. The following five lines of code should really - * be atomic! - * - * NOTE: see also the timeout related comment in the - * modbus_tcp_read() function! - */ - if (end_time == NULL) { - tv_ptr = NULL; - } else { - tv_ptr = &timeout; - if ((end_time->tv_sec == 0) && (end_time->tv_nsec == 0)) { - timeout.tv_sec = timeout.tv_usec = 0; - } else { - /* ATOMIC - start */ - if (clock_gettime(CLOCK_REALTIME, &cur_time) < 0) - return -1; - timeout = timespec_to_timeval(timespec_dif(*end_time, cur_time)); - } - } - - res = select(fd, &tmp_fds, NULL, NULL, tv_ptr); - /* ATOMIC - end */ - - if (res == 0) { -#ifdef DEBUG - printf("Comms time out\n"); -#endif - return -1; - } - if ((res < 0) && (errno != EINTR)) { - return -1; - } - } while (res <= 0); - - *rfds = tmp_fds; - return res; -} - - - - - - -#endif /* __MODBUS_TIME_UTIL_H */ diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_util.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_util.h deleted file mode 100644 index 9d8ea0868bf986eeeb164985cefb0208aafac55a..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/mb_util.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * (c) 2002 Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -#ifndef MB_UTIL_H -#define MB_UTIL_H - -/* This file has constants related to the modbus protocol */ -/* - * Some of these constants are specific to the layer two protocols - * (i.e. master and slave), while others are specific of the - * layer one protocols (i.e. rtu, ascii, tcp). - * - * a) Unfortunately, due to the nature of the modbus protocol, that does not - * include a frame size field in the layer 1 frame (see note 1), and the - * fact that we are implementing it at the user level, the implementation - * of some layer 1 protocols need to know the content of the layer 2 protocol - * in order to determine the size of the frame. - * - * b) The layer two message formats are in fact the same, just reversing the role - * being played (master or slave). - * - * Bothe a) and b) mean we need the same modbus protocol constants in several files. - * It ends up making more sense to put them all together in a single file, which - * makes updating easier, even though we are trying to strictly seperate the layer 1 - * and layer 2 protocols. - * - * - * - * Notes: - * (1) There is no layer 1 field with the frame size, nevertheless this - * size can be determined indirectly due to timing restrictions on the rtu - * protocol. Unfortunately, due to the fact that we are implementing - * it at the user level, we are not guaranteed to detect these timings - * correctly, and therefore have to rely on layer 2 protocol info to - * determine the frame size. - * For the ascii protocol, the frame size is determined indirectly by - * a frame header and tail, so we do not use layer 2 protocol info. - */ - - - /* Layer 2 Frame Structure... */ - /* Valid for both master and slave protocols */ -#define L2_FRAME_HEADER_LENGTH 6 -#define L2_FRAME_BYTECOUNT_LENGTH 1 -#define L2_FRAME_DATABYTES_LENGTH 255 -#define MAX_L2_FRAME_LENGTH (L2_FRAME_HEADER_LENGTH + L2_FRAME_BYTECOUNT_LENGTH + L2_FRAME_DATABYTES_LENGTH) - -#define L2_FRAME_SLAVEID_OFS 0 -#define L2_FRAME_FUNCTION_OFS 1 - - /* Layer 1 - Ascii Frame sizes... */ -#define L2_TO_ASC_CODING 2 /* number of ascii bytes used to code a Layer 2 frame byte */ -#define ASC_FRAME_HEADER_LENGTH 1 -#define ASC_FRAME_HEADER ':' -#define ASC_FRAME_TAIL_LENGTH 2 -#define ASC_FRAME_TAIL_0 '\13' /* 'CR' */ -#define ASC_FRAME_TAIL_1 '\10' /* 'LF' */ -#define ASC_FRAME_LRC_LENGTH 2 - - /* Layer 1 - RTU Frame sizes... */ -#define RTU_FRAME_CRC_LENGTH 2 - - /* Layer 1 - TCP Frame sizes... */ -#define TCP_HEADER_LENGTH 6 - - /* Global Frame sizes */ -#define MAX_RTU_FRAME_LENGTH MAX_L2_FRAME_LENGTH + RTU_FRAME_CRC_LENGTH -#define MAX_ASC_FRAME_LENGTH ((MAX_L2_FRAME_LENGTH * L2_TO_ASC_CODING) + ASC_FRAME_HEADER_LENGTH + ASC_FRAME_TAIL_LENGTH + ASC_FRAME_LRC_LENGTH) - -#endif /* MB_UTIL_H */ - - - - - - - - diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/sin_util.cpp b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/sin_util.cpp deleted file mode 100644 index 682f4bbc2679bfd2f311b60fb9d9416b18c799db..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/sin_util.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * (c) 2000 Jiri Baum - * Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -/* - * Socket INET utility routines - * - * This file implements the routines in sin_util.h - * - * These routines merely make life simpler when working with - * internet protocol sockets - */ -#include "sin_util.h" - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <netdb.h> -#include <string.h> - - -#ifdef __cplusplus -extern "C" { -#endif - - -#define ERR_EXIT(mesg, err_num) {perror(mesg);return err_num;} -#define MSG_EXIT(mesg, err_num) {fprintf(stderr, "%s\n", mesg);return err_num;} - - -int sin_init_dot_addr(struct sockaddr_in *sad_in, - char *dot_addr, - unsigned short int port) -{ -sad_in->sin_family = AF_INET; - -if( inet_aton(dot_addr, &(sad_in->sin_addr)) == 0 ) - ERR_EXIT("inet_aton", -1); - -sad_in->sin_port = htons(port); - -return 1; -} /* sin_init_dot_addr */ - - - - - -int sin_init_addr(struct sockaddr_in *addr, - const char *host, - const char *service, - const char *protocol) -/* initialize an address structure */ -{ -int port_flag; -long int tmp_numb; -long long int lli = 1; -char *error_char; - -struct servent *serv_entry_ptr; -struct hostent *host_entry_ptr; - -bzero ((char *)addr, sizeof(*addr)); -addr->sin_family = AF_INET; - -/* Map service name to port number */ -port_flag = 0; -if (port_flag == 0) - if ((service == NULL) || (strcmp(service, "") == 0)) - {addr->sin_port = htons(0); /* OS will sugest a port when binding */ - port_flag = 1; - } - -if (port_flag == 0) - if ((serv_entry_ptr = getservbyname ((char *)service, (char *)protocol)) ) - {addr->sin_port = serv_entry_ptr->s_port; - port_flag = 1; - } - -if (port_flag == 0) - {tmp_numb = strtol(service, &error_char, 0); - if ((*error_char == '\0') && (error_char != service) && - (tmp_numb >= 0) && (tmp_numb < (lli << (8*sizeof(addr->sin_port))))) - {addr->sin_port = htons(tmp_numb); - port_flag = 1; - } - } - -if (port_flag == 0) - MSG_EXIT("sin_init_addr: Could not determine the port number.", -1); - -/* Map host name to IP address, allowing dotted decimal */ -addr->sin_addr.s_addr = INADDR_NONE; - -if (addr->sin_addr.s_addr == INADDR_NONE) - if ( (host_entry_ptr = gethostbyname ((char *)host)) ) - bcopy (host_entry_ptr->h_addr, - (char *)&(addr->sin_addr), - host_entry_ptr->h_length); - -if (addr->sin_addr.s_addr == INADDR_NONE) - inet_aton (host, (struct in_addr *)addr); - -if (addr->sin_addr.s_addr == INADDR_NONE) - MSG_EXIT("sin_init_addr: Could not determine the host IP address.", -1); - -return 1; -} /* sin_init_addr(...) */ - - - - -int sin_init_proto(int *type, - int *protocol_num, - const char *protocol) -{ -struct protoent *proto_entry_ptr; - -/* determine the protocol */ -*type = SOCK_RDM; -if (strcasecmp (protocol, "udp") == 0) *type = SOCK_DGRAM; -if (strcasecmp (protocol, "tcp") == 0) *type = SOCK_STREAM; -if (strcasecmp (protocol, "raw") == 0) *type = SOCK_RAW; -if (*type == SOCK_RDM) - MSG_EXIT ("sin_init_proto: protocol not supported.", -1); - -/* map protocol name to protocol number */ -if ( (proto_entry_ptr = getprotobyname ((char *)protocol)) == NULL ) - MSG_EXIT ("sin_init_proto: can't get protocol number.", -1); -*protocol_num = proto_entry_ptr->p_proto; - -return 1; -} /* sin_init_proto(...) */ - - -int bind_socket(const char *host, - const char *service, - const char *protocol) -/* create a socket and bind to a local port */ -{ -struct sockaddr_in sock_addr; -int socket_id, type, protocol_num; - -/* initialize the sock_addr sturcture */ -if ( (sin_init_addr (&sock_addr, host, service, protocol)) < 0) - MSG_EXIT ("bind_socket: wrong address format", -1); - -/* determine the protocol */ -if ( (sin_init_proto (&type, &protocol_num, protocol)) < 0) - MSG_EXIT ("bind_socket: wrong protocol format", -1); - -/* create the socket */ -if ( (socket_id = socket (PF_INET, type, protocol_num)) < 0) - ERR_EXIT ("socket", -1); - -/* bind the socket */ -if ( bind (socket_id, (struct sockaddr *)&sock_addr, sizeof (sock_addr)) < 0) - ERR_EXIT ("bind", -1); - -return socket_id; -} /* bind_socket(...) */ - - - -int connect_socket(const char *host, - const char *service, - const char *protocol) -/* create a socket and connect to a remote host */ -{ -struct sockaddr_in sock_addr; -int socket_id, type, protocol_num; - -/* initialize the sock_addr sturcture */ -if ( (sin_init_addr (&sock_addr, host, service, protocol)) < 0) - MSG_EXIT ("bind_socket: wrong address format", -1); - -/* determine the protocol */ -if ( (sin_init_proto (&type, &protocol_num, protocol)) < 0) - MSG_EXIT ("bind_socket: wrong protocol format", -1); - -/* create the socket */ -if ( (socket_id = socket (PF_INET, type, protocol_num)) < 0) - ERR_EXIT ("socket", -1); - -/* bind the socket */ -if ( connect(socket_id, (struct sockaddr *)&sock_addr, sizeof (sock_addr)) < 0) - ERR_EXIT ("bind", -1); - -return socket_id; -} /* connect_socket(...) */ - - - -int get_socket_port(int sock) -/* returns the port to which the socket is bound */ -{ - int length; - struct sockaddr_in srv_addr; - - length = sizeof(srv_addr); - if (getsockname(sock, (struct sockaddr *)&srv_addr, (socklen_t *)&length) < 0) - return -1; - - if (srv_addr.sin_family == AF_INET) - return ntohs(srv_addr.sin_port); - - return -1; /* not INET socket... */ -} /* get_socket_port(...) */ - -#ifdef __cplusplus -} -#endif diff --git a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/sin_util.h b/silecs-communication-cpp/src/silecs-communication/protocol/modbus/sin_util.h deleted file mode 100644 index 5e6a6b6499532c60288bc7a463aee775fb51310c..0000000000000000000000000000000000000000 --- a/silecs-communication-cpp/src/silecs-communication/protocol/modbus/sin_util.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * (c) 2000 Jiri Baum - * Mario de Sousa - * - * Offered to the public under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 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 General - * Public License for more details. - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -#ifndef SIN_UTIL_H -#define SIN_UTIL_H - -#ifdef __cplusplus -extern "C" { -#endif - - -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - - - -int sin_init_addr(struct sockaddr_in *addr, - const char *host, - const char *service, - const char *protocol); -/* initialize an address structure */ - - -int bind_socket(const char *host, - const char *service, - const char *protocol); -/* create a socket and bind to a local port */ - - -int connect_socket(const char *host, - const char *service, - const char *protocol); -/* create a socket and connect to a remote host */ - - -int get_socket_port(int sock); -/* returns the port to which the socket is bound */ - -#ifdef __cplusplus -} -#endif -#endif