OpenWalnut  1.5.0dev
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 <memory>
27 #include <string>
28 #include <vector>
29 
30 #include "../wrappers/WColorWrapper.h"
31 #include "../wrappers/WLoggerWrapper.h"
32 #include "../wrappers/WModuleWrapper.h"
33 #include "../wrappers/WPropertyGroupWrapper.h"
34 #include "../wrappers/WPropertyWrapper.h"
35 #include "../wrappers/WUtilityFunctions.h"
36 #include "WScriptInterpreterPython.h"
37 #include "core/kernel/WKernel.h"
38 
39 #ifdef PYTHON_FOUND
40 
41 WScriptInterpreterPython::WScriptInterpreterPython( std::shared_ptr< WModuleContainer > const& rootContainer )
43  m_rootContainer( rootContainer ),
44  m_argc( 0 ),
45  m_argv( 0 ),
46  m_scriptThread( *this )
47 {
48  try
49  {
50  Py_Initialize();
51  m_pyModule = pb::import( "__main__" );
52  m_pyMainNamespace = m_pyModule.attr( "__dict__" );
53  }
54  catch( pb::error_already_set const& )
55  {
56  PyErr_Print();
57  }
58  // Make ctrl+c key available for killing interpeter
59  execute( "import signal" );
60  execute( "signal.signal( signal.SIGINT, signal.SIG_DFL )" );
61 
62  m_scriptThread.run();
63 }
64 
65 WScriptInterpreterPython::~WScriptInterpreterPython()
66 {
67  m_scriptThread.requestStop();
68  m_scriptThread.wait();
69 
70  Py_Finalize();
71 
72  if( m_argv )
73  {
74  for( int k = 0; k < m_argc; ++k )
75  {
76  delete[] m_argv[ k ];
77  }
78  delete[] m_argv;
79  }
80 }
81 
82 void WScriptInterpreterPython::initBindings()
83 {
84  std::unique_lock< boost::mutex > lock( m_mutex );
85 
86  m_pyMainNamespace[ "WColor" ] = pb::class_< WColorWrapper >( "WColor", pb::init< float, float, float, float >() )
87  .add_property( "r", &WColorWrapper::getR, &WColorWrapper::setR )
88  .add_property( "g", &WColorWrapper::getG, &WColorWrapper::setG )
89  .add_property( "b", &WColorWrapper::getB, &WColorWrapper::setB )
90  .add_property( "a", &WColorWrapper::getA, &WColorWrapper::setA );
91 
92  // bind WPropertyWrapper class to "WProperty" in the python namespace
93  // no constructor in python for now
94  m_pyMainNamespace[ "WProperty" ] = pb::class_< WPropertyWrapper >( "WProperty", pb::no_init )
95  .def( "getBool", &WPropertyWrapper::getBool )
96  .def( "getInt", &WPropertyWrapper::getInt )
97  .def( "getString", &WPropertyWrapper::getString )
98  .def( "getDouble", &WPropertyWrapper::getDouble )
99  .def( "getFilename", &WPropertyWrapper::getFilename )
100  .def( "getSelection", &WPropertyWrapper::getSelection )
101  .def( "getColor", &WPropertyWrapper::getColor )
102  .def( "setBool", &WPropertyWrapper::setBool )
103  .def( "setInt", &WPropertyWrapper::setInt )
104  .def( "setString", &WPropertyWrapper::setString )
105  .def( "setDouble", &WPropertyWrapper::setDouble )
106  .def( "setFilename", &WPropertyWrapper::setFilename )
107  .def( "setSelection", &WPropertyWrapper::setSelection )
108  .def( "setColor", &WPropertyWrapper::setColor )
109  .def( "click", &WPropertyWrapper::click )
110  .def( "getName", &WPropertyWrapper::getName )
111  .def( "getDescription", &WPropertyWrapper::getDescription )
112  .def( "waitForUpdate", &WPropertyWrapper::waitForUpdate );
113 
114  m_pyMainNamespace[ "WPropertyGroup" ] = pb::class_< WPropertyGroupWrapper >( "WPropertyGroup", pb::no_init )
115  .def( "getProperty", &WPropertyGroupWrapper::getProperty )
116  .def( "getGroup", &WPropertyGroupWrapper::getGroup )
117  .def( "getName", &WPropertyGroupWrapper::getName )
118  .def( "getDescription", &WPropertyGroupWrapper::getDescription );
119 
120  m_pyMainNamespace[ "WModuleContainer" ] = pb::class_< WModuleContainerWrapper >( "WModuleContainer", pb::no_init )
121  .def( "create", &WModuleContainerWrapper::create )
122  .def( "remove", &WModuleContainerWrapper::remove )
123  .def( "createDataModule", &WModuleContainerWrapper::createDataModule );
124 
125  m_pyMainNamespace[ "WOutputConnector" ] = pb::class_< WOutputConnectorWrapper >( "WOutputConnectorWrapper", pb::no_init )
126  .def( "disconnect", &WOutputConnectorWrapper::disconnect );
127 
128  m_pyMainNamespace[ "WInputConnector" ] = pb::class_< WInputConnectorWrapper >( "WInputConnectorWrapper", pb::no_init )
129  .def( "connect", &WInputConnectorWrapper::connect )
130  .def( "disconnect", &WInputConnectorWrapper::disconnect )
131  .def( "waitForInput", &WInputConnectorWrapper::waitForInput );
132 
133  m_pyMainNamespace[ "WModule" ] = pb::class_< WModuleWrapper >( "WModule", pb::no_init )
134  .def( "getName", &WModuleWrapper::getName )
135  .def( "getDescription", &WModuleWrapper::getDescription )
136  .def( "getProperties", &WModuleWrapper::getProperties )
137  .def( "getInformationProperties", &WModuleWrapper::getInformationProperties )
138  .def( "getInputConnector", &WModuleWrapper::getInputConnector )
139  .def( "getOutputConnector", &WModuleWrapper::getOutputConnector );
140 
141  // bind the kernel's root container to the "rootContainer" variable in the python namespace
142  // this allows access to the modules via this variable
143  m_pyMainNamespace[ "rootContainer" ] = &m_rootContainer;
144 
145  m_pyMainNamespace[ "WLogger" ] = pb::class_< WLoggerWrapper >( "WLogger", pb::no_init )
146  .def( "addFileStream", &WLoggerWrapper::addFileStream )
147  .def( "removeFileStream", &WLoggerWrapper::removeFileStream )
148  .def( "removeAllFileStreams", &WLoggerWrapper::removeAllFileStreams )
149  .def( "error", &WLoggerWrapper::error )
150  .def( "warning", &WLoggerWrapper::warning )
151  .def( "info", &WLoggerWrapper::info )
152  .def( "debug", &WLoggerWrapper::debug );
153 
154 
155  m_pyMainNamespace[ "screenshot" ] = pb::make_function( &screenshot );
156  m_pyMainNamespace[ "initCamera" ] = pb::make_function( &initCamera );
157 
158  m_logger = WLoggerWrapper( WLogger::getLogger() );
159  m_pyMainNamespace[ "logger" ] = &m_logger;
160 }
161 
162 void WScriptInterpreterPython::setParameters( std::vector< std::string > const& params )
163 {
164  std::unique_lock< boost::mutex > lock( m_mutex );
165 
166  if( params.size() == 0 )
167  {
168  return;
169  }
170 
171  m_argc = params.size();
172  m_argv = new char*[ params.size() ];
173 
174  for( std::size_t k = 0; k < params.size(); ++k )
175  {
176  m_argv[ k ] = new char[ params[ k ].length() + 1 ];
177  std::snprintf( m_argv[ k ], params[ k ].length() + 1, "%s", params[ k ].c_str() );
178  m_argv[ k ][ params[ k ].length() ] = '\0';
179  }
180 
181  // Needed for python 3
182  wchar_t** w_argv = static_cast<wchar_t**>( PyMem_Malloc( sizeof( wchar_t* ) * m_argc ) );
183  for( int i = 0; i < m_argc; ++i )
184  {
185  wchar_t* arg = Py_DecodeLocale( m_argv[i], NULL );
186  w_argv[i] = arg;
187  }
188 
189  PySys_SetArgv( m_argc, w_argv );
190 }
191 
192 void WScriptInterpreterPython::execute( std::string const& line )
193 {
194  std::unique_lock< boost::mutex > lock( m_mutex );
195 
196  try
197  {
198  pb::exec( line.c_str(), m_pyMainNamespace );
199  }
200  catch( pb::error_already_set const& )
201  {
202  PyErr_Print();
203  }
204 }
205 
206 void WScriptInterpreterPython::executeAsync( std::string const& script )
207 {
208  m_scriptThread.addToExecuteQueue( script );
209 }
210 
211 void WScriptInterpreterPython::executeFile( std::string const& filename )
212 {
213  // load file content into string
214  std::ifstream in( filename.c_str() );
215  std::string script;
216  std::string line;
217  while( std::getline( in, line ) )
218  {
219  script += line + "\n";
220  }
221  in.close();
222 
223  // execute
224  try
225  {
226  execute( script );
227  }
228  catch( WException const& e )
229  {
230  wlog::error( "Walnut" ) << "Error while executing script: " << e.what();
231  }
232 }
233 
234 void WScriptInterpreterPython::executeFileAsync( std::string const& filename )
235 {
236  // load file content into string
237  std::ifstream in( filename.c_str() );
238  std::string script;
239  std::string line;
240  while( std::getline( in, line ) )
241  {
242  script += line + "\n";
243  }
244  in.close();
245 
246  // execute
247  executeAsync( script );
248 }
249 
250 std::string const WScriptInterpreterPython::getName() const
251 {
252  return "python";
253 }
254 
255 std::string const WScriptInterpreterPython::getExtension() const
256 {
257  return ".py";
258 }
259 
260 WScriptInterpreterPython::ScriptThread::ScriptThread( WScriptInterpreterPython& interpreter ) // NOLINT reference
261  : WThreadedRunner(),
262  m_scriptQueue(),
263  m_queueMutex(),
264  m_condition( new WCondition() ),
265  m_conditionSet(),
266  m_interpreter( interpreter )
267 {
268  m_conditionSet.setResetable( true, true );
269  m_conditionSet.add( m_condition );
270 }
271 
272 WScriptInterpreterPython::ScriptThread::~ScriptThread()
273 {
274 }
275 
276 void WScriptInterpreterPython::ScriptThread::requestStop()
277 {
279  m_condition->notify();
280 }
281 
282 void WScriptInterpreterPython::ScriptThread::threadMain()
283 {
284  while( !m_shutdownFlag )
285  {
286  m_conditionSet.wait();
287 
288  if( m_shutdownFlag )
289  break;
290 
291  std::size_t numScripts = 0;
292  {
293  std::unique_lock< boost::mutex > lock( m_queueMutex );
294  numScripts = m_scriptQueue.size();
295  }
296 
297  while( numScripts > 0 )
298  {
299  std::string script;
300 
301  // only getting the script content must be locked
302  {
303  std::unique_lock< boost::mutex > lock( m_queueMutex );
304  script = m_scriptQueue.front();
305  m_scriptQueue.pop();
306  }
307 
308  if( script.length() != 0 )
309  {
310  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Executing script asyncronously.";
311  // note that this may block if the interpreter is currently executing another script
312  m_interpreter.execute( script );
313  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Done executing script.";
314  }
315  {
316  std::unique_lock< boost::mutex > lock( m_queueMutex );
317  numScripts = m_scriptQueue.size();
318  }
319  }
320  }
321 }
322 
323 void WScriptInterpreterPython::ScriptThread::addToExecuteQueue( std::string const& script )
324 {
325  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Queueing script for asynchronous execution.";
326 
327  std::unique_lock< boost::mutex > lock( m_queueMutex );
328  m_scriptQueue.push( script );
329  m_condition->notify();
330 }
331 
332 #endif // PYTHON_FOUND
void setG(float g)
Set the green component.
float getB() const
Get the blue component.
Definition: WColorWrapper.h:81
float getG() const
Get the green component.
Definition: WColorWrapper.h:71
float getR() const
Get the red component.
Definition: WColorWrapper.h:61
void setA(float a)
Set the alpha component.
void setR(float r)
Set the red component.
float getA() const
Get the alpha component.
Definition: WColorWrapper.h:91
void setB(float b)
Set the blue component.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
Basic exception handler.
Definition: WException.h:39
virtual const char * what() const
Returns the message string set on throw.
Definition: WException.cpp:90
void disconnect()
Disconnect this connector.
void connect(WOutputConnectorWrapper conn)
Connect this input connector to an output connector.
void waitForInput()
Wait for new input.
A wrapper for WLogger.
void warning(std::string const &location, std::string const &message)
Output a warning to the logs.
void debug(std::string const &location, std::string const &message)
Output a debug message.
void removeAllFileStreams()
Remove all files to which the logger writes (and which were added by this wrapper).
void error(std::string const &location, std::string const &message)
Output an error to the logs.
bool addFileStream(std::string filename)
Add a file to which the logger output will be written.
bool removeFileStream(std::string filename)
Remove a file to which the logger writes.
void info(std::string const &location, std::string const &message)
Output information to the logs.
static WLogger * getLogger()
Returns pointer to the currently running logger instance.
Definition: WLogger.cpp:64
WModuleWrapper create(std::string const &name)
Creates a module from the prototype with the given name.
void remove(WModuleWrapper module)
Remove a module from the container.
WModuleWrapper createDataModule(std::string const &filename)
Creates a data module and load the file given via filename.
WInputConnectorWrapper getInputConnector(std::string const &name)
Get an input connector by name.
WPropertyGroupWrapper getProperties()
Returns a WPropertyGroupWrapper containing the module's properties.
WOutputConnectorWrapper getOutputConnector(std::string const &name)
Get an output connector by name.
WPropertyGroupWrapper getInformationProperties()
Returns a WPropertyGroupWrapper containing the module's info properties.
std::string getName() const
Get the name of the module.
std::string getDescription() const
Get the description of the module.
void disconnect()
Disconnect this connector.
WPropertyGroupWrapper getGroup(std::string const &name)
Retrieve a property group by name.
std::string getName() const
Return the name of the property group.
WPropertyWrapper getProperty(std::string const &name)
Retrieve a property by name.
std::string getDescription() const
Return the description of the property group.
std::string getName() const
Return the name of the property.
void setColor(WColorWrapper col)
Set the color of a color property.
void click()
Trigger a trigger property.
void setInt(int i)
Set the value of an integer property.
std::string getString(bool notify=false) const
Get the value of a string property.
std::string getDescription() const
Return the description of the property.
void setString(std::string const &s)
Set the value of a string property.
void setBool(bool b)
Set the value of a boolean property.
void waitForUpdate()
Wait for the property to update its value.
bool getBool(bool notify=false) const
Get the value of a boolean property.
std::string getFilename(bool notify=false) const
Get the filename of a filename property.
void setFilename(std::string const &fn)
Set the filename of the filename property.
double getDouble(bool notify=false) const
Get the value of a double property.
WColorWrapper getColor(bool notify=false) const
Get the color of a color property.
int getSelection(bool notify=false) const
Get the (first) selected item of a selection property.
void setSelection(int s)
Sets the selected item of a selection.
int getInt(bool notify=false) const
Get the value of an integer property.
void setDouble(double d)
Set the value of a double property.
An abstract base class for a script interpreter.
Base class for all classes needing to be executed in a separate thread.
virtual void requestStop()
This method's purpose is to request a stop without waiting for it.
WStreamedLogger info(const std::string &source)
Logging an information message.
Definition: WLogger.h:320
WStreamedLogger error(const std::string &source)
Logging an error message.
Definition: WLogger.h:298