// ************************************************************************** // // (C) Copyright 2009 Swiss SIMTEC AG // // ************************************************************************** // // $HeadURL$ // $LastChangedBy$ // $LastChangedDate$ // $LastChangedRevision$ // // Implementation of the Class PrtCnvSocketThread // ************************************************************************** #include "PrtCnvSocketThread.hpp" #include "PrtCnvDbThread.hpp" #include "PrtCnvTelegramLogThread.hpp" #include "PrtCnvThreadStarter.hpp" #include "PrtCnvInjectionThread.hpp" #include #include namespace PrtCnv { // --------------------------------------------------------------- // Constructor with pointer to thread-starter as parameter // --------------------------------------------------------------- PrtCnvSocketThread::PrtCnvSocketThread(PrtCnv::PrtCnvThreadStarter* a_parent, const char *loggerName) : PrtCnvThread(a_parent, loggerName) , m_ConnectionTimer(nullptr) , m_Socket(nullptr) , m_TickTimer(nullptr) , m_ReceiveCounter(0) , m_ReceiveAckCounter(0) , m_State(PrtCnvSocketThread::e_INITIAL) , m_DbThread(nullptr) , m_ConnectTimeout(2000) , m_DisconnectTimeout(10000) , m_AliveTimeout(3000) , m_TickTimeout(900) , m_DoCleanup(false) , m_LogThread(nullptr) , m_InjectionThread(nullptr) , m_currentHostAdressIndex(0) {} PrtCnvSocketThread::~PrtCnvSocketThread() { cleanup(); } // --------------------------------------------------------------- // Creates all subcomponents and connects signals to slots // --------------------------------------------------------------- int32_t PrtCnvSocketThread::create(NGL::Settings *settings) { PrtCnvThread::create(settings); createNewSocket(); m_ConnectionTimer = new QTimer(this); connect(m_ConnectionTimer, &QTimer::timeout, this, &PrtCnvSocketThread::timeoutAliveAndConnection); m_TickTimer = new QTimer(this); connect(m_TickTimer, &QTimer::timeout, this, &PrtCnvSocketThread::timeoutTick); m_DbThread = qobject_cast(m_Parent->getOtherThread(PrtCnvApplication::e_Db)); m_LogThread = qobject_cast(m_Parent->getOtherThread(PrtCnvApplication::e_Log)); m_InjectionThread = qobject_cast(m_Parent->getOtherThread(PrtCnvApplication::e_Injection)); return 0; } // --------------------------------------------------------------- // Creates a new socket to avoid getting old stuff from the // former socket when reconnecting // --------------------------------------------------------------- void PrtCnvSocketThread::createNewSocket() { INFO_LOG(m_logger) << "createNewSocket m_oldSocket=" << m_Socket; Q_ASSERT(QThread::currentThreadId() == m_currentThreadId); if (m_Socket) { m_Socket->disconnectFromHost(); // Falls noch connected m_Socket->disconnect(); // Alle Signals/Slots aufräumen if (m_Socket->state() != QAbstractSocket::UnconnectedState) m_Socket->waitForDisconnected(); m_Socket->deleteLater(); // und weg damit } m_Socket = new QTcpSocket(this); qRegisterMetaType("QAbstractSocket::SocketError"); qRegisterMetaType("QAbstractSocket::SocketState"); connect(m_Socket, &QTcpSocket::connected, this, &PrtCnvSocketThread::connected, Qt::QueuedConnection); connect(m_Socket, &QTcpSocket::disconnected, this, &PrtCnvSocketThread::disconnected, Qt::QueuedConnection); connect(m_Socket, QOverload::of(&QTcpSocket::error), this, &PrtCnvSocketThread::error, Qt::QueuedConnection); connect(m_Socket, &QTcpSocket::hostFound, this, &PrtCnvSocketThread::hostFound, Qt::QueuedConnection); connect(m_Socket, &QTcpSocket::stateChanged, this, &PrtCnvSocketThread::stateChanged); connect(m_Socket, &QTcpSocket::readyRead, this, &PrtCnvSocketThread::readyRead, Qt::QueuedConnection); } // --------------------------------------------------------------- // Deallocates subcomponents. // --------------------------------------------------------------- int32_t PrtCnvSocketThread::destroy() { PrtCnvThread::destroy(); cleanup(); return 0; } void PrtCnvSocketThread::cleanup() { delete m_Socket; m_Socket = nullptr; delete m_ConnectionTimer; m_ConnectionTimer = nullptr; delete m_TickTimer; m_TickTimer = nullptr; } // --------------------------------------------------------------- // Initializes the variables. // --------------------------------------------------------------- int32_t PrtCnvSocketThread::init() { INFO_LOG(m_logger) << "PrtCnvSocketThread::init()"; PrtCnvThread::init(); m_State = e_INITIAL; m_ConnectTimeout = m_Config->getInteger(getName(), QStringLiteral("ConnectTimeout"), 2000); m_DisconnectTimeout = m_Config->getInteger(getName(), QStringLiteral("DisconnectTimeout"), 10000); m_AliveTimeout = m_Config->getInteger(getName(), QStringLiteral("AliveTimeout"), 3000); return 0; } // --------------------------------------------------------------- // Connect as client to a TCP-server on ip-address configured // as HostName and Port configured as Port // --------------------------------------------------------------- void PrtCnvSocketThread::connectSocket() { int sockState = m_Socket ? m_Socket->state() : -1; INFO_LOG(m_logger) << "connectSocket m_Socket=" << m_Socket << " state=" << m_State << " socketState=" << sockState; if (e_WORKING != m_State && e_REGISTERING != m_State) { if (m_Socket) { m_Socket->disconnectFromHost(); // Falls noch connected if (m_Socket->state() != QAbstractSocket::UnconnectedState) m_Socket->waitForDisconnected(); } createNewSocket(); if(m_hostAdresses.isEmpty()) readHostAdresses(); const QString hostname = m_hostAdresses.at(m_currentHostAdressIndex).first; const uint16_t port = m_hostAdresses.at(m_currentHostAdressIndex).second; m_currentHostAdressIndex = ((m_currentHostAdressIndex + 1) % m_hostAdresses.size()); INFO_LOG(m_logger) << "Connecting to " << hostname << ":" << port << " m_Socket=" << m_Socket; m_Socket->connectToHost(hostname, port); m_State = e_CONNECTING; startConnectionTimer(); } else { WARNING_LOG(m_logger) << getName() << ": connectSocket called, but m_State is " << m_State; } } // --------------------------------------------------------------- // Disconnects from TCP-server // --------------------------------------------------------------- void PrtCnvSocketThread::disconnectSocket() { m_Socket->disconnectFromHost(); startDisconnectionTimer(); m_State = e_DISCONNECTING; } // --------------------------------------------------------------- // Will be called from Qt-Framework when we have connected to // TCP-server // --------------------------------------------------------------- void PrtCnvSocketThread::connected() { INFO_LOG(m_logger) << "connected (implement connected() in inherited class, if you want use this signal...)"; } // --------------------------------------------------------------- // Will be called from Qt-Framework when we have disconnected // to TCP-server // --------------------------------------------------------------- void PrtCnvSocketThread::disconnected() { INFO_LOG(m_logger) << "got signal 'disconnected' (implement disconnected() in inherited class, if you want use this signal...)"; } // --------------------------------------------------------------- // Will be called from Qt-Framework when we have have read // data over socket // --------------------------------------------------------------- void PrtCnvSocketThread::readyRead() { INFO_LOG(m_logger) << "got signal 'newData' (implement newData() in inherited class, if you want use this signal...)"; } // --------------------------------------------------------------- // this slot is used for connection, disconnection and alive // timeouts // --------------------------------------------------------------- void PrtCnvSocketThread::timeoutAliveAndConnection() { INFO_LOG(m_logger) << "got signal 'reconnectTimeout' (implement reconnectTimeout() in inherited class, if you want use this signal...)"; } // --------------------------------------------------------------- // Will be called from Qt-Framework when we have a socket-error // --------------------------------------------------------------- void PrtCnvSocketThread::error(QAbstractSocket::SocketError a_socketError) { switch (a_socketError) { case QAbstractSocket::ConnectionRefusedError: ERROR_LOG(m_logger) << getName() << ": ConnectionRefusedError m_State=" << m_State; break; case QAbstractSocket::RemoteHostClosedError: ERROR_LOG(m_logger) << getName() << ": RemoteHostClosedError"; break; case QAbstractSocket::HostNotFoundError: //ERROR_LOG(m_logger) << getName() << ": HostNotFoundError"; break; default: ERROR_LOG(m_logger) << getName() << ": got signal 'error(QAbstractSocket::SocketError=" << a_socketError << ")'"; } } // --------------------------------------------------------------- // Will be called from Qt-Framework when the host was found // --------------------------------------------------------------- void PrtCnvSocketThread::hostFound() { // INFO_LOG(m_logger) << "got signal 'hostFound' (implement hostFound() in inherited class, if you want use this signal...)"; } // --------------------------------------------------------------- // Will be called from Qt-Framework when socket-state has // changed // --------------------------------------------------------------- void PrtCnvSocketThread::stateChanged(QAbstractSocket::SocketState a_socketState) { Q_UNUSED(a_socketState); // INFO_LOG(m_logger) << "got signal 'stateChanged(" << ao_socketState << ")' (implement stateChanged() in inherited class, if you want use this signal...)"; } // --------------------------------------------------------------- // calls storeRadioTelegram of DbThread, see there // --------------------------------------------------------------- void PrtCnvSocketThread::storeRadioTelegram(const std::shared_ptr &a_telegram, const uint32_t a_readCounter) { m_DbThread->storeRadioTelegram(a_telegram, a_readCounter); } // --------------------------------------------------------------- // used to check whether the other side is still alive // --------------------------------------------------------------- void PrtCnvSocketThread::startAliveTimer() { m_ConnectionTimer->setInterval(m_AliveTimeout); m_ConnectionTimer->start(); } // --------------------------------------------------------------- // used to retry connection // --------------------------------------------------------------- void PrtCnvSocketThread::startConnectionTimer() { m_ConnectionTimer->setInterval(m_ConnectTimeout); m_ConnectionTimer->start(); } // --------------------------------------------------------------- // used to abort connection, when normal disconnection is not // successful // --------------------------------------------------------------- void PrtCnvSocketThread::startDisconnectionTimer() { m_ConnectionTimer->setInterval(m_DisconnectTimeout); m_ConnectionTimer->start(); } // --------------------------------------------------------------- // used for periodically ticks (i.e. for acks or sending // nachforderungen) // --------------------------------------------------------------- void PrtCnvSocketThread::startTickTimer() { // INFO_LOG(m_logger) << "start timer"; m_TickTimer->setInterval(m_TickTimeout); m_TickTimer->start(); } // --------------------------------------------------------------- // The counter will be set to a_startCounter if a_maxCounter // is reached // returns true if receive-counter is ok // returns false if a telegram is repeated or lost // --------------------------------------------------------------- bool PrtCnvSocketThread::checkAndUpdateReceiveCounter(const uint32_t a_counter, const uint32_t a_maxCounter, const uint32_t a_startCounter) { TRACE_LOG(m_logger) << "checkAndUpdateReceiveCounter: " << a_counter << ", " << m_ReceiveCounter; // check for wrap if (a_maxCounter == ++m_ReceiveCounter) { m_ReceiveCounter = a_startCounter; } // has the counter the expected value? if (m_ReceiveCounter != a_counter) { //if (m_ReceiveCounter != 1) { if (m_ReceiveCounter > a_counter) { ERROR_LOG(m_logger) << "It seems, the telegram with counter " << a_counter << " was resent from " << getName() << "! Expected: " << m_ReceiveCounter; } else { ERROR_LOG(m_logger) << "It seems, we lost one or several telegrams from " << getName() << ": receive-counter of received telegram: " << a_counter << "; Expected: " << m_ReceiveCounter; } } #if 0 m_ReceiveCounter--; return false; // failure #else m_ReceiveCounter = a_counter; return true; #endif } return true; // all ok } void PrtCnvSocketThread::readHostAdresses() { QString host = m_Config->getString(getName(), QStringLiteral("HostName"), QStringLiteral("127.0.0.1")); uint16_t port = m_Config->getInteger(getName(), QStringLiteral("Port"), 4934); m_hostAdresses.push_back(qMakePair(host, port)); for (int i = 1; i < 10; ++i) { const QString hostName = QLatin1String("HostName") + QString::number(i); const QString portName = QLatin1String("Port") + QString::number(i); host = m_Config->getString(getName(), hostName, QString()); port = m_Config->getInteger(getName(), portName, 0); if (host.isEmpty() || port == 0) continue; m_hostAdresses.push_back(qMakePair(host, port)); } for (const auto &p : qAsConst(m_hostAdresses)) { INFO_LOG(m_logger) << "Read Host Adress " << p.first << ":" << p.second << " from settings"; } } } // namespace PrtCnv