OpenWalnut  1.4.0
WScriptInterpreterPython.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <fstream>
26 #include <string>
27 #include <vector>
28 
29 #include "core/kernel/WKernel.h"
30 
31 #include "../wrappers/WLoggerWrapper.h"
32 #include "../wrappers/WModuleWrapper.h"
33 #include "../wrappers/WPropertyGroupWrapper.h"
34 #include "../wrappers/WPropertyWrapper.h"
35 
36 #include "WScriptInterpreterPython.h"
37 
38 #ifdef PYTHON_FOUND
39 
40 WScriptInterpreterPython::WScriptInterpreterPython( boost::shared_ptr< WModuleContainer > const& rootContainer )
42  m_rootContainer( rootContainer ),
43  m_argc( 0 ),
44  m_argv( 0 ),
45  m_scriptThread( *this )
46 {
47  try
48  {
49  Py_Initialize();
50  m_pyModule = pb::import( "__main__" );
51  m_pyMainNamespace = m_pyModule.attr( "__dict__" );
52  }
53  catch( pb::error_already_set const& )
54  {
55  PyErr_Print();
56  }
57  // Make ctrl+c key available for killing interpeter
58  execute( "import signal" );
59  execute( "signal.signal( signal.SIGINT, signal.SIG_DFL )" );
60 
61  m_scriptThread.run();
62 }
63 
64 WScriptInterpreterPython::~WScriptInterpreterPython()
65 {
66  m_scriptThread.requestStop();
67  m_scriptThread.wait();
68 
69  Py_Finalize();
70 
71  if( m_argv )
72  {
73  for( int k = 0; k < m_argc; ++k )
74  {
75  delete[] m_argv[ k ];
76  }
77  delete[] m_argv;
78  }
79 }
80 
81 void WScriptInterpreterPython::initBindings()
82 {
83  boost::unique_lock< boost::mutex > lock( m_mutex );
84 
85  // bind WPropertyWrapper class to "WProperty" in the python namespace
86  // no constructor in python for now
87  m_pyMainNamespace[ "WProperty" ] = pb::class_< WPropertyWrapper >( "WProperty", pb::no_init )
88  .def( "getBool", &WPropertyWrapper::getBool )
89  .def( "getInt", &WPropertyWrapper::getInt )
90  .def( "getString", &WPropertyWrapper::getString )
91  .def( "getDouble", &WPropertyWrapper::getDouble )
92  .def( "getFilename", &WPropertyWrapper::getFilename )
93  .def( "getSelection", &WPropertyWrapper::getSelection )
94  .def( "setBool", &WPropertyWrapper::setBool )
95  .def( "setInt", &WPropertyWrapper::setInt )
96  .def( "setString", &WPropertyWrapper::setString )
97  .def( "setDouble", &WPropertyWrapper::setDouble )
98  .def( "setFilename", &WPropertyWrapper::setFilename )
99  .def( "setSelection", &WPropertyWrapper::setSelection )
100  .def( "click", &WPropertyWrapper::click )
101  .def( "getName", &WPropertyWrapper::getName )
102  .def( "getDescription", &WPropertyWrapper::getDescription )
103  .def( "waitForUpdate", &WPropertyWrapper::waitForUpdate );
104 
105  m_pyMainNamespace[ "WPropertyGroup" ] = pb::class_< WPropertyGroupWrapper >( "WPropertyGroup", pb::no_init )
106  .def( "getProperty", &WPropertyGroupWrapper::getProperty )
107  .def( "getGroup", &WPropertyGroupWrapper::getGroup )
108  .def( "getName", &WPropertyGroupWrapper::getName )
109  .def( "getDescription", &WPropertyGroupWrapper::getDescription );
110 
111  m_pyMainNamespace[ "WModuleContainer" ] = pb::class_< WModuleContainerWrapper >( "WModuleContainer", pb::no_init )
112  .def( "create", &WModuleContainerWrapper::create )
113  .def( "remove", &WModuleContainerWrapper::remove )
114  .def( "createDataModule", &WModuleContainerWrapper::createDataModule );
115 
116  m_pyMainNamespace[ "WOutputConnector" ] = pb::class_< WOutputConnectorWrapper >( "WOutputConnectorWrapper", pb::no_init )
117  .def( "disconnect", &WOutputConnectorWrapper::disconnect );
118 
119  m_pyMainNamespace[ "WInputConnector" ] = pb::class_< WInputConnectorWrapper >( "WInputConnectorWrapper", pb::no_init )
120  .def( "connect", &WInputConnectorWrapper::connect )
121  .def( "disconnect", &WInputConnectorWrapper::disconnect )
122  .def( "waitForInput", &WInputConnectorWrapper::waitForInput );
123 
124  m_pyMainNamespace[ "WModule" ] = pb::class_< WModuleWrapper >( "WModule", pb::no_init )
125  .def( "getName", &WModuleWrapper::getName )
126  .def( "getDescription", &WModuleWrapper::getDescription )
127  .def( "getProperties", &WModuleWrapper::getProperties )
128  .def( "getInformationProperties", &WModuleWrapper::getInformationProperties )
129  .def( "getInputConnector", &WModuleWrapper::getInputConnector )
130  .def( "getOutputConnector", &WModuleWrapper::getOutputConnector );
131 
132  // bind the kernel's root container to the "rootContainer" variable in the python namespace
133  // this allows access to the modules via this variable
134  m_pyMainNamespace[ "rootContainer" ] = &m_rootContainer;
135 
136  m_pyMainNamespace[ "WLogger" ] = pb::class_< WLoggerWrapper >( "WLogger", pb::no_init )
137  .def( "addFileStream", &WLoggerWrapper::addFileStream )
138  .def( "removeFileStream", &WLoggerWrapper::removeFileStream )
139  .def( "removeAllFileStreams", &WLoggerWrapper::removeAllFileStreams );
140 
141  m_logger = WLoggerWrapper( WLogger::getLogger() );
142  m_pyMainNamespace[ "logger" ] = &m_logger;
143 }
144 
145 void WScriptInterpreterPython::setParameters( std::vector< std::string > const& params )
146 {
147  boost::unique_lock< boost::mutex > lock( m_mutex );
148 
149  if( params.size() == 0 )
150  {
151  return;
152  }
153 
154  m_argc = params.size();
155  m_argv = new char*[ params.size() ];
156 
157  for( std::size_t k = 0; k < params.size(); ++k )
158  {
159  m_argv[ k ] = new char[ params[ k ].length() + 1 ];
160  std::snprintf( m_argv[ k ], params[ k ].length() + 1, "%s", params[ k ].c_str() );
161  m_argv[ k ][ params[ k ].length() ] = '\0';
162  }
163 
164  PySys_SetArgv( m_argc, m_argv );
165 }
166 
167 void WScriptInterpreterPython::execute( std::string const& line )
168 {
169  boost::unique_lock< boost::mutex > lock( m_mutex );
170 
171  try
172  {
173  pb::exec( line.c_str(), m_pyMainNamespace );
174  }
175  catch( pb::error_already_set const& )
176  {
177  PyErr_Print();
178  }
179 }
180 
181 void WScriptInterpreterPython::executeAsync( std::string const& script )
182 {
183  m_scriptThread.addToExecuteQueue( script );
184 }
185 
186 void WScriptInterpreterPython::executeFile( std::string const& filename )
187 {
188  // load file content into string
189  std::ifstream in( filename.c_str() );
190  std::string script;
191  std::string line;
192  while( std::getline( in, line ) )
193  {
194  script += line + "\n";
195  }
196  in.close();
197 
198  // execute
199  try
200  {
201  execute( script );
202  }
203  catch( WException const& e )
204  {
205  wlog::error( "Walnut" ) << "Error while executing script: " << e.what();
206  }
207 }
208 
209 void WScriptInterpreterPython::executeFileAsync( std::string const& filename )
210 {
211  // load file content into string
212  std::ifstream in( filename.c_str() );
213  std::string script;
214  std::string line;
215  while( std::getline( in, line ) )
216  {
217  script += line + "\n";
218  }
219  in.close();
220 
221  // execute
222  executeAsync( script );
223 }
224 
225 std::string const WScriptInterpreterPython::getName() const
226 {
227  return "python";
228 }
229 
230 std::string const WScriptInterpreterPython::getExtension() const
231 {
232  return ".py";
233 }
234 
235 WScriptInterpreterPython::ScriptThread::ScriptThread( WScriptInterpreterPython& interpreter ) // NOLINT reference
236  : WThreadedRunner(),
237  m_scriptQueue(),
238  m_queueMutex(),
239  m_condition( new WCondition() ),
240  m_conditionSet(),
241  m_interpreter( interpreter )
242 {
243  m_conditionSet.setResetable( true, true );
244  m_conditionSet.add( m_condition );
245 }
246 
247 WScriptInterpreterPython::ScriptThread::~ScriptThread()
248 {
249 }
250 
251 void WScriptInterpreterPython::ScriptThread::requestStop()
252 {
254  m_condition->notify();
255 }
256 
257 void WScriptInterpreterPython::ScriptThread::threadMain()
258 {
259  while( !m_shutdownFlag )
260  {
261  m_conditionSet.wait();
262 
263  if( m_shutdownFlag )
264  break;
265 
266  std::size_t numScripts = 0;
267  {
268  boost::unique_lock< boost::mutex > lock( m_queueMutex );
269  numScripts = m_scriptQueue.size();
270  }
271 
272  while( numScripts > 0 )
273  {
274  std::string script;
275 
276  // only getting the script content must be locked
277  {
278  boost::unique_lock< boost::mutex > lock( m_queueMutex );
279  script = m_scriptQueue.front();
280  m_scriptQueue.pop();
281  }
282 
283  if( script.length() != 0 )
284  {
285  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Executing script asyncronously.";
286  // note that this may block if the interpreter is currently executing another script
287  m_interpreter.execute( script );
288  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Done executing script.";
289  }
290  {
291  boost::unique_lock< boost::mutex > lock( m_queueMutex );
292  numScripts = m_scriptQueue.size();
293  }
294  }
295  }
296 }
297 
298 void WScriptInterpreterPython::ScriptThread::addToExecuteQueue( std::string const& script )
299 {
300  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Queueing script for asyncronous execution.";
301 
302  boost::unique_lock< boost::mutex > lock( m_queueMutex );
303  m_scriptQueue.push( script );
304  m_condition->notify();
305 }
306 
307 #endif // PYTHON_FOUND
WStreamedLogger error(const std::string &source)
Logging an error message.
Definition: WLogger.h:302
void setFilename(std::string const &fn)
Set the filename of the filename property.
void disconnect()
Disconnect this connector.
void remove(WModuleWrapper module)
Remove a module from the container.
bool getBool(bool notify=false) const
Get the value of a boolean property.
void waitForUpdate()
Wait for the property to update its value.
void setString(std::string const &s)
Set the value of a string property.
A wrapper for WLogger.
WStreamedLogger info(const std::string &source)
Logging an information message.
Definition: WLogger.h:324
static WLogger * getLogger()
Returns pointer to the currently running logger instance.
Definition: WLogger.cpp:64
WPropertyGroupWrapper getProperties()
Returns a WPropertyGroupWrapper containing the module's properties.
virtual void execute(std::string const &line)=0
Execute some code.
WModuleWrapper create(std::string const &name)
Creates a module from the prototype with the given name.
void setDouble(double d)
Set the value of a double property.
void setBool(bool b)
Set the value of a boolean property.
void connect(WOutputConnectorWrapper conn)
Connect this input connector to an output connector.
void disconnect()
Disconnect this connector.
void waitForInput()
Wait for new input.
Base class for all classes needing to be executed in a separate thread.
WPropertyGroupWrapper getGroup(std::string const &name)
Retrieve a property group by name.
std::string getName() const
Get the name of the module.
void setSelection(int s)
Sets the selected item of a selection.
An abstract base class for a script interpreter.
bool removeFileStream(std::string filename)
Remove a file to which the logger writes.
std::string getDescription() const
Get the description of the module.
std::string getDescription() const
Return the description of the property.
WPropertyGroupWrapper getInformationProperties()
Returns a WPropertyGroupWrapper containing the module's info properties.
std::string getString(bool notify=false) const
Get the value of a string property.
double getDouble(bool notify=false) const
Get the value of a double property.
std::string getName() const
Return the name of the property group.
void click()
Trigger a trigger property.
int getSelection(bool notify=false) const
Get the (first) selected item of a selection property.
WOutputConnectorWrapper getOutputConnector(std::string const &name)
Get an output connector by name.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:47
virtual const char * what() const
Returns the message string set on throw.
Definition: WException.cpp:90
WPropertyWrapper getProperty(std::string const &name)
Retrieve a property by name.
virtual void requestStop()
This method's purpose is to request a stop without waiting for it.
std::string getName() const
Return the name of the property.
void setInt(int i)
Set the value of an integer property.
Basic exception handler.
Definition: WException.h:38
std::string getFilename(bool notify=false) const
Get the filename of a filename property.
void removeAllFileStreams()
Remove all files to which the logger writes (and which were added by this wrapper).
bool addFileStream(std::string filename)
Add a file to which the logger output will be written.
WInputConnectorWrapper getInputConnector(std::string const &name)
Get an input connector by name.
WModuleWrapper createDataModule(std::string const &filename)
Creates a data module and load the file given via filename.
int getInt(bool notify=false) const
Get the value of an integer property.
std::string getDescription() const
Return the description of the property group.