Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "DeviceSimulatorThread.h"
#include "IDeviceFactory.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------

//   Important: Methods and properties of objects in VCL can only be
//   used in a method called using Synchronize, for example:
//
//      Synchronize(UpdateCaption);
//
//   where UpdateCaption could look like:
//
//      void __fastcall DeviceSimulatorThread::UpdateCaption()
//      {
//        Form1->Caption = "Updated in a thread";
//      }
//---------------------------------------------------------------------------

using namespace std;

__fastcall DeviceSimulatorThread::DeviceSimulatorThread( IDevice * device,
                                                         IDeviceFactory * factory,
                                                         const AnsiString & hostname ) :
    TThread( false ),
    m_device( device ),
    m_factory( factory ),
    m_address( hostname ),
    m_running( false ),
    m_error( "" )
{
    m_device->registerListener( this );

    SECURITY_ATTRIBUTES attribs = { 0 };
    attribs.nLength = sizeof( attribs );
    attribs.bInheritHandle = false;

    m_semaphore = CreateSemaphore( &attribs, 0, 10000, "DeviceSimulatorSemaphore" );
}
//---------------------------------------------------------------------------

void DeviceSimulatorThread::SetName()
{
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = "DeviceSimulatorThread";
    info.dwThreadID = -1;
    info.dwFlags = 0;

    __try
    {
         RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD),(DWORD*)&info );
    }
    __except (EXCEPTION_CONTINUE_EXECUTION)
    {
    }
}

//---------------------------------------------------------------------------
void __fastcall DeviceSimulatorThread::Execute()
{
    SetName();
    m_running = true;

    try
    {
        // Connect the device

        if( !m_device->connect( m_address.c_str() ) )
        {
            m_error = "Device failed to connect to Site Computer at " + m_address + ":5003";
        }

        // Loop until exit, or an error has occured

        while( m_running && m_error.IsEmpty() )
        {
            // Wait for:
            //  1. A disconnection/connection event to reconnect or update our status
            //  2. A request to upload a UD file from our manager
            //  3. A termination request

            if( WaitForSingleObject( m_semaphore, INFINITE ) == WAIT_OBJECT_0 )
            {
                // Check connection state

                if( !m_device->isConnected() )
                {
                    Reconnect();
                }

                // Perform any transfers required

                TransferUdFiles();
                ReleaseSemaphore( m_semaphore, 1, NULL );
            }
        }
    }
    __finally
    {
        // Terminate and cleanup. Destroy the device using the factory or
        // exceptions will be raised.

        m_device->detachListener( this );
        m_device->disconnect();
        m_running = false;

        m_factory->destroyDevice( *m_device );
        m_device = 0;

        CloseHandle( m_semaphore );
    }
}
//---------------------------------------------------------------------------

void DeviceSimulatorThread::Reconnect()
{
    //TODO: configurable?
    const int retries = 5;

    if( !m_device->isConnected() )
    {
        bool connected = false;
        int connectCount = retries;

        while( (connectCount >= 0) && !connected )
        {
            m_device->connect( m_address.c_str() );
            connectCount--;

            int checkCount = retries;
            while( (checkCount >= 0) && !(connected = m_device->isConnected()) )
            {
                checkCount--;
                Sleep( 500 );
            }
        }

        // Terminate if we couldn't get a connection

        if( !connected )
        {
            m_error.sprintf( "Failed to connect to site computer %s", m_address );
        }
    }
}
//---------------------------------------------------------------------------

bool DeviceSimulatorThread::TransferUdFiles()
{
    bool success = true;

    // TODO: configurable?
    const int retries = 5;

    vector<AnsiString>::iterator file = m_udFiles.begin();
    if( file != m_udFiles.end() )
    {
        // Retry a transfer if we fail.. check connection in between
        // incase it has dropped

        success = m_device->sendUdFile( (*file).c_str(), 0, true, false );

        if( success )
        {
            m_udFiles.erase( file );
        }
        else if( m_running )
        {
            m_error.sprintf( "Failed to transfer UD file %s", (*file) );
        }
    }

    return success;
}
//---------------------------------------------------------------------------

void __fastcall DeviceSimulatorThread::UploadUDFile( const AnsiString & filename )
{
    if( m_running )
    {
        m_udFiles.push_back( filename );
        ReleaseSemaphore( m_semaphore, 1, NULL );
    }
}
//---------------------------------------------------------------------------

void __fastcall DeviceSimulatorThread::Stop()
{
    // Will stop the thread after the UD queue has finished transferring
    
    m_running = false;
    ReleaseSemaphore( m_semaphore, 1, NULL );
}
//---------------------------------------------------------------------------

void DS_STDCALL DeviceSimulatorThread::onEvent( const unsigned short type,
                                                const char * msg,
                                                const unsigned char status )
{
    switch( type )
    {
    case EVENT_UDXFER:
        break;

    case EVENT_DISCON:
        // Simply wake the main thread; it will handle a reconnection
        ReleaseSemaphore( m_semaphore, 1, NULL );
        break;
    }
}
//---------------------------------------------------------------------------

void DS_STDCALL DeviceSimulatorThread::detach()
{
    // TODO: This is fatal as we rely on event callbacks for processing
    m_device->detachListener( this );
}