//************************************************************************************************** // PrcBase.cpp * // ------------- * // Started : 2004-01-29 * // Last Update : 2016-09-30 * // Copyright : (C) 2004-2016 MSWaters * //************************************************************************************************** //************************************************************************************************** // * // 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. * // * //************************************************************************************************** #include "PrcBase.hpp" //************************************************************************************************** // Constructor. PrcBase::PrcBase( int iFlags ) : wxProcess( iFlags ) { m_oNameLog = DEF_LOG_FILE; // Set the default log file path and name m_osErrMsg = wxT(""); // Clear the error message m_iPid = -1; // Clear the process ID number m_iExitCode = 0; // Clear the process exit code } //************************************************************************************************** // Destructor. PrcBase::~PrcBase( ) { // Delete the process log file bDelLogFile( ); } //************************************************************************************************** // Find a binary using the user's PATH environment variable given the binary name. // // Argument List : // ofnBin - The binary name without the path // // Return Values : // true - Success (path prepended to oFnBinary) // false - Failure bool PrcBase::bFindBinary( wxFileName & roFileBin ) { wxPathList opl1; wxFileName ofn1; wxString os1, os2, os3; m_osErrMsg = wxT(""); // Initial checks if( ! roFileBin.IsOk( ) ) return( false ); // Search environment variable PATH for the first occurrence of the binary opl1.AddEnvList( wxT("PATH") ); ofn1 = opl1.FindAbsoluteValidPath( roFileBin.GetFullName( ) ); // Check whether the binary was successfully found if( !ofn1.IsOk( ) || !ofn1.FileExists( ) ) { os1 << wxT("Can't find the binary : ") << m_oNameBin.GetFullName( ); wxGetEnv( wxT("PATH"), &os2 ); os2.Prepend( wxT("$PATH = \n") ); // Set the object error message os3 = os1 + wxT("\n\n") + os2; SetErrMsg( os3 ); // If debug mode is enabled send the error message to the console if( g_bDebug ) { os3 = os1 + wxT(" (") + os2 + wxT(")."); std::cerr << "DEBUG : " << rosStrToLine( os3 ).mb_str( ) << "\n\n"; } return( false ); } m_oNameBin = ofn1; return( true ); } //************************************************************************************************** // Get the console output after a process has been initiated and record it in the log file. // // Return Values : // true - Success // false - Failure bool PrcBase::bLogOutput( void ) { wxOutputStream * poStdOut; wxInputStream * poStdIn; wxInputStream * poStdErr; wxString os1, os2; wxChar oc1; // Check that the log file name is valid if( ! m_oNameLog.IsOk( ) ) return( false ); // Open the file wxTextFile oFileLog( m_oNameLog.GetFullPath( ) ); if( oFileLog.Exists( ) ) { if( ! oFileLog.Open( ) ) return( false ); } else { if( ! oFileLog.Create( ) ) return( false ); } // Clear the file if it contains lines oFileLog.Clear( ); // Get pointers to the input streams poStdOut = GetOutputStream( ); poStdIn = GetInputStream( ); poStdErr = GetErrorStream( ); // Read the console input while( bIsExec( ) || poStdIn->IsOk( ) || poStdErr->IsOk( ) ) { // Get a line of data from stdout while( poStdIn->CanRead( ) ) { oc1 = poStdIn->GetC( ); if( oc1==wxT('\t') || (oc1>31 && oc1!=127) ) os1 << oc1; if( oc1 == wxT('\n') ) { oFileLog.AddLine( os1 ); os1 = wxT(""); } } // Get a line of data from stderr while( poStdErr->CanRead( ) ) { oc1 = poStdErr->GetC( ); if( oc1==wxT('\t') || (oc1>31 && oc1!=127) ) os2 << oc1; if( oc1 == wxT('\n') ) { oFileLog.AddLine( os2 ); os2 = wxT(""); } } wxTheApp->Yield( true ); // Yield CPU to other processes } // Delete the stream objects delete poStdOut; delete poStdIn; delete poStdErr; SetPipeStreams( NULL, NULL, NULL ); if( ! os1.IsEmpty( ) ) oFileLog.AddLine( os1 ); if( ! os2.IsEmpty( ) ) oFileLog.AddLine( os2 ); oFileLog.Write( ); // Save the changes to disk oFileLog.Close( ); // Close the log file return( true ); } //************************************************************************************************** // Does the binary exist? // // Return Values : // true - The binary does exist // false - The binary doesn't exist bool PrcBase::bBinExists( void ) { if( !m_oNameBin.IsOk( ) || !m_oNameBin.FileExists( ) ) return( false ); return( true ); } //************************************************************************************************** // Set the file name of the binary to be executed. // // Argument List : // rosFileName - A string containing the binary file name // // Return Values : // true - Success // false - Failure bool PrcBase::bSetBinFile( const wxString & rosFileName ) { if( rosFileName.IsEmpty( ) ) return( false ); m_oNameBin = rosFileName; if( ! bFindBinary( m_oNameBin ) ) return( false ); return( true ); } //************************************************************************************************** // Set the process log file name. // (If set all process output can be captured to this file.) // // Argument List : // rosFileName - A string containing the full path and file name // // Return Values : // true - Success // false - Failure bool PrcBase::bSetLogFile( const wxString & rosFileName ) { wxFileName ofn1; ofn1 = rosFileName; if( ! ofn1.IsOk( ) ) return( false ); if( ofn1.GetPath( ).IsEmpty( ) ) ofn1.SetPath( wxT(".") ); m_oNameLog = ofn1; return( true ); } //************************************************************************************************** // Set the argument list to be appended to the binary name. // // Argument List : // rosArgLst - A string containing the argument list // // Return Values : // true - Success // false - Failure bool PrcBase::bSetArgLst( const wxString & rosArgLst ) { if( rosArgLst.IsEmpty( ) ) return( false ); m_osArgLst = rosArgLst; return( true ); } //************************************************************************************************** // Execute a process asynchronously ie. return as soon as the process has been launched. // // Return Values : // true - Success // false - Failure bool PrcBase::bExecAsync( void ) { wxString os1, os2; // Only execute simulation if it isn't already running if( bIsExec( ) ) return( false ); // Clear error attributes m_osErrMsg = wxT(""); // Check that the binary exists if( ! bBinExists( ) ) if( ! bFindBinary( m_oNameBin ) ) return( false ); // Construct the command line to be executed os1 = roGetBinFile( ).GetFullPath( ); if( ! rosGetArgLst( ).IsEmpty( ) ) os1 << wxT(' ') << rosGetArgLst( ); // Attempt to execute the simulation m_iPid = (int) wxExecute( os1, wxEXEC_ASYNC, this ); if( m_iPid <= 0 ) { os2.Empty( ); os2 << wxT("Couldn't start process : ") << os1; SetErrMsg( os2 ); return( false ); } return( true ); } //************************************************************************************************** // Execute a process synchronously ie. wait for the process to terminate before returning. // // Return Values : // true - Success // false - Failure bool PrcBase::bExecSync( void ) { // Not yet implemented ??? 12/03/2010 return( false ); // long liRtn; // wxEnableTopLevelWindows( false ); // liRtn = wxExecute( rosCmd, wxEXEC_SYNC ); // wxEnableTopLevelWindows( true ); // if( liRtn != 0 ) return( false ); // return( true ); // This is how I'd like to do it but it doesn't work 20/11/2003 ??? // wxEnableTopLevelWindows( false ); // int iRtn = (int) wxExecute( osCmd, wxEXEC_SYNC ); // wxEnableTopLevelWindows( true ); // wxProcess oProcess( wxPROCESS_DEFAULT ); // iPid = (int) wxExecute( osCmd, wxEXEC_SYNC ); // if( iPid == 0 ) // { // Error gnetlist could not be executed // cerr << "The command:\n " << osCmd << "\ncould not be executed."; // return( false ); // } // for( ui1=0; ui1<300; ui1++ ) // { // Wait up to 30 seconds for the process to complete //cerr << iPid << '\n'; // wxUsleep( 100 ); // Sleep for 100msec // if( ! oProcess.Exists( iPid ) ) break; // } // if( oProcess.Exists( iPid ) ) // { // Error gnetlist had to be terminated prematurely // oProcess.Kill( iPid ); // cerr << "The command:\n " << osCmd << "\ntook more than 30 sec. to " // << "execute and so was terminated prematurely."; // return( false ); // } } //************************************************************************************************** // Stop the simulation currently in progress. // // Return Values : // true - Success // false - Failure bool PrcBase::bKill( void ) { if( ! bIsExec( ) ) return( true ); if( ! Exists( m_iPid ) ) return( true ); if( Kill( m_iPid, wxSIGTERM ) != wxKILL_OK ) return( false ); m_iPid = -1; m_iExitCode = 0; return( true ); } //************************************************************************************************** // Remove the log file. // // Return Values : // Success - true // Failure - false bool PrcBase::bDelLogFile( void ) { if( m_oNameLog.GetFullPath( ).IsEmpty( ) ) return( true ); if( ! ::wxFileExists( m_oNameLog.GetFullPath( ) ) ) return( true ); return( ::wxRemoveFile( m_oNameLog.GetFullPath( ) ) ); } //************************************************************************************************** // Collect the output from the simulator and print to a text control. // // Argument List : // roTxtCtl - The text control to contain the simulator output void PrcBase::Print( TextCtrl & roTxtCtl ) { roTxtCtl.bClear( ); // Clear the text control PrintCmd( roTxtCtl ); // Print the process command PrintRsp( roTxtCtl ); // Print the process command response } //************************************************************************************************** // Print the command envoking the process to a text control. // // Argument List : // roTxtCtl - The text control to contain process input void PrcBase::PrintCmd( TextCtrl & roTxtCtl ) { wxString os1; // Print the process command os1.Empty( ); os1 << wxT(" *************** PROCESS COMMAND ***************"); roTxtCtl.bAppendLine( os1 ); os1.Empty( ); roTxtCtl.bAppendLine( os1 ); os1 << roGetBinFile( ).GetFullPath( ); if( ! rosGetArgLst( ).IsEmpty( ) ) os1 << wxT(' ') << rosGetArgLst( ); roTxtCtl.bAppendLine( os1 ); os1.Empty( ); roTxtCtl.bAppendLine( os1 ); } //************************************************************************************************** // Collect the output from the process and print it to a text control. // // Argument List : // roTxtCtl - The text control to contain the simulator output void PrcBase::PrintRsp( TextCtrl & roTxtCtl ) { wxString os1; // Print the process command response if( ! m_oNameLog.GetFullPath( ).IsEmpty( ) ) { os1.Empty( ); os1 << wxT(" *************** PROCESS RESPONSE **************"); roTxtCtl.bAppendLine( os1 ); os1.Empty( ); roTxtCtl.bAppendLine( os1 ); if( m_oNameLog.IsOk( ) && m_oNameLog.FileExists( ) ) roTxtCtl.bAppendFile( m_oNameLog.GetFullPath( ) ); else { os1 << wxT("Couldn't load the file containing the process ouput : ") << m_oNameLog.GetFullPath( ); roTxtCtl.bAppendLine( os1 ); } } } //************************************************************************************************** // Print the object attributes. // // Argument List : // rosPrefix - A prefix to every line displayed (usually just spaces) void PrcBase::Print( const wxString & rosPrefix ) { std::cout << rosPrefix.mb_str() << "m_oNameBin : " << m_oNameBin.GetFullPath( ).mb_str() << '\n'; std::cout << rosPrefix.mb_str() << "m_oNameLog : " << m_oNameLog.GetFullPath( ).mb_str() << '\n'; std::cout << rosPrefix.mb_str() << "m_osArgLst : " << m_osArgLst.mb_str( ) << '\n'; std::cout << rosPrefix.mb_str() << "m_osErrMsg : " << m_osErrMsg.mb_str( ) << '\n'; std::cout << rosPrefix.mb_str() << "m_iPid : " << m_iPid << '\n'; std::cout << rosPrefix.mb_str() << "m_iExitCode : " << m_iExitCode << '\n'; } //************************************************************************************************** // Call-back function called when the process terminates. // (WARNING : Be careful of this function definition, if it's incorrect the application can throw a // segmentation fault.) // // Argument List : // iPid - The PID of the process which just terminated // iStatus - The exit code of the process void PrcBase::OnTerminate( int iPid, int iStatus ) { if( iPid == m_iPid ) { m_iPid = -1; m_iExitCode = iStatus; } } //**************************************************************************************************