#include "stdafx.h" #include "PyInt.h" #include "renderer.h" #include "Train.h" #include "Logs.h" //#include //#include //#include TPythonInterpreter *TPythonInterpreter::_instance = NULL; //#define _PY_INT_MORE_LOG #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wwrite-strings" #endif TPythonInterpreter::TPythonInterpreter() { WriteLog("Loading Python ..."); #ifdef _WIN32 if (sizeof(void*) == 8) Py_SetPythonHome("python64"); else Py_SetPythonHome("python"); #elif __linux__ if (sizeof(void*) == 8) Py_SetPythonHome("linuxpython64"); else Py_SetPythonHome("linuxpython"); #endif Py_Initialize(); _main = PyImport_ImportModule("__main__"); if (_main == NULL) { WriteLog("Cannot import Python module __main__"); } PyObject *cStringModule = PyImport_ImportModule("cStringIO"); _stdErr = NULL; if (cStringModule == NULL) return; PyObject *cStringClassName = PyObject_GetAttrString(cStringModule, "StringIO"); if (cStringClassName == NULL) return; PyObject *cString = PyObject_CallObject(cStringClassName, NULL); if (cString == NULL) return; if (PySys_SetObject("stderr", cString) != 0) return; _stdErr = cString; } TPythonInterpreter *TPythonInterpreter::getInstance() { if (!_instance) { _instance = new TPythonInterpreter(); } return _instance; } void TPythonInterpreter::killInstance() { delete _instance; } bool TPythonInterpreter::loadClassFile( std::string const &lookupPath, std::string const &className ) { std::set::const_iterator it = _classes.find(className); if (it == _classes.end()) { FILE *sourceFile = _getFile(lookupPath, className); if (sourceFile != nullptr) { fseek(sourceFile, 0, SEEK_END); auto const fsize = ftell(sourceFile); char *buffer = (char *)calloc(fsize + 1, sizeof(char)); fseek(sourceFile, 0, SEEK_SET); auto const freaded = fread(buffer, sizeof(char), fsize, sourceFile); buffer[freaded] = 0; // z jakiegos powodu czytamy troche mniej i trzczeba dodac konczace // zero do bufora (mimo ze calloc teoretycznie powiniene zwrocic // wyzerowana pamiec) #ifdef _PY_INT_MORE_LOG char buf[255]; sprintf(buf, "readed %d / %d characters for %s", freaded, fsize, className); WriteLog(buf); #endif // _PY_INT_MORE_LOG fclose(sourceFile); if (PyRun_SimpleString(buffer) != 0) { handleError(); return false; } _classes.insert( className ); /* char *classNameToRemember = (char *)calloc(strlen(className) + 1, sizeof(char)); strcpy(classNameToRemember, className); _classes.insert(classNameToRemember); */ free(buffer); return true; } return false; } return true; } PyObject *TPythonInterpreter::newClass( std::string const &className ) { return newClass(className, NULL); } FILE *TPythonInterpreter::_getFile( std::string const &lookupPath, std::string const &className ) { if( false == lookupPath.empty() ) { std::string const sourcefilepath = lookupPath + className + ".py"; FILE *file = fopen( sourcefilepath.c_str(), "r" ); #ifdef _PY_INT_MORE_LOG WriteLog( sourceFilePath ); #endif // _PY_INT_MORE_LOG if( nullptr != file ) { return file; } } std::string sourcefilepath = "python/local/" + className + ".py"; FILE *file = fopen( sourcefilepath.c_str(), "r" ); #ifdef _PY_INT_MORE_LOG WriteLog( sourceFilePath ); #endif // _PY_INT_MORE_LOG return file; // either the file, or a nullptr on fail /* char *sourceFilePath; if (lookupPath != NULL) { sourceFilePath = (char *)calloc(strlen(lookupPath) + strlen(className) + 4, sizeof(char)); strcat(sourceFilePath, lookupPath); strcat(sourceFilePath, className); strcat(sourceFilePath, ".py"); FILE *file = fopen(sourceFilePath, "r"); #ifdef _PY_INT_MORE_LOG WriteLog(sourceFilePath); #endif // _PY_INT_MORE_LOG free(sourceFilePath); if (file != NULL) { return file; } } char *basePath = "python/local/"; sourceFilePath = (char *)calloc(strlen(basePath) + strlen(className) + 4, sizeof(char)); strcat(sourceFilePath, basePath); strcat(sourceFilePath, className); strcat(sourceFilePath, ".py"); FILE *file = fopen(sourceFilePath, "r"); #ifdef _PY_INT_MORE_LOG WriteLog(sourceFilePath); #endif // _PY_INT_MORE_LOG free(sourceFilePath); if (file != NULL) { return file; } return NULL; */ } void TPythonInterpreter::handleError() { #ifdef _PY_INT_MORE_LOG WriteLog("Python Error occured"); #endif // _PY_INT_MORE_LOG if (_stdErr != NULL) { // std err pythona jest buforowane PyErr_Print(); PyObject *bufferContent = PyObject_CallMethod(_stdErr, "getvalue", NULL); PyObject_CallMethod(_stdErr, "truncate", "i", 0); // czyscimy bufor na kolejne bledy WriteLog(PyString_AsString(bufferContent)); } else { // nie dziala buffor pythona if (PyErr_Occurred() != NULL) { PyObject *ptype, *pvalue, *ptraceback; PyErr_Fetch(&ptype, &pvalue, &ptraceback); if (ptype == NULL) { WriteLog("Don't konw how to handle NULL exception"); } PyErr_NormalizeException(&ptype, &pvalue, &ptraceback); if (ptype == NULL) { WriteLog("Don't konw how to handle NULL exception"); } PyObject *pStrType = PyObject_Str(ptype); if (pStrType != NULL) { WriteLog(PyString_AsString(pStrType)); } WriteLog(PyString_AsString(pvalue)); PyObject *pStrTraceback = PyObject_Str(ptraceback); if (pStrTraceback != NULL) { WriteLog(PyString_AsString(pStrTraceback)); } else { WriteLog("Python Traceback cannot be shown"); } } else { #ifdef _PY_INT_MORE_LOG WriteLog("Called python error handler when no error occured!"); #endif // _PY_INT_MORE_LOG } } } PyObject *TPythonInterpreter::newClass(std::string const &className, PyObject *argsTuple) { if (_main == NULL) { WriteLog("main turned into null"); return NULL; } PyObject *classNameObj = PyObject_GetAttrString(_main, className.c_str()); if (classNameObj == NULL) { #ifdef _PY_INT_MORE_LOG char buf[255]; sprintf(buf, "Python class %s not defined!", className); WriteLog(buf); #endif // _PY_INT_MORE_LOG return NULL; } PyObject *object = PyObject_CallObject(classNameObj, argsTuple); if (PyErr_Occurred() != NULL) { handleError(); return NULL; } return object; } TPythonScreenRenderer::TPythonScreenRenderer(int textureId, PyObject *renderer) { _textureId = textureId; _pyRenderer = renderer; } void TPythonScreenRenderer::updateTexture() { int width, height; if (_pyWidth == NULL || _pyHeight == NULL) { WriteLog("Unknown python texture size!"); return; } width = PyInt_AsLong(_pyWidth); height = PyInt_AsLong(_pyHeight); if (_pyTexture != NULL) { char *textureData = PyString_AsString(_pyTexture); if (textureData != NULL) { #ifdef _PY_INT_MORE_LOG char buff[255]; sprintf(buff, "Sending texture id: %d w: %d h: %d", _textureId, width, height); WriteLog(buff); #endif // _PY_INT_MORE_LOG /* glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glPixelStorei( GL_PACK_ALIGNMENT, 1 ); */ GfxRenderer.Bind_Material(_textureId); // setup texture parameters glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 ); // build texture glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData ); #ifdef _PY_INT_MORE_LOG GLenum status = glGetError(); switch (status) { case GL_INVALID_ENUM: WriteLog("An unacceptable value is specified for an enumerated argument. The " "offending function is ignored, having no side effect other than to set " "the error flag."); break; case GL_INVALID_VALUE: WriteLog("A numeric argument is out of range. The offending function is ignored, " "having no side effect other than to set the error flag."); break; case GL_INVALID_OPERATION: WriteLog("The specified operation is not allowed in the current state. The " "offending function is ignored, having no side effect other than to set " "the error flag."); break; case GL_NO_ERROR: WriteLog("No error has been recorded. The value of this symbolic constant is " "guaranteed to be zero."); break; case GL_STACK_OVERFLOW: WriteLog("This function would cause a stack overflow. The offending function is " "ignored, having no side effect other than to set the error flag."); break; case GL_STACK_UNDERFLOW: WriteLog("This function would cause a stack underflow. The offending function is " "ignored, having no side effect other than to set the error flag."); break; case GL_OUT_OF_MEMORY: WriteLog("There is not enough memory left to execute the function. The state of " "OpenGL is undefined, except for the state of the error flags, after this " "error is recorded."); break; }; #endif // _PY_INT_MORE_LOG } else { WriteLog("RAW python texture data is NULL!"); } } else { WriteLog("Python texture object is NULL!"); } } void TPythonScreenRenderer::render(PyObject *trainState) { #ifdef _PY_INT_MORE_LOG WriteLog("Python rendering texture ..."); #endif // _PY_INT_MORE_LOG _pyTexture = PyObject_CallMethod(_pyRenderer, "render", "O", trainState); if (_pyTexture == NULL) { TPythonInterpreter::getInstance()->handleError(); } else { _pyWidth = PyObject_CallMethod(_pyRenderer, "get_width", NULL); if (_pyWidth == NULL) { TPythonInterpreter::getInstance()->handleError(); } _pyHeight = PyObject_CallMethod(_pyRenderer, "get_height", NULL); if (_pyHeight == NULL) { TPythonInterpreter::getInstance()->handleError(); } } } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif TPythonScreenRenderer::~TPythonScreenRenderer() { #ifdef _PY_INT_MORE_LOG WriteLog("PythonScreenRenderer descturctor called"); #endif // _PY_INT_MORE_LOG if (_pyRenderer != NULL) { Py_CLEAR(_pyRenderer); } cleanup(); #ifdef _PY_INT_MORE_LOG WriteLog("PythonScreenRenderer desctructor finished"); #endif // _PY_INT_MORE_LOG } void TPythonScreenRenderer::cleanup() { if (_pyTexture != NULL) { Py_CLEAR(_pyTexture); _pyTexture = NULL; } if (_pyWidth != NULL) { Py_CLEAR(_pyWidth); _pyWidth = NULL; } if (_pyHeight != NULL) { Py_CLEAR(_pyHeight); _pyHeight = NULL; } } void TPythonScreens::reset(void *train) { _terminationFlag = true; if (_thread != NULL) { // WriteLog("Awaiting python thread to end"); _thread->join(); delete _thread; _thread = nullptr; } _terminationFlag = false; _cleanupReadyFlag = false; _renderReadyFlag = false; for (std::vector::iterator i = _screens.begin(); i != _screens.end(); ++i) { delete *i; } #ifdef _PY_INT_MORE_LOG WriteLog("Clearing renderer vector"); #endif // _PY_INT_MORE_LOG _screens.clear(); _train = train; } void TPythonScreens::init(cParser &parser, TModel3d *model, std::string const &name, int const cab) { std::string asSubModelName, asPyClassName; parser.getTokens( 2, false ); parser >> asSubModelName >> asPyClassName; std::string subModelName = ToLower( asSubModelName ); std::string pyClassName = ToLower( asPyClassName ); TSubModel *subModel = model->GetFromName(subModelName); if (subModel == NULL) { WriteLog( "Python Screen: submodel " + subModelName + " not found - Ignoring screen" ); return; // nie ma takiego sub modelu w danej kabinie pomijamy } auto textureId = subModel->GetMaterial(); if (textureId <= 0) { WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" ); return; // sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana } TPythonInterpreter *python = TPythonInterpreter::getInstance(); python->loadClassFile(_lookupPath, pyClassName); PyObject *args = Py_BuildValue("(ssi)", _lookupPath.c_str(), name.c_str(), cab); if (args == NULL) { WriteLog("Python Screen: cannot create __init__ arguments"); return; } PyObject *pyRenderer = python->newClass(pyClassName, args); Py_CLEAR(args); if (pyRenderer == NULL) { WriteLog( "Python Screen: null renderer for " + pyClassName + " - Ignoring screen" ); return; // nie mozna utworzyc obiektu Pythonowego } TPythonScreenRenderer *renderer = new TPythonScreenRenderer(textureId, pyRenderer); _screens.push_back(renderer); WriteLog( "Created python screen " + pyClassName + " on submodel " + subModelName + " (" + std::to_string(textureId) + ")" ); } void TPythonScreens::update() { if (!_renderReadyFlag) { return; } _renderReadyFlag = false; for (std::vector::iterator i = _screens.begin(); i != _screens.end(); ++i) { (*i)->updateTexture(); } _cleanupReadyFlag = true; } void TPythonScreens::setLookupPath(std::string const &path) { _lookupPath = path; std::replace(_lookupPath.begin(), _lookupPath.end(), '\\', '/'); } TPythonScreens::TPythonScreens() { TPythonInterpreter::getInstance()->loadClassFile("", "abstractscreenrenderer"); _terminationFlag = false; _renderReadyFlag = false; _cleanupReadyFlag = false; _thread = NULL; } TPythonScreens::~TPythonScreens() { #ifdef _PY_INT_MORE_LOG WriteLog("Called python sceeens destructor"); #endif // _PY_INT_MORE_LOG reset(NULL); /* if (_lookupPath != NULL) { #ifdef _PY_INT_MORE_LOG WriteLog("Freeing lookup path"); #endif // _PY_INT_MORE_LOG free(_lookupPath); } */ } void TPythonScreens::run() { while (1) { if (_terminationFlag) { return; } TTrain *train = (TTrain *)_train; _trainState = train->GetTrainState(); if (_terminationFlag) { _freeTrainState(); return; } for (std::vector::iterator i = _screens.begin(); i != _screens.end(); ++i) { (*i)->render(_trainState); } _freeTrainState(); if (_terminationFlag) { _cleanup(); return; } _renderReadyFlag = true; while (!_cleanupReadyFlag && !_terminationFlag) { #ifdef _WIN32 Sleep(100); #elif __linux__ usleep(100*1000); #endif } if (_terminationFlag) { return; } _cleanup(); } } void TPythonScreens::finish() { _thread = NULL; } void ScreenRendererThread(TPythonScreens* renderer) { renderer->run(); renderer->finish(); #ifdef _PY_INT_MORE_LOG WriteLog("Python Screen Renderer Thread Ends"); #endif // _PY_INT_MORE_LOG } void TPythonScreens::start() { if (_screens.size() > 0) { _thread = new std::thread(ScreenRendererThread, this); } } void TPythonScreens::_cleanup() { _cleanupReadyFlag = false; for (std::vector::iterator i = _screens.begin(); i != _screens.end(); ++i) { (*i)->cleanup(); } } void TPythonScreens::_freeTrainState() { if (_trainState != NULL) { PyDict_Clear(_trainState); Py_CLEAR(_trainState); _trainState = NULL; } }