Commit 1c588d7a authored by Marco Bartolini's avatar Marco Bartolini
Browse files

Added ModbusChannel library

parent 58942988
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
#ifndef MODBUSCHANNEL_HPP
#define MODBUSCHANNEL_HPP

#include <modbus/modbus.h>
#include <sys/time.h>
#include <string>
#include <cerrno>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>

#define MODBUS_DEFAULT_PORT MODBUS_TCP_DEFAULT_PORT
#define MODBUS_TIMEOUT 2

class ModbusChannel; //fwd declaration
typedef boost::shared_ptr<ModbusChannel> ModbusChannel_sp;


ModbusChannel_sp 
get_modbus_channel(const char* server_ip, 
                   int server_port=MODBUS_DEFAULT_PORT);

class ModbusError: public std::runtime_error
{
    public:
        ModbusError(const char *msg): std::runtime_error(std::string(msg)){};
};

class ModbusTimeout : public ModbusError
{
    public:
        ModbusTimeout(const char *msg): ModbusError(msg){};
};

/**
 * utility called for raising the proper errors during execution of
 * modbus methods
 */
void modbus_check_error(int rc);

class ModbusChannel
{
    public:
        /**
         * Ctor
         * @param ip: the ip of the modbus server
         * @param port: the modbus server port, defaults to 502 as specified by 
         *              modbus refence documentation
         */
        ModbusChannel(const char* server_ip, 
                      int server_port = MODBUS_DEFAULT_PORT);
        /**
         * destructor
         */
        virtual ~ModbusChannel();
        /**
         * Synchronized
         */
        void connect(); //throw ModbusError
        /**
         * Synchronized
         */
        void disconnect();
        inline bool is_connected() {return _is_connected;};
        /**
         * Read from the server the specified bytes into the dest_buffer. 
         * It verifies that the specified address is even and also
         * size is even so that it can use 16 bit words addressing as specified
         * by modbus library. If something goes wrong raises a ModbusError.
         * @param address: the address in bytes of the registers to be read
         * @param size: the size in bytes of the buffer to be read
         * @param dest_buffer: the binary content where data will be copied
         */
        void read(int address, int size, unsigned char *dest_buffer); //throw ModbusError
        /**
         * Write the content of source_buffer at the specified address in the
         * server. It verifies that the destination address is even and also
         * size is even so that it can use 16 bit words addressing as specified
         * by modbus library. If something goes wrong raises a ModbusError.
         * @param address: the address in bytes of the registers to be written
         * @param size: the size in bytes of the buffer to be written
         * @param source_buffer: the binary content to be copied into the server
         */
        void write(int address, int size, const unsigned char *source_buffer); //throw ModbusError
        /**
         * Template method used for directly unpacking the read value at
         * specified address, into the return type
         */
        template<typename T>
        T get(int address); //throw ModbusError
    private:
        /* class members */
        boost::mutex _connection_guard;
        std::string _server_ip;
        int _server_port;
        bool _is_connected;
        modbus_t *_modbus_context;

        /* We do not want this object to be copied so we prohibit copying
         * operations by definition , the _modbus_context is thread safe 
         * so the ModbusChannel can be shared among many threads
         */
        ModbusChannel(const ModbusChannel&);
        ModbusChannel& operator=(const ModbusChannel&);
};

template <typename T>
T ModbusChannel::get(int address)
{
    T return_value;
    this->read(address, sizeof(T), (unsigned char*)(&return_value));
    return return_value;
}


#endif
+208 −0
Original line number Diff line number Diff line
#*******************************************************************************
# PPPPPPPP
#
# "@(#) $Id$"
#
# Makefile of ........
#
# who       when      what
# --------  --------  ----------------------------------------------
# mbartolini  03/03/15  created
#

#*******************************************************************************
# This Makefile follows VLT Standards (see Makefile(5) for more).
#*******************************************************************************
# REMARKS
#    None
#------------------------------------------------------------------------

#
# user definable C-compilation flags
#USER_CFLAGS = 

#
# additional include and library search paths
#USER_INC = 
#USER_LIB = 

#
# MODULE CODE DESCRIPTION:
# ------------------------
# As a general rule:  public file are "cleaned" and "installed"  
#                     local (_L) are not "installed".

#
# C programs (public and local)
# -----------------------------
EXECUTABLES     =
EXECUTABLES_L   = 

#
# <brief description of xxxxx program>
xxxxx_OBJECTS   =	
xxxxx_LDFLAGS   =
xxxxx_LIBS      =

#
# special compilation flags for single c sources
#yyyyy_CFLAGS   = 

#
# Includes (.h) files (public only)
# ---------------------------------
INCLUDES        = ModbusChannel.h

#
# Libraries (public and local)
# ----------------------------
LIBRARIES       = ModbusChannel
LIBRARIES_L     =

#
# <brief description of lllll library>
ModbusChannel_OBJECTS   = ModbusChannel
ModbusChannel_LIBS      = modbus boost_thread

#
# Scripts (public and local)
# ----------------------------
SCRIPTS         =
SCRIPTS_L       =

#
# TCL scripts (public and local)
# ------------------------------
TCL_SCRIPTS     =
TCL_SCRIPTS_L   =

#
# Python stuff (public and local)
# ----------------------------
PY_SCRIPTS         =
PY_SCRIPTS_L       =

PY_MODULES         =
PY_MODULES_L       =

PY_PACKAGES        =
PY_PACKAGES_L      =
pppppp_MODULES	   =

#
# <brief description of tttttt tcl-script>
tttttt_OBJECTS  =
tttttt_TCLSH    = 
tttttt_LIBS     = 

#
# TCL libraries (public and local)
# ------------------------------
TCL_LIBRARIES   =
TCL_LIBRARIES_L =

#
# <brief description of tttlll library>
tttlll_OBJECTS  = 

#
# Configuration Database Files
# ----------------------------
CDB_SCHEMAS = 

# 
# IDL Files and flags
# 
IDL_FILES =
TAO_IDLFLAGS =
USER_IDL =
#
# Jarfiles and their directories
#
JARFILES= 
jjj_DIRS=
jjj_EXTRAS= 
#
# java sources in Jarfile on/off
DEBUG= 
#
# ACS XmlIdl generation on/off
#
XML_IDL= 
#
# Java Component Helper Classes generation on/off
#
COMPONENT_HELPERS=
#
# Java Entity Classes generation on/off
#
XSDBIND=
#
# Schema Config files for the above
#
XSDBIND_INCLUDE=
# man pages to be done
# --------------------
MANSECTIONS =
MAN1 =
MAN3 =
MAN5 =
MAN7 =
MAN8 =

#
# local man pages
# ---------------
MANl =

#
# ASCII file to be converted into Framemaker-MIF
# --------------------
ASCII_TO_MIF = 

#
# other files to be installed
#----------------------------
INSTALL_FILES =

#
# list of all possible C-sources (used to create automatic dependencies)
# ------------------------------
CSOURCENAMES = \
	$(foreach exe, $(EXECUTABLES) $(EXECUTABLES_L), $($(exe)_OBJECTS)) \
	$(foreach rtos, $(RTAI_MODULES) , $($(rtos)_OBJECTS)) \
	$(foreach lib, $(LIBRARIES) $(LIBRARIES_L), $($(lib)_OBJECTS))

#
#>>>>> END OF standard rules

#
# INCLUDE STANDARDS
# -----------------

MAKEDIRTMP := $(shell searchFile include/acsMakefile)
ifneq ($(MAKEDIRTMP),\#error\#)
   MAKEDIR := $(MAKEDIRTMP)/include
   include $(MAKEDIR)/acsMakefile
endif

#
# TARGETS
# -------
all:	do_all
	@echo " . . . 'all' done" 

clean : clean_all 
	@echo " . . . clean done"

clean_dist : clean_all clean_dist_all 
	@echo " . . . clean_dist done"

man   : do_man 
	@echo " . . . man page(s) done"

install : install_all
	@echo " . . . installation done"


#___oOo___
+113 −0
Original line number Diff line number Diff line
#include "ModbusChannel.h"

ModbusChannel_sp
get_modbus_channel(const char *server_ip, int server_port)
{
    ModbusChannel_sp _modbus_channel(new ModbusChannel(server_ip, server_port));
    _modbus_channel->connect();
    return _modbus_channel;
};

void
modbus_check_error(int rc)
{
    if( rc == -1)
    {
        if(errno == ETIMEDOUT){
            throw ModbusTimeout(modbus_strerror(errno));
        }else{
            throw ModbusError(modbus_strerror(errno));
        }
    }
};

ModbusChannel::ModbusChannel(const char* server_ip, int server_port) :
    _server_ip(std::string(server_ip)),
    _server_port(server_port),
    _is_connected(false),
    _modbus_context(NULL)
{
    int rc;
    this->_modbus_context = modbus_new_tcp(this->_server_ip.c_str(), this->_server_port);
    if(this->_modbus_context == NULL)
    {
        throw ModbusError(modbus_strerror(errno));
    }
    rc = modbus_set_error_recovery(this->_modbus_context,
                                   (modbus_error_recovery_mode)
                                   (MODBUS_ERROR_RECOVERY_PROTOCOL |
                                   MODBUS_ERROR_RECOVERY_LINK) );
    modbus_check_error(rc);
    timeval _response_timeout;
    _response_timeout.tv_sec = MODBUS_TIMEOUT;
    _response_timeout.tv_usec = 0;
    modbus_set_response_timeout(this->_modbus_context, &_response_timeout);
};

ModbusChannel::~ModbusChannel()
{
    this->disconnect();
    modbus_free(this->_modbus_context);
};

void
ModbusChannel::connect()
{
    boost::mutex::scoped_lock lock(_connection_guard);
    int rc;
    if(!(this->_is_connected))
    {
        rc = modbus_connect(this->_modbus_context);
        modbus_check_error(rc);
        this->_is_connected = true;
    }
};

void
ModbusChannel::disconnect()
{
    boost::mutex::scoped_lock lock(_connection_guard);
    if(this->_is_connected){
        modbus_close(this->_modbus_context);
        this->_is_connected = false;
    }
};

void 
ModbusChannel::read(int address, int size, unsigned char *dest)
{
    int rc;
    if(!(size % 2 == 0))
    {
        throw ModbusError("data transfer size must be even");
    }
    int _size = size / 2;
    if(!(address % 2 == 0))
    {
        throw ModbusError("destination address must be even");
    }
    int _address = address / 2;
    rc = modbus_read_registers(this->_modbus_context, _address, _size,
                                (uint16_t *)dest);
    modbus_check_error(rc);
};

void 
ModbusChannel::write(int address, int size, const unsigned char *source_buffer)
{
    int rc;
    if(!(size % 2 == 0))
    {
        throw ModbusError("data transfer size must be even");
    }
    int _size = size / 2;
    if(!(address % 2 == 0))
    {
        throw ModbusError("destination address must be even");
    }
    int _address = address / 2;
    rc = modbus_write_registers(this->_modbus_context, _address, _size,
                                (uint16_t *)source_buffer);
    modbus_check_error(rc);
};