//--------------------------------------------------------------------------- #include #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::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 ); }