/*
* Remote Laboratory FPGA Programming Part
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* ( c ) 2012 Timothy Pearson
* Raptor Engineering
* http : //www.raptorengineeringinc.com
*/
# include "define.h"
# include "part.h"
# include <kaboutdata.h> //::createAboutData()
# include <kaction.h>
# include <klocale.h>
# include <kmessagebox.h> //::start()
# include <kparts/genericfactory.h>
# include <kstatusbar.h>
# include <kstdaction.h>
# include <knuminput.h>
# include <kmdcodec.h>
# include <kurlrequester.h>
# include <tqfile.h> //encodeName()
# include <tqtimer.h> //postInit() hack
# include <tqvbox.h>
# include <tqsocket.h>
# include <tqmutex.h>
# include <tqeventloop.h>
# include <tqapplication.h>
# include <tqgroupbox.h>
# include <tqcheckbox.h>
# include <tqpushbutton.h>
# include <tqprogressbar.h>
# include <unistd.h> //access()
# include <stdint.h>
# include <tqpainter.h>
# include "tracewidget.h"
# include "floatspinbox.h"
# include "layout.h"
// RAJA UNCOMMENT ME
//#define SERVER_TIMEOUT_MS 10000
// RAJA DEBUG ONLY
# define SERVER_TIMEOUT_MS 100000
# define NETWORK_COMM_TIMEOUT_MS 2500
# define FPGA_DATA_PROCESSING_TIMEOUT_MS 2500
namespace RemoteLab {
typedef KParts : : GenericFactory < RemoteLab : : FPGAProgramPart > Factory ;
# define CLIENT_LIBRARY "libremotelab_fpgaprogrammer"
K_EXPORT_COMPONENT_FACTORY ( libremotelab_fpgaprogrammer , RemoteLab : : Factory )
FPGAProgramPart : : FPGAProgramPart ( TQWidget * parentWidget , const char * widgetName , TQObject * parent , const char * name , const TQStringList & )
: RemoteInstrumentPart ( parent , name ) , m_socket ( 0 ) , m_base ( 0 ) , connToServerConnecting ( false ) , connToServerState ( - 1 ) , connToServerTimeoutTimer ( NULL ) ,
m_commHandlerState ( 0 ) , m_connectionActiveAndValid ( false ) , m_tickerState ( 0 )
{
// Initialize mutex
m_connectionMutex = new TQMutex ( false ) ;
// Initialize kpart
setInstance ( Factory : : instance ( ) ) ;
setWidget ( new TQVBox ( parentWidget , widgetName ) ) ;
// Create timers
m_connectionTimer = new TQTimer ( this ) ;
connect ( m_connectionTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( finishConnectingToServer ( ) ) ) ;
m_updateTimeoutTimer = new TQTimer ( this ) ;
connect ( m_updateTimeoutTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( mainEventLoop ( ) ) ) ;
m_pingDelayTimer = new TQTimer ( this ) ;
connect ( m_pingDelayTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( mainEventLoop ( ) ) ) ;
// Create widgets
m_base = new FPGAProgramBase ( widget ( ) ) ;
// Initialize widgets
connect ( m_base - > programRunButton , SIGNAL ( clicked ( ) ) , this , SLOT ( programRunButtonClicked ( ) ) ) ;
connect ( m_base - > programmingInputFile , SIGNAL ( textChanged ( const TQString & ) ) , this , SLOT ( processLockouts ( ) ) ) ;
TQTimer : : singleShot ( 0 , this , TQT_SLOT ( postInit ( ) ) ) ;
}
FPGAProgramPart : : ~ FPGAProgramPart ( ) {
if ( m_connectionMutex - > locked ( ) ) {
printf ( " [WARNING] Exiting when data transfer still in progress! \n \r " ) ; fflush ( stdout ) ;
}
disconnectFromServer ( ) ;
delete m_connectionMutex ;
}
void FPGAProgramPart : : processLockouts ( ) {
TQWidget * mainWidget = widget ( ) ;
if ( mainWidget ) {
if ( ( m_socket ) & & ( m_socket - > state ( ) = = TQSocket : : Connected ) & & ( connToServerState > 0 ) & & ( connToServerConnecting = = false ) ) {
mainWidget - > setEnabled ( true ) ;
}
else {
mainWidget - > setEnabled ( false ) ;
}
}
if ( ( m_base - > programmingInputFile - > url ( ) ! = " " ) & & ( m_commHandlerMode = = 0 ) & & ( m_connectionActiveAndValid = = true ) ) {
m_base - > programRunButton - > setEnabled ( true ) ;
}
else {
m_base - > programRunButton - > setEnabled ( false ) ;
}
if ( m_commHandlerMode = = 1 ) {
m_base - > programmingInputFile - > setEnabled ( false ) ;
}
else {
m_base - > programmingInputFile - > setEnabled ( true ) ;
}
if ( ( m_connectionActiveAndValid = = true ) & & ( m_commHandlerMode = = 0 ) ) {
m_base - > programmingStatusLabel - > setText ( i18n ( " Ready " ) ) ;
}
}
void FPGAProgramPart : : resizeToHint ( ) {
resize ( widget ( ) - > sizeHint ( ) ) ;
}
void FPGAProgramPart : : connectionClosed ( ) {
closeURL ( ) ;
}
void FPGAProgramPart : : postInit ( ) {
//
}
bool FPGAProgramPart : : openURL ( const KURL & url ) {
int ret ;
ret = connectToServer ( url . url ( ) ) ;
processLockouts ( ) ;
return ( ret ! = 0 ) ;
}
bool FPGAProgramPart : : closeURL ( ) {
disconnectFromServer ( ) ;
m_url = KURL ( ) ;
return true ;
}
void FPGAProgramPart : : disconnectFromServer ( ) {
m_connectionTimer - > stop ( ) ;
m_updateTimeoutTimer - > stop ( ) ;
if ( m_socket ) {
m_socket - > clearPendingData ( ) ;
m_socket - > close ( ) ;
delete m_socket ;
m_socket = NULL ;
}
processLockouts ( ) ;
}
void FPGAProgramPart : : finishConnectingToServer ( ) {
if ( ! m_socket ) {
connToServerState = - 1 ;
connToServerConnecting = false ;
processLockouts ( ) ;
return ;
}
if ( connToServerConnecting ) {
switch ( connToServerState ) {
case 0 :
if ( ! connToServerTimeoutTimer ) {
connToServerTimeoutTimer = new TQTimer ;
connToServerTimeoutTimer - > start ( SERVER_TIMEOUT_MS , TRUE ) ;
}
if ( ( m_socket - > state ( ) = = TQSocket : : Connecting ) | | ( m_socket - > state ( ) = = TQSocket : : HostLookup ) ) {
if ( ! connToServerTimeoutTimer - > isActive ( ) ) {
connToServerState = - 3 ;
connToServerConnecting = false ;
disconnectFromServer ( ) ;
KMessageBox : : error ( 0 , i18n ( " <qt>Unable to establish connection to remote server</qt> " ) , i18n ( " Connection Failed " ) ) ;
}
}
else {
if ( m_socket - > state ( ) = = TQSocket : : Connected ) {
printf ( " [DEBUG] Initial connection established... \n \r " ) ; fflush ( stdout ) ;
m_socket - > setDataTimeout ( SERVER_TIMEOUT_MS ) ;
m_socket - > setUsingKerberos ( true ) ;
connToServerState = 1 ;
}
else {
connToServerState = - 1 ;
connToServerConnecting = false ;
disconnectFromServer ( ) ;
KMessageBox : : error ( 0 , i18n ( " <qt>Unable to establish connection to remote server</qt> " ) , i18n ( " Connection Failed " ) ) ;
}
}
break ;
case 1 :
if ( m_socket - > kerberosStatus ( ) = = TDEKerberosClientSocket : : KerberosInitializing ) {
// Do nothing
}
else {
if ( m_socket - > kerberosStatus ( ) ! = TDEKerberosClientSocket : : KerberosInUse ) {
connToServerState = - 1 ;
connToServerConnecting = false ;
disconnectFromServer ( ) ;
KMessageBox : : error ( 0 , i18n ( " <qt>Unable to establish Kerberos protocol with remote server<p>Please verify that you currently hold a valid Kerberos ticket</qt> " ) , i18n ( " Connection Failed " ) ) ;
}
else {
connToServerState = 2 ;
}
}
break ;
case 2 :
// Connection established!
// Read magic number and proto version from server
m_socket - > processPendingData ( ) ;
if ( m_socket - > bytesAvailable ( ) > 0 ) {
TQDataStream ds ( m_socket ) ;
TQ_UINT32 magicnum ;
TQ_UINT32 protover ;
ds > > magicnum ;
ds > > protover ;
printf ( " [DEBUG] Got magic number %d and protocol version %d \n \r " , magicnum , protover ) ; fflush ( stdout ) ;
// Request connection to backend server
ds < < TQString ( " SERV " ) ;
ds < < TQString ( CLIENT_LIBRARY ) ;
connToServerState = 3 ;
}
break ;
case 3 :
// Read response from server
m_socket - > processPendingData ( ) ;
if ( m_socket - > bytesAvailable ( ) > 0 ) {
TQDataStream ds ( m_socket ) ;
TQString response ;
ds > > response ;
if ( response = = " OK " ) {
connToServerState = 4 ;
connToServerConnecting = false ;
connect ( m_socket , SIGNAL ( readyRead ( ) ) , m_socket , SLOT ( processPendingData ( ) ) ) ;
m_socket - > processPendingData ( ) ;
connect ( m_socket , SIGNAL ( newDataReceived ( ) ) , this , SLOT ( mainEventLoop ( ) ) ) ;
m_tickerState = 0 ;
m_commHandlerState = 0 ;
m_commHandlerMode = 0 ;
m_socket - > setDataTimeout ( NETWORK_COMM_TIMEOUT_MS ) ;
m_updateTimeoutTimer - > start ( NETWORK_COMM_TIMEOUT_MS , TRUE ) ;
processLockouts ( ) ;
mainEventLoop ( ) ;
return ;
}
else {
TQStringList errorStrings = textForServerError ( response ) ;
connToServerState = - 1 ;
connToServerConnecting = false ;
disconnectFromServer ( ) ;
KMessageBox : : error ( 0 , errorStrings [ 0 ] , errorStrings [ 1 ] ) ;
close ( ) ;
return ;
}
}
break ;
}
m_connectionTimer - > start ( 100 , TRUE ) ;
}
}
int FPGAProgramPart : : connectToServer ( TQString server ) {
if ( m_socket ) {
return - 1 ;
}
if ( ! m_socket ) {
m_socket = new TDEKerberosClientSocket ( this ) ;
connect ( m_socket , TQT_SIGNAL ( statusMessageUpdated ( const TQString & ) ) , this , TQT_SLOT ( setStatusMessage ( const TQString & ) ) ) ;
}
m_socket - > setServiceName ( " remotefpga " ) ;
m_socket - > setServerFQDN ( server ) ;
m_socket - > connectToHost ( server , 4004 ) ;
// Finish connecting when appropriate
connToServerState = 0 ;
connToServerConnecting = true ;
m_connectionTimer - > start ( 100 , TRUE ) ;
return 0 ;
}
void FPGAProgramPart : : programRunButtonClicked ( ) {
m_commHandlerState = 0 ;
m_commHandlerMode = 1 ;
processLockouts ( ) ;
}
# define UPDATEDISPLAY_TIMEOUT m_connectionActiveAndValid = false; \
m_tickerState = 0 ; \
m_commHandlerState = 0 ; \
m_commHandlerMode = 0 ; \
while ( m_socket - > bytesAvailable ( ) > 0 ) { \
m_socket - > readBlock ( data , 64 ) ; \
} \
setStatusMessage ( i18n ( " Server ping timeout. Please verify the status of your network connection. " ) ) ; \
m_updateTimeoutTimer - > start ( NETWORK_COMM_TIMEOUT_MS , TRUE ) ; \
m_connectionMutex - > unlock ( ) ; \
return ;
# define SET_WATCHDOG_TIMER if (!m_updateTimeoutTimer->isActive()) m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE);
# define PAT_WATCHDOG_TIMER m_updateTimeoutTimer->stop(); m_updateTimeoutTimer->start(NETWORK_COMM_TIMEOUT_MS, TRUE);
void FPGAProgramPart : : mainEventLoop ( ) {
TQDataStream ds ( m_socket ) ;
if ( ! m_connectionMutex - > tryLock ( ) ) {
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ; // Handle the concurrently received call immediately after current execution
return ;
}
if ( m_socket ) {
char data [ 64 ] ;
if ( m_commHandlerMode = = 0 ) {
// Normal operation
switch ( m_commHandlerState ) {
case 0 :
// Get status of remote system
// RAJA FIXME
ds < < TQString ( " STATUS " ) ;
m_commHandlerState = 1 ;
break ;
case 1 :
// Get all data
if ( m_socket - > bytesAvailable ( ) > 0 ) {
PAT_WATCHDOG_TIMER
// RAJA FIXME
TQString status ;
ds > > status ;
printf ( " [RAJA DEBUG 106.0] Status: '%s' \n \r " , status . ascii ( ) ) ; fflush ( stdout ) ;
if ( status = = " " ) {
// Transfer probably failed
UPDATEDISPLAY_TIMEOUT
}
m_connectionActiveAndValid = true ;
TQString tickerChar ;
switch ( m_tickerState ) {
case 0 :
tickerChar = " - " ;
break ;
case 1 :
tickerChar = " \\ " ;
break ;
case 2 :
tickerChar = " | " ;
break ;
case 3 :
tickerChar = " / " ;
break ;
}
setStatusMessage ( i18n ( " Running " ) + TQString ( " ... %1 " ) . arg ( tickerChar ) ) ;
m_tickerState + + ;
if ( m_tickerState > 3 ) {
m_tickerState = 0 ;
}
m_pingDelayTimer - > start ( 250 , TRUE ) ;
m_commHandlerState = 2 ;
}
else {
if ( ! m_updateTimeoutTimer - > isActive ( ) ) {
UPDATEDISPLAY_TIMEOUT
}
}
break ;
case 2 :
if ( ! m_pingDelayTimer - > isActive ( ) ) {
// Fire next event loop immediately
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ;
// Execute query on next event loop
m_commHandlerState = 0 ;
}
PAT_WATCHDOG_TIMER
break ;
}
}
else if ( m_commHandlerMode = = 1 ) {
// Program mode!
// RAJA FIXME
if ( m_commHandlerState = = 0 ) {
m_base - > programmingStatusLabel - > setText ( i18n ( " Reading input file " ) + " ... " ) ;
TQFile file ( m_base - > programmingInputFile - > url ( ) ) ;
if ( file . open ( IO_ReadOnly ) ) {
m_programmingFileData = file . readAll ( ) ;
file . close ( ) ;
m_base - > programmingProgressBar - > setTotalSteps ( 0 ) ;
m_base - > programmingProgressBar - > setProgress ( 0 ) ;
// Transmit file to remote server
m_base - > programmingStatusLabel - > setText ( i18n ( " Sending data to server " ) + " ... " ) ;
ds < < TQString ( " FILE " ) ;
m_programmingFileTotalSize = m_programmingFileData . size ( ) ;
m_programmingFileTransferredBytes = 0 ;
ds < < m_programmingFileTotalSize ;
m_base - > programmingProgressBar - > setTotalSteps ( m_programmingFileTotalSize ) ;
m_commHandlerState = 1 ;
}
else {
KMessageBox : : error ( 0 , i18n ( " <qt>Unable to open selected programming file</qt> " ) , i18n ( " Program Failed " ) ) ;
m_commHandlerMode = 0 ;
m_commHandlerState = 0 ;
m_base - > programmingProgressBar - > setTotalSteps ( 1 ) ;
m_base - > programmingProgressBar - > setProgress ( 0 ) ;
processLockouts ( ) ;
}
}
else if ( m_commHandlerState = = 1 ) {
TQ_ULONG bytesLeft = ( m_programmingFileTotalSize - m_programmingFileTransferredBytes ) ;
TQ_ULONG bytesToTransfer = bytesLeft ;
if ( bytesToTransfer > 512 ) {
bytesToTransfer = 512 ;
}
m_programmingFileTransferredBytes = m_programmingFileTransferredBytes + m_socket - > writeBlock ( m_programmingFileData . data ( ) + m_programmingFileTransferredBytes , bytesToTransfer ) ;
m_base - > programmingProgressBar - > setProgress ( m_programmingFileTransferredBytes ) ;
if ( m_programmingFileTransferredBytes > = m_programmingFileTotalSize ) {
// Initiate programming
m_base - > programmingStatusLabel - > setText ( i18n ( " Programming device " ) + " ... " ) ;
ds < < TQString ( " PROGRAM " ) ;
// Request status
ds < < TQString ( " STATUS " ) ;
m_commHandlerState = 2 ;
}
else {
// Fire next event loop immediately
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ;
}
}
else if ( m_commHandlerState = = 2 ) {
// Get response
if ( m_socket - > bytesAvailable ( ) > 0 ) {
PAT_WATCHDOG_TIMER
// RAJA FIXME
TQString result ;
ds > > result ;
if ( result = = " PROGRAMMING " ) {
// Request status
ds < < TQString ( " STATUS " ) ;
}
else if ( result = = " DONE " ) {
int retCode ;
TQString log ;
ds > > retCode ;
ds > > log ;
// RAJA FIXME
// Handle errors
// This does not update the log as it should!
if ( retCode < 0 ) {
// Error!
m_commHandlerMode = 0 ;
m_commHandlerState = 0 ;
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ;
m_base - > programmingProgressBar - > setTotalSteps ( 1 ) ;
m_base - > programmingProgressBar - > setProgress ( 0 ) ;
KMessageBox : : error ( 0 , i18n ( " <qt>Programming process failure<p>Please see log for details</qt> " ) , i18n ( " Program Failed " ) ) ;
processLockouts ( ) ;
}
// Done!
m_commHandlerMode = 0 ;
m_commHandlerState = 0 ;
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ;
m_base - > programmingProgressBar - > setTotalSteps ( 1 ) ;
m_base - > programmingProgressBar - > setProgress ( 0 ) ;
processLockouts ( ) ;
}
else if ( result = = " " ) {
// Transfer probably failed
// Do nothing
}
else {
// Error!
m_commHandlerMode = 0 ;
m_commHandlerState = 0 ;
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ;
m_base - > programmingProgressBar - > setTotalSteps ( 1 ) ;
m_base - > programmingProgressBar - > setProgress ( 0 ) ;
KMessageBox : : error ( 0 , i18n ( " <qt>Unknown error</qt> " ) , i18n ( " Program Failed " ) ) ;
processLockouts ( ) ;
}
}
else {
if ( ! m_updateTimeoutTimer - > isActive ( ) ) {
m_commHandlerMode = 0 ;
m_commHandlerState = 0 ;
TQTimer : : singleShot ( 0 , this , SLOT ( mainEventLoop ( ) ) ) ;
m_base - > programmingProgressBar - > setTotalSteps ( 1 ) ;
m_base - > programmingProgressBar - > setProgress ( 0 ) ;
processLockouts ( ) ;
UPDATEDISPLAY_TIMEOUT
}
}
}
}
SET_WATCHDOG_TIMER
}
else {
m_commHandlerState = 0 ;
m_commHandlerMode = 0 ;
}
m_connectionMutex - > unlock ( ) ;
}
KAboutData * FPGAProgramPart : : createAboutData ( ) {
return new KAboutData ( APP_NAME , I18N_NOOP ( APP_PRETTYNAME ) , APP_VERSION ) ;
}
} //namespace RemoteLab
# include "part.moc"