blob: 8e6cffd1a452f07747f266c75059dfb5ceca0909 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 -2014 AIT
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Filip Andren - initial API and implementation and/or initial documentation
*******************************************************************************/
#include "modbusclientconnection.h"
#include "devlog.h"
#include "modbuspoll.h"
#include <forte_thread.h>
using namespace modbus_connection_event;
/*************************************
* CModbusClientConnection class
*************************************/
CModbusClientConnection::CModbusClientConnection(CModbusHandler* pa_modbusHandler) :
CModbusConnection(pa_modbusHandler), m_pModbusConnEvent(NULL), m_nNrOfPolls(0), m_nSlaveId(0xFF), m_unBufFillSize(0){
memset(m_anRecvBuffPosition, 0, sizeof(m_anRecvBuffPosition)); //TODO change this to m_anRecvBuffPosition{0} in the extended list when fully switching to C++11
memset(m_acRecvBuffer, 0, sizeof(m_acRecvBuffer)); //TODO change this to m_acRecvBuffer{0} in the extended list when fully switching to C++11
}
CModbusClientConnection::~CModbusClientConnection(){
if (m_bConnected){
disconnect();
}
TModbusPollList::Iterator itEnd(m_lstPollList.end());
for(TModbusPollList::Iterator itRunner = m_lstPollList.begin(); itRunner != itEnd; ++itRunner){
delete *itRunner;
}
if (m_pModbusConnEvent != NULL){
delete m_pModbusConnEvent;
}
}
int CModbusClientConnection::readData(uint8_t *pa_pData){
for(unsigned int i = 0; i < m_unBufFillSize; i++){
pa_pData[i] = m_acRecvBuffer[i];
}
return (int) m_unBufFillSize;
}
int CModbusClientConnection::writeData(uint16_t *pa_pData, unsigned int pa_nDataSize){
unsigned int dataIndex = 0;
TModbusSendList::Iterator itEnd = m_lstSendList.end();
for (TModbusSendList::Iterator it = m_lstSendList.begin(); it!=itEnd; ++it) {
if (dataIndex + it->m_nNrAddresses > pa_nDataSize) {
break;
}
modbus_write_registers(m_pModbusConn, it->m_nStartAddress, it->m_nNrAddresses, &pa_pData[dataIndex]);
dataIndex += it->m_nNrAddresses;
}
return (int)dataIndex;
}
int CModbusClientConnection::connect(){
CModbusConnection::connect();
if(m_nSlaveId != 0xFF){
modbus_set_slave(m_pModbusConn, m_nSlaveId);
}
m_pModbusConnEvent = new CModbusConnectionEvent(1000);
m_pModbusConnEvent->activate();
this->start();
return 0;
}
void CModbusClientConnection::disconnect(){
this->end();
if (m_bConnected){
modbus_close(m_pModbusConn);
m_bConnected = false;
}
CModbusConnection::disconnect();
}
void CModbusClientConnection::addNewPoll(TForteUInt32 pa_nPollInterval, unsigned int pa_nFunctionCode, unsigned int pa_nStartAddress, unsigned int pa_nNrAddresses){
CModbusPoll *newPoll = NULL;
TModbusPollList::Iterator itEnd = m_lstPollList.end();
for(TModbusPollList::Iterator it = m_lstPollList.begin(); it != itEnd; ++it){
if(it->getUpdateInterval() == pa_nPollInterval && it->getFunctionCode() == pa_nFunctionCode){
it->addPollAddresses(pa_nStartAddress, pa_nNrAddresses);
newPoll = *it;
break;
}
}
if(newPoll == NULL){
m_lstPollList.pushBack(new CModbusPoll(pa_nPollInterval, pa_nFunctionCode, pa_nStartAddress, pa_nNrAddresses));
m_nNrOfPolls++;
m_anRecvBuffPosition[m_nNrOfPolls - 1] = m_unBufFillSize;
}
// Count bytes
unsigned int nrBytes = 0;
switch (pa_nFunctionCode){
case 1:
case 2:
nrBytes = pa_nNrAddresses;
break;
case 3:
case 4:
nrBytes = pa_nNrAddresses * 2;
break;
}
for(unsigned int i = m_unBufFillSize; i < m_unBufFillSize + nrBytes; i++){
m_acRecvBuffer[i] = 0;
}
m_unBufFillSize += nrBytes;
}
void CModbusClientConnection::addNewSend(unsigned int pa_nStartAddress, unsigned int pa_nNrAddresses) {
SSendInformation sendInfo = {pa_nStartAddress, pa_nNrAddresses};
m_lstSendList.pushBack(sendInfo);
}
void CModbusClientConnection::setSlaveId(unsigned int pa_nSlaveId){
m_nSlaveId = pa_nSlaveId;
}
void CModbusClientConnection::run(){
while(isAlive()){
if(m_bConnected){
tryPolling();
}
else{
tryConnect();
}
CThread::sleepThread(1);
}
}
void CModbusClientConnection::tryPolling(){
unsigned int nrErrors = 0;
bool dataReturned = false;
unsigned int index = 0;
TModbusPollList::Iterator itEnd(m_lstPollList.end());
for(TModbusPollList::Iterator itPoll = m_lstPollList.begin(); itPoll != itEnd; ++itPoll, ++index){
if(itPoll->readyToExecute()){
int nrVals = itPoll->executeEvent(m_pModbusConn, (void*) &m_acRecvBuffer[m_anRecvBuffPosition[index]]); // retVal);
if(nrVals < 0){
DEVLOG_ERROR("Error reading input status :: %s\n", modbus_strerror(errno));
itPoll->deactivate();
nrErrors++;
}
else if(nrVals > 0){
dataReturned = true;
}
}
}
if(dataReturned) {
m_pModbusHandler->executeComCallback(m_nComCallbackId);
}
if((nrErrors == m_nNrOfPolls) && (0 != m_nNrOfPolls)){
modbus_close(m_pModbusConn); // in any case it is worth trying to close the socket
m_bConnected = false;
m_pModbusConnEvent = new CModbusConnectionEvent(1000);
m_pModbusConnEvent->activate();
}
}
void CModbusClientConnection::tryConnect(){
if(m_pModbusConnEvent != NULL){
if(m_pModbusConnEvent->readyToExecute()){
if(m_pModbusConnEvent->executeEvent(m_pModbusConn, NULL) < 0) {
DEVLOG_ERROR("Connection to Modbus server failed: %s\n", modbus_strerror(errno));
} else {
DEVLOG_INFO("Connection to Modbus server succeded\n");
delete m_pModbusConnEvent;
m_pModbusConnEvent = NULL;
m_bConnected = true;
// Start polling
TModbusPollList::Iterator itEnd(m_lstPollList.end());
for(TModbusPollList::Iterator itPoll = m_lstPollList.begin(); itPoll != itEnd; ++itPoll){
itPoll->activate();
}
}
}
}
}
/*************************************
* CModbusConnectionEvent class
*************************************/
CModbusConnectionEvent::CModbusConnectionEvent(long pa_nReconnectInterval) :
CModbusTimedEvent(pa_nReconnectInterval){
}
int CModbusConnectionEvent::executeEvent(modbus_t *pa_pModbusConn, void *pa_pRetVal){
(void)pa_pRetVal; // avoid warning
restartTimer();
return modbus_connect(pa_pModbusConn);
}