OpenWalnut  1.4.0
WScriptInterpreterPython.cpp
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #include <fstream>
00026 #include <string>
00027 #include <vector>
00028 
00029 #include "core/kernel/WKernel.h"
00030 
00031 #include "../wrappers/WLoggerWrapper.h"
00032 #include "../wrappers/WModuleWrapper.h"
00033 #include "../wrappers/WPropertyGroupWrapper.h"
00034 #include "../wrappers/WPropertyWrapper.h"
00035 
00036 #include "WScriptInterpreterPython.h"
00037 
00038 #ifdef PYTHON_FOUND
00039 
00040 WScriptInterpreterPython::WScriptInterpreterPython( boost::shared_ptr< WModuleContainer > const& rootContainer )
00041     : WScriptInterpreter(),
00042       m_rootContainer( rootContainer ),
00043       m_argc( 0 ),
00044       m_argv( 0 ),
00045       m_scriptThread( *this )
00046 {
00047     try
00048     {
00049         Py_Initialize();
00050         m_pyModule = pb::import( "__main__" );
00051         m_pyMainNamespace = m_pyModule.attr( "__dict__" );
00052     }
00053     catch( pb::error_already_set const& )
00054     {
00055         PyErr_Print();
00056     }
00057     // Make ctrl+c key available for killing interpeter
00058     execute( "import signal" );
00059     execute( "signal.signal( signal.SIGINT, signal.SIG_DFL )" );
00060 
00061     m_scriptThread.run();
00062 }
00063 
00064 WScriptInterpreterPython::~WScriptInterpreterPython()
00065 {
00066     m_scriptThread.requestStop();
00067     m_scriptThread.wait();
00068 
00069     Py_Finalize();
00070 
00071     if( m_argv )
00072     {
00073         for( int k = 0; k < m_argc; ++k )
00074         {
00075             delete[] m_argv[ k ];
00076         }
00077         delete[] m_argv;
00078     }
00079 }
00080 
00081 void WScriptInterpreterPython::initBindings()
00082 {
00083     boost::unique_lock< boost::mutex > lock( m_mutex );
00084 
00085     // bind WPropertyWrapper class to "WProperty" in the python namespace
00086     // no constructor in python for now
00087     m_pyMainNamespace[ "WProperty" ] = pb::class_< WPropertyWrapper >( "WProperty", pb::no_init )
00088                                        .def( "getBool", &WPropertyWrapper::getBool )
00089                                        .def( "getInt", &WPropertyWrapper::getInt )
00090                                        .def( "getString", &WPropertyWrapper::getString )
00091                                        .def( "getDouble", &WPropertyWrapper::getDouble )
00092                                        .def( "getFilename", &WPropertyWrapper::getFilename )
00093                                        .def( "getSelection", &WPropertyWrapper::getSelection )
00094                                        .def( "setBool", &WPropertyWrapper::setBool )
00095                                        .def( "setInt", &WPropertyWrapper::setInt )
00096                                        .def( "setString", &WPropertyWrapper::setString )
00097                                        .def( "setDouble", &WPropertyWrapper::setDouble )
00098                                        .def( "setFilename", &WPropertyWrapper::setFilename )
00099                                        .def( "setSelection", &WPropertyWrapper::setSelection )
00100                                        .def( "click", &WPropertyWrapper::click )
00101                                        .def( "getName", &WPropertyWrapper::getName )
00102                                        .def( "getDescription", &WPropertyWrapper::getDescription )
00103                                        .def( "waitForUpdate", &WPropertyWrapper::waitForUpdate );
00104 
00105     m_pyMainNamespace[ "WPropertyGroup" ] = pb::class_< WPropertyGroupWrapper >( "WPropertyGroup", pb::no_init )
00106                                             .def( "getProperty", &WPropertyGroupWrapper::getProperty )
00107                                             .def( "getGroup", &WPropertyGroupWrapper::getGroup )
00108                                             .def( "getName", &WPropertyGroupWrapper::getName )
00109                                             .def( "getDescription", &WPropertyGroupWrapper::getDescription );
00110 
00111     m_pyMainNamespace[ "WModuleContainer" ] = pb::class_< WModuleContainerWrapper >( "WModuleContainer", pb::no_init )
00112                                               .def( "create", &WModuleContainerWrapper::create )
00113                                               .def( "remove", &WModuleContainerWrapper::remove )
00114                                               .def( "createDataModule", &WModuleContainerWrapper::createDataModule );
00115 
00116     m_pyMainNamespace[ "WOutputConnector" ] = pb::class_< WOutputConnectorWrapper >( "WOutputConnectorWrapper", pb::no_init )
00117                                              .def( "disconnect", &WOutputConnectorWrapper::disconnect );
00118 
00119     m_pyMainNamespace[ "WInputConnector" ] = pb::class_< WInputConnectorWrapper >( "WInputConnectorWrapper", pb::no_init )
00120                                              .def( "connect", &WInputConnectorWrapper::connect )
00121                                              .def( "disconnect", &WInputConnectorWrapper::disconnect )
00122                                              .def( "waitForInput", &WInputConnectorWrapper::waitForInput );
00123 
00124     m_pyMainNamespace[ "WModule" ] = pb::class_< WModuleWrapper >( "WModule", pb::no_init )
00125                                      .def( "getName", &WModuleWrapper::getName )
00126                                      .def( "getDescription", &WModuleWrapper::getDescription )
00127                                      .def( "getProperties", &WModuleWrapper::getProperties )
00128                                      .def( "getInformationProperties", &WModuleWrapper::getInformationProperties )
00129                                      .def( "getInputConnector", &WModuleWrapper::getInputConnector )
00130                                      .def( "getOutputConnector", &WModuleWrapper::getOutputConnector );
00131 
00132     // bind the kernel's root container to the "rootContainer" variable in the python namespace
00133     // this allows access to the modules via this variable
00134     m_pyMainNamespace[ "rootContainer" ] = &m_rootContainer;
00135 
00136     m_pyMainNamespace[ "WLogger" ]  = pb::class_< WLoggerWrapper >( "WLogger", pb::no_init )
00137                                      .def( "addFileStream", &WLoggerWrapper::addFileStream )
00138                                      .def( "removeFileStream", &WLoggerWrapper::removeFileStream )
00139                                      .def( "removeAllFileStreams", &WLoggerWrapper::removeAllFileStreams );
00140 
00141     m_logger = WLoggerWrapper( WLogger::getLogger() );
00142     m_pyMainNamespace[ "logger" ] = &m_logger;
00143 }
00144 
00145 void WScriptInterpreterPython::setParameters( std::vector< std::string > const& params )
00146 {
00147     boost::unique_lock< boost::mutex > lock( m_mutex );
00148 
00149     if( params.size() == 0 )
00150     {
00151         return;
00152     }
00153 
00154     m_argc = params.size();
00155     m_argv = new char*[ params.size() ];
00156 
00157     for( std::size_t k = 0; k < params.size(); ++k )
00158     {
00159         m_argv[ k ] = new char[ params[ k ].length() + 1 ];
00160         std::snprintf( m_argv[ k ], params[ k ].length() + 1, "%s", params[ k ].c_str() );
00161         m_argv[ k ][ params[ k ].length() ] = '\0';
00162     }
00163 
00164     PySys_SetArgv( m_argc, m_argv );
00165 }
00166 
00167 void WScriptInterpreterPython::execute( std::string const& line )
00168 {
00169     boost::unique_lock< boost::mutex > lock( m_mutex );
00170 
00171     try
00172     {
00173         pb::exec( line.c_str(), m_pyMainNamespace );
00174     }
00175     catch( pb::error_already_set const& )
00176     {
00177         PyErr_Print();
00178     }
00179 }
00180 
00181 void WScriptInterpreterPython::executeAsync( std::string const& script )
00182 {
00183     m_scriptThread.addToExecuteQueue( script );
00184 }
00185 
00186 void WScriptInterpreterPython::executeFile( std::string const& filename )
00187 {
00188     // load file content into string
00189     std::ifstream in( filename.c_str() );
00190     std::string script;
00191     std::string line;
00192     while( std::getline( in, line ) )
00193     {
00194         script += line + "\n";
00195     }
00196     in.close();
00197 
00198     // execute
00199     try
00200     {
00201         execute( script );
00202     }
00203     catch( WException const& e )
00204     {
00205         wlog::error( "Walnut" ) << "Error while executing script: " << e.what();
00206     }
00207 }
00208 
00209 void WScriptInterpreterPython::executeFileAsync( std::string const& filename )
00210 {
00211     // load file content into string
00212     std::ifstream in( filename.c_str() );
00213     std::string script;
00214     std::string line;
00215     while( std::getline( in, line ) )
00216     {
00217         script += line + "\n";
00218     }
00219     in.close();
00220 
00221     // execute
00222     executeAsync( script );
00223 }
00224 
00225 std::string const WScriptInterpreterPython::getName() const
00226 {
00227     return "python";
00228 }
00229 
00230 std::string const WScriptInterpreterPython::getExtension() const
00231 {
00232     return ".py";
00233 }
00234 
00235 WScriptInterpreterPython::ScriptThread::ScriptThread( WScriptInterpreterPython& interpreter ) // NOLINT reference
00236     : WThreadedRunner(),
00237       m_scriptQueue(),
00238       m_queueMutex(),
00239       m_condition( new WCondition() ),
00240       m_conditionSet(),
00241       m_interpreter( interpreter )
00242 {
00243     m_conditionSet.setResetable( true, true );
00244     m_conditionSet.add( m_condition );
00245 }
00246 
00247 WScriptInterpreterPython::ScriptThread::~ScriptThread()
00248 {
00249 }
00250 
00251 void WScriptInterpreterPython::ScriptThread::requestStop()
00252 {
00253     WThreadedRunner::requestStop();
00254     m_condition->notify();
00255 }
00256 
00257 void WScriptInterpreterPython::ScriptThread::threadMain()
00258 {
00259     while( !m_shutdownFlag )
00260     {
00261         m_conditionSet.wait();
00262 
00263         if( m_shutdownFlag )
00264             break;
00265 
00266         std::size_t numScripts = 0;
00267         {
00268             boost::unique_lock< boost::mutex > lock( m_queueMutex );
00269             numScripts = m_scriptQueue.size();
00270         }
00271 
00272         while( numScripts > 0 )
00273         {
00274             std::string script;
00275 
00276             // only getting the script content must be locked
00277             {
00278                 boost::unique_lock< boost::mutex > lock( m_queueMutex );
00279                 script = m_scriptQueue.front();
00280                 m_scriptQueue.pop();
00281             }
00282 
00283             if( script.length() != 0 )
00284             {
00285                 wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Executing script asyncronously.";
00286                 // note that this may block if the interpreter is currently executing another script
00287                 m_interpreter.execute( script );
00288                 wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Done executing script.";
00289             }
00290             {
00291                 boost::unique_lock< boost::mutex > lock( m_queueMutex );
00292                 numScripts = m_scriptQueue.size();
00293             }
00294         }
00295     }
00296 }
00297 
00298 void WScriptInterpreterPython::ScriptThread::addToExecuteQueue( std::string const& script )
00299 {
00300     wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Queueing script for asyncronous execution.";
00301 
00302     boost::unique_lock< boost::mutex > lock( m_queueMutex );
00303     m_scriptQueue.push( script );
00304     m_condition->notify();
00305 }
00306 
00307 #endif  // PYTHON_FOUND