poolmanager.cpp 8.42 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/
#include "poolmanager.h"
#include "log.h"
#include <QCoreApplication>

namespace osdev  {
namespace components {

PoolManager::PoolManager(bool           bServer,
                         const QString& ipAddress,
                         int            portNumber,
                         int            maxTcpConnections,
                         int            maxUdpConnections,
                         QObject        *_parent )
    : QObject( _parent )
    , m_pServer( nullptr )
    , m_pSocketContainer( new SocketContainer() )
    , m_pSocket( nullptr )
    , m_inputBuffer()
    , m_outputBuffer()
    , m_bServer( bServer )
    , m_ipAddress( ipAddress )
    , m_portNumber( portNumber )
    , m_maxTcpConnections( maxTcpConnections )
    , m_maxUdpConnections( maxUdpConnections )
{
}

PoolManager::~PoolManager()
{
    delete m_pSocketContainer;
    m_pSocketContainer = nullptr;
}
/***************************************************************************
 * Server Section
 ***************************************************************************/
void PoolManager::slotNewConnection()
{
    // Create the socket, pass the pendingconnection and add it to our channelList.

    QTcpSocket* nextConnection = m_pServer->nextPendingConnection();
    if( nextConnection )
    {
        m_pSocket = new TcpSocket( nextConnection );
        m_pSocketContainer->addSocket( m_pSocket );

        // Connect the socket to the poolmanager
        this->connectSocketSignals( m_pSocket );
    }
}

bool PoolManager::startNetworkLayer()
{
    bool    bResult = false;

    if( m_bServer )
    {
        m_pServer = new QTcpServer; // Create our server object

        connect( m_pServer, SIGNAL( newConnection() ),
                 this, SLOT( slotNewConnection() ) );

        m_pServer->setMaxPendingConnections( m_maxTcpConnections );
        bResult = m_pServer->listen( QHostAddress(m_ipAddress), static_cast<quint16>(m_portNumber) );
        if( bResult )
        {
            LogDebug("interfaceplugin", "Server started");
        }
        else
        {
            LogError("interfaceplugin", "Server error : " + m_pServer->errorString());
        }
    }
    else
    {
        // Here we start a couple of client connections.
        for( int nCounter = 0; nCounter < m_maxTcpConnections; nCounter++ )
        {
            m_pSocket = new TcpSocket( m_ipAddress, m_portNumber );
            bResult = m_pSocket->isConnected();
            if( bResult )
            {
                m_pSocketContainer->addSocket( m_pSocket );
                this->connectSocketSignals( m_pSocket );
                emit message( 0, 0, osdev::components::gEventConnectionEstablished, 0 );
            }
            else
            {
                LogDebug("[PoolManager::startNetworkLayer()]", QString( "Socket Error : %1" ).arg( m_pSocket->showError() ) );
                delete m_pSocket;
            }
        }
    }
    return bResult;
}

void PoolManager::slotDataSent( TcpSocket *pSocket )
{
    m_pSocketContainer->setChannelFree( pSocket );

    // Check for delayed messages and send the first one.
    this->resendDelayedMessages();
}

void PoolManager::slotReceivingData( TcpSocket *pSocket )
{
    m_pSocketContainer->setChannelBusy( pSocket );
}

void PoolManager::slotDataReceived( const QString &sData, TcpSocket *pSocket )
{
    // Create a unique ticket
    QString l_sTicket = createBufferTicket();

    // Store the dat with the ticket as key
    m_outputBuffer.insert( l_sTicket, sData );

    // Free the channel.
    m_pSocketContainer->setChannelFree( pSocket );

    // Signal the upper layer we have new data available.
    emit dataPresent( l_sTicket );

    // Resend first delayed messages if there are any.
    this->resendDelayedMessages();
}

void PoolManager::sendData( const QString &sData )
{
    // Get a free channel and signal the data we want to send.
    TcpSocket *pSocket = m_pSocketContainer->getFreeSocket();
    if( pSocket )
    {
        m_pSocketContainer->setChannelBusy( pSocket );
        emit signalSendData( sData, pSocket );
        QCoreApplication::processEvents();
    }
    else
    {
        LogError("NetworkInterface", QString("No free channel available. Storing for delayed send. Message number: %1").arg(m_inputBuffer.size() + 1));
        m_inputBuffer.append( sData );
    }
}

QString PoolManager::createBufferTicket()
{
    return QUuid::createUuid().toString();
}

QString PoolManager::getData(const QString& sTicket )
{
    if( m_outputBuffer.contains( sTicket ) )
    {
        return m_outputBuffer.take( sTicket );
    }
    else
    {
        return QString( "[ERROR : ] No data present in outputbuffer under ticket : %1 " ).arg( sTicket );
    }
}

void PoolManager::connectSocketSignals( TcpSocket* l_pSocket)
{
    // First we connect the socket -> poolmanager
    connect( l_pSocket, SIGNAL( signalDataReceived( const QString&, TcpSocket* ) ),
             this, SLOT( slotDataReceived( const QString&, TcpSocket* ) ) );

    connect( l_pSocket, SIGNAL( signalDataSent( TcpSocket* ) ),
             this, SLOT( slotDataSent( TcpSocket* ) ) );

    connect( l_pSocket, SIGNAL( signalReceivingData( TcpSocket* ) ),
             this, SLOT( slotReceivingData( TcpSocket* ) ) );

    connect( l_pSocket, SIGNAL( signalConnected( TcpSocket* ) ),
             this, SLOT( slotConnected( TcpSocket* ) ) );

    connect( l_pSocket, SIGNAL( signalDisconnected( TcpSocket* ) ),
             this, SLOT( slotDisconnected( TcpSocket* ) ) );

    // And now the poolmanager -> socket
    connect( this, SIGNAL( signalSendData( const QString&, TcpSocket* ) ),
             l_pSocket, SLOT( slotSendData( const QString&, TcpSocket* ) ) );

}

void PoolManager::resendDelayedMessages()
{
    while( m_inputBuffer.size() > 0 ) // Check for delayed messages
    {
        if( m_pSocketContainer->getFreeSocket() )  // Check if there is a free channel
        {
            // Always send the first available message (FIFO)
            this->sendData( m_inputBuffer.takeFirst() );
            LogDebug("[PoolManager::resendDelayedMessages]", QString( "Number of queued messages : %1 ").arg( m_inputBuffer.size() ) );
        }
        else
        {
            // Warn if there is a socket available....
            LogDebug( "[PoolManager::resendDelayedMessages]", QString( "No free Socket Available (yet)" ) );
        }
    }
}

void PoolManager::slotConnected( TcpSocket *pSocket )
{
    Q_UNUSED( pSocket );

    emit message( 0, 0, osdev::components::gEventConnectionEstablished, 0 );
}

void PoolManager::slotDisconnected( TcpSocket *pSocket )
{
    LogError("NetworkInterface", "Socket disconnected unexpectantly.");
    LogError("NetworkInterface", pSocket->showError());

    // emit message( 0, 0, gEventConnectionLost, 0 );

    m_pSocketContainer->removeSocket( pSocket );
    delete pSocket;
}

}   /* End namespace components */
}   /* End namespace osdev  */