#include "ConnectionConfig.h" #include "ModbusAdapter.h" #include /// Added for memset functionality ModbusAdapter::ModbusAdapter() : modbus( nullptr ) { this->InitBuffers(); } ModbusAdapter::~ModbusAdapter() { } bool ModbusAdapter::ModbusConnect( const ConnectionConfig &config ) { if( connected ) { this->ModbusDisconnect(); // Will already set m_connected } connectionType = config.getType(); switch( connectionType ) { case ConnectionType::CT_SERIAL: connected = this->ModbusConnectRTU( config.getPort(), config.getBaudRate(), config.getParity(), config.getDataBits(), config.getStopBits(), config.getTimeOut(), MODBUS_RTU_RTS_NONE ); break; case ConnectionType::CT_TCP: connected = this->ModbusConnectTCP( config.getIpAddress(), config.getTcpPort(), 10 ); break; default: // throw a sensible message or return an errorcode. break; } return connected; } bool ModbusAdapter::ModbusDisconnect() { if( modbus != nullptr ) { if( connected ) { modbus_close( modbus ); modbus_free( modbus ); } // Clean up after ourselves. modbus = nullptr; } connected = false; return true; } modbusData ModbusAdapter::ModbusReadData( int slaveId, int functionCode, int startAddress, int noOfItems ) { if( modbus == nullptr ) { // No context return modbusData(); } int resultValue = -1; bool is16Bit = false; modbus_set_slave( modbus, slaveId ); // Request data from modbus. switch( functionCode ) { case MODBUS_FC_READ_COILS: resultValue = modbus_read_bits( modbus, startAddress, noOfItems, m_dest ); break; case MODBUS_FC_READ_DISCRETE_INPUTS: resultValue = modbus_read_input_bits( modbus, startAddress, noOfItems, m_dest ); break; case MODBUS_FC_READ_HOLDING_REGISTERS: resultValue = modbus_read_registers( modbus, startAddress, noOfItems, m_dest16 ); break; case MODBUS_FC_READ_INPUT_REGISTERS: resultValue = modbus_read_input_registers( modbus, startAddress, noOfItems, m_dest16 ); break; default: break; } // Read the databuffers if( resultValue != noOfItems ) { return modbusData(); } modbusData resultData; for( int index = 0; index < noOfItems; ++index ) { resultData.push_back( (is16Bit ? static_cast(m_dest16[index]) : static_cast(m_dest[index])) ); } modbus_flush( modbus ); // flush data this->ClearBuffers(); return resultData; } modbusData ModbusAdapter::ModbusReadHoldReg( int slaveId, int startAddress, int noOfItems ) { if( modbus == nullptr ) { return modbusData(); } int resultValue = -1; /// Return value from read functions // -- Start Critical Section --- ?? // modbus_set_slave( modbus, slaveId ); /// Request data from modbus resultValue = modbus_read_registers( modbus, startAddress, noOfItems, m_dest16 ); /// Read the databuffers if( resultValue != noOfItems ) { return modbusData(); } modbusData resultData; for( int index = 0; index < noOfItems; ++index ) { resultData.push_back( static_cast(m_dest16[index]) ); } modbus_flush( modbus ); this->ClearBuffers(); // -- End Critical Section --- ?? // return resultData; } void ModbusAdapter::ModBusWriteData( int slaveId, int functionCode, int startAddress, int noOfItems, std::vectorvalues ) { if( modbus == nullptr ) { // No modbus context. Sensible log-line and exit. return; } // ------------------------------------------------ // Create 8 bits databuffer auto * data8 = new uint8_t[noOfItems]; for( int index = 0; index < noOfItems; ++index ) { data8[index] = values[index]; } // Create same buffer for 16 bits data auto * data16 = new uint8_t[noOfItems]; for( int index = 0; index < noOfItems; ++index ) { data16[index] = values[index]; } // ------------------------------------------------ int resultValue = -1; modbus_set_slave( modbus, slaveId ); // Request data from modbus switch( functionCode ) { case MODBUS_FC_WRITE_SINGLE_COIL: resultValue = modbus_write_bit( modbus, startAddress, values[0] ); noOfItems = 1; break; case MODBUS_FC_WRITE_SINGLE_REGISTER: resultValue = modbus_write_register( modbus, startAddress, values[0] ); noOfItems = 1; break; case MODBUS_FC_WRITE_MULTIPLE_COILS: resultValue = modbus_write_bits( modbus, startAddress, noOfItems, data8 ); break; case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: resultValue = modbus_write_bits( modbus, startAddress, noOfItems, data16 ); break; } delete[] data8; delete[] data16; modbus_flush(modbus); // Flush data. if( resultValue != noOfItems ) { // Create a log line that writing the register failed. Maybe increment ErrorCounter(s) } } bool ModbusAdapter::isConnected() const { return connected; } std::string ModbusAdapter::ErrorString( int errnum ) const { switch(errnum) { case EINVAL: return "Protocol context is NULL"; break; case ETIMEDOUT: return "Timeout"; break; case ECONNRESET: return "Connection reset"; break; case ECONNREFUSED: return "Connection refused"; break; case EPIPE: return "Socket error"; break; default: return modbus_strerror(errno); } } /* ============= PRIVATE METHODS ============= */ bool ModbusAdapter::ModbusConnectRTU( const std::string &serialport, int baud, char parity, int dataBits, int stopBits, int RTS, int timeOut ) { modbus = modbus_new_rtu( serialport.c_str(), baud, parity, dataBits, stopBits, RTS ); #ifdef LIB_MODBUS_DEBUG_OUTPUT // Do sensible logging through PRIVA_LOG m_modbus_set_debug( m_modbus, 1 ); #endif if( modbus == nullptr ) { // We can stop here. Nothing to be done as we don't have a valid context // Log to PRIVA_LOG connected = false; return connected; } else if( modbus && modbus_connect( modbus ) == -1 ) { // We could not connect to the selected serial port. // We can stop here. Nothing to be done as we don't have a valid connection modbus_free( modbus ); connected = false; return connected; } connected = true; // Set recovery mode modbus_set_error_recovery( modbus, MODBUS_ERROR_RECOVERY_PROTOCOL ); // Set the response timeout modbus_set_response_timeout( modbus, timeOut, 0 ); return connected; } bool ModbusAdapter::ModbusConnectTCP( const std::string &ip, int port, int timeOut ) { if( ip.empty() ) { // Nothing to be done. Set an Logline to PRIVA_LOG and exit. return false; } modbus = modbus_new_tcp( ip.c_str(), port ); #ifdef LIB_MODBUS_DEBUG_OUTPUT // Do sensible logging through PRIVA_LOG m_modbus_set_debug( m_modbus, 1 ); #endif if( modbus == nullptr ) { // We can stop here. Nothing to be done as we don't have a valid context // Log to PRIVA_LOG return false; } else if( modbus && modbus_connect( modbus ) == -1 ) { // We could not connect to the selected serial port. // We can stop here. Nothing to be done as we don't have a valid connection modbus_free( modbus ); connected = false; return connected; } connected = true; // Set recovery mode modbus_set_error_recovery( modbus, MODBUS_ERROR_RECOVERY_PROTOCOL ); // Set the response timeout modbus_set_response_timeout( modbus, timeOut, 0 ); return connected; } void ModbusAdapter::InitBuffers() { // Setup memory for Data. m_dest = (uint8_t *)malloc(2000 + sizeof(uint8_t)); m_dest16 = (uint16_t *)malloc(125 * sizeof(uint16_t)); this->ClearBuffers(); } void ModbusAdapter::ClearBuffers() { memset(m_dest, 0, 2000 * sizeof(uint8_t)); memset(m_dest16, 0, 125 * sizeof(uint16_t)); }