From 11e79f52ae6996cdc066c734779835bad1975a6f Mon Sep 17 00:00:00 2001 From: Hirek Date: Sun, 18 Jan 2026 21:21:15 +0100 Subject: [PATCH] Conversion to cpp 20 --- CMakeLists.txt | 2 +- EU07.cpp | 59 ++-- PyInt.cpp | 669 ++++++++++++++++++++----------------- PyInt.h | 131 ++++---- application.cpp | 14 +- driveruipanels.cpp | 20 +- parser.cpp | 683 +++++++++++++++++++++----------------- utilities.cpp | 6 +- widgets/trainingcard.cpp | 63 ++-- widgets/vehicleparams.cpp | 275 +++++++-------- 10 files changed, 1014 insertions(+), 908 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0eef1607..a776a8d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ set(VERSION_PATCH ${VERSION_DAY}) set(VERSION_STRING "${VERSION_YEAR}.${VERSION_MONTH}.${VERSION_DAY}") -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) file(GLOB HEADERS "*.h" "Console/*.h" diff --git a/EU07.cpp b/EU07.cpp index 5493359d..e4dba5ad 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -77,41 +77,54 @@ LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ExceptionInfo) return EXCEPTION_EXECUTE_HANDLER; } - #endif - int main(int argc, char *argv[]) { #ifdef WITHDUMPGEN #ifdef _WIN32 SetUnhandledExceptionFilter(CrashHandler); - #endif +#endif #endif // init start timestamp Global.startTimestamp = std::chrono::steady_clock::now(); - // quick short-circuit for standalone e3d export - if (argc == 6 && std::string(argv[1]) == "-e3d") { - std::string in(argv[2]); - std::string out(argv[3]); - int flags = std::stoi(std::string(argv[4])); - int dynamic = std::stoi(std::string(argv[5])); - export_e3d_standalone(in, out, flags, dynamic); - } else { - try { - auto result { Application.init( argc, argv ) }; - if( result == 0 ) { - result = Application.run(); - Application.exit(); - } - } catch( std::bad_alloc const &Error ) { - ErrorLog( "Critical error, memory allocation failure: " + std::string( Error.what() ) ); - } - } + // quick short-circuit for standalone e3d export + if (argc == 6 && std::string(argv[1]) == "-e3d") + { + std::string in(argv[2]); + std::string out(argv[3]); + int flags = std::stoi(std::string(argv[4])); + int dynamic = std::stoi(std::string(argv[5])); + export_e3d_standalone(in, out, flags, dynamic); + } + else + { + try + { + auto result{Application.init(argc, argv)}; + if (result == 0) + { + result = Application.run(); + Application.exit(); + } + } + catch (std::bad_alloc const &Error) + { + ErrorLog("Critical error, memory allocation failure: " + std::string(Error.what())); + } +#ifdef _WIN32 + catch (std::runtime_error const &Error) + { + std::string msg = "Simulator crash occured :(\n"; + msg += Error.what(); + MessageBoxA(nullptr, msg.c_str(), "Simulator crashed :(", MB_ICONERROR); + } +#endif + } #ifndef _WIN32 - fflush(stdout); - fflush(stderr); + fflush(stdout); + fflush(stderr); #endif std::_Exit(0); // skip destructors, there are ordering errors which causes segfaults } diff --git a/PyInt.cpp b/PyInt.cpp index f5e60331..92b66ded 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -20,53 +20,76 @@ http://mozilla.org/MPL/2.0/. #endif #include -void render_task::run() { +void render_task::run() +{ - // convert provided input to a python dictionary - auto *input = PyDict_New(); - if (input == nullptr) { + // convert provided input to a python dictionary + auto *input = PyDict_New(); + if (input == nullptr) + { cancel(); return; } - for( auto const &datapair : m_input->floats ) { auto *value{ PyGetFloat( datapair.second ) }; PyDict_SetItemString( input, datapair.first.c_str(), value ); Py_DECREF( value ); } - for( auto const &datapair : m_input->integers ) { auto *value{ PyGetInt( datapair.second ) }; PyDict_SetItemString( input, datapair.first.c_str(), value ); Py_DECREF( value ); } - for( auto const &datapair : m_input->bools ) { auto *value{ PyGetBool( datapair.second ) }; PyDict_SetItemString( input, datapair.first.c_str(), value ); } - for( auto const &datapair : m_input->strings ) { auto *value{ PyGetString( datapair.second.c_str() ) }; PyDict_SetItemString( input, datapair.first.c_str(), value ); Py_DECREF( value ); } - for (auto const &datapair : m_input->vec2_lists) { - PyObject *list = PyList_New(datapair.second.size()); + for (auto const &datapair : m_input->floats) + { + auto *value{PyGetFloat(datapair.second)}; + PyDict_SetItemString(input, datapair.first.c_str(), value); + Py_DECREF(value); + } + for (auto const &datapair : m_input->integers) + { + auto *value{PyGetInt(datapair.second)}; + PyDict_SetItemString(input, datapair.first.c_str(), value); + Py_DECREF(value); + } + for (auto const &datapair : m_input->bools) + { + auto *value{PyGetBool(datapair.second)}; + PyDict_SetItemString(input, datapair.first.c_str(), value); + } + for (auto const &datapair : m_input->strings) + { + auto *value{PyGetString(datapair.second.c_str())}; + PyDict_SetItemString(input, datapair.first.c_str(), value); + Py_DECREF(value); + } + for (auto const &datapair : m_input->vec2_lists) + { + PyObject *list = PyList_New(datapair.second.size()); - for (size_t i = 0; i < datapair.second.size(); i++) { - auto const &vec = datapair.second[i]; - WriteLog("passing " + glm::to_string(vec)); + for (size_t i = 0; i < datapair.second.size(); i++) + { + auto const &vec = datapair.second[i]; + WriteLog("passing " + glm::to_string(vec)); - PyObject *tuple = PyTuple_New(2); - PyTuple_SetItem(tuple, 0, PyGetFloat(vec.x)); // steals ref - PyTuple_SetItem(tuple, 1, PyGetFloat(vec.y)); // steals ref + PyObject *tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, PyGetFloat(vec.x)); // steals ref + PyTuple_SetItem(tuple, 1, PyGetFloat(vec.y)); // steals ref - PyList_SetItem(list, i, tuple); // steals ref - } + PyList_SetItem(list, i, tuple); // steals ref + } - PyDict_SetItemString(input, datapair.first.c_str(), list); - Py_DECREF(list); - } + PyDict_SetItemString(input, datapair.first.c_str(), list); + Py_DECREF(list); + } m_input = nullptr; - // call the renderer - auto *output { PyObject_CallMethod( m_renderer, "render", "O", input ) }; - Py_DECREF( input ); + // call the renderer + auto *output{PyObject_CallMethod(m_renderer, const_cast("render"), const_cast("O"), input)}; + Py_DECREF(input); - if( output != nullptr ) { - auto *outputwidth { PyObject_CallMethod( m_renderer, "get_width", nullptr ) }; - auto *outputheight { PyObject_CallMethod( m_renderer, "get_height", nullptr ) }; - // upload texture data - if( ( outputwidth != nullptr ) - && ( outputheight != nullptr ) - && m_target) { - int width = PyInt_AsLong( outputwidth ); - int height = PyInt_AsLong( outputheight ); + if (output != nullptr) + { + auto *outputwidth{PyObject_CallMethod(m_renderer, const_cast("get_width"), nullptr)}; + auto *outputheight{PyObject_CallMethod(m_renderer, const_cast("get_height"), nullptr)}; + // upload texture data + if ((outputwidth != nullptr) && (outputheight != nullptr) && m_target) + { + int width = PyInt_AsLong(outputwidth); + int height = PyInt_AsLong(outputheight); int components, format; - const unsigned char *image = reinterpret_cast( PyString_AsString( output ) ); + const unsigned char *image = reinterpret_cast(PyString_AsString(output)); std::lock_guard guard(m_target->mutex); @@ -101,16 +124,22 @@ void render_task::run() { m_target->components = components; m_target->format = format; m_target->timestamp = std::chrono::high_resolution_clock::now(); - } - if( outputheight != nullptr ) { Py_DECREF( outputheight ); } - if( outputwidth != nullptr ) { Py_DECREF( outputwidth ); } - Py_DECREF( output ); - } + } + if (outputheight != nullptr) + { + Py_DECREF(outputheight); + } + if (outputwidth != nullptr) + { + Py_DECREF(outputwidth); + } + Py_DECREF(output); + } - // get commands from renderer - auto *commandsPO = PyObject_CallMethod(m_renderer, "getCommands", nullptr); + // get commands from renderer + auto *commandsPO = PyObject_CallMethod(m_renderer, const_cast("getCommands"), nullptr); if (commandsPO != nullptr) - { + { std::vector commands = python_external_utils::PyObjectToStringArray(commandsPO); Py_DECREF(commandsPO); @@ -152,7 +181,8 @@ void render_task::run() { cd.param1 = p1; cd.param2 = p2; - WriteLog("Python: Executing command [" + baseCmd + "] with params: P1=" + std::to_string(p1) + " P2=" + std::to_string(p2) + " Target ID=" + std::to_string(simulation::Train->id())); + WriteLog("Python: Executing command [" + baseCmd + "] with params: P1=" + std::to_string(p1) + " P2=" + std::to_string(p2) + + " Target ID=" + std::to_string(simulation::Train->id())); simulation::Commands.push(cd, static_cast(command_target::vehicle) | simulation::Train->id()); } @@ -162,302 +192,317 @@ void render_task::run() { } } } - - - } - - - - + } } void render_task::upload() { if (Global.python_uploadmain && m_target && m_target->shared_tex) { - m_target->shared_tex->update_from_memory(m_target->width, m_target->height, reinterpret_cast(m_target->image.data())); - //glBindTexture(GL_TEXTURE_2D, m_target->shared_tex->get_id()); - //glTexImage2D( - // GL_TEXTURE_2D, 0, - // m_target->format, - // m_target->width, m_target->height, 0, - // m_target->components, GL_UNSIGNED_BYTE, m_target->image); - // - //if (Global.python_mipmaps) + m_target->shared_tex->update_from_memory(m_target->width, m_target->height, reinterpret_cast(m_target->image.data())); + // glBindTexture(GL_TEXTURE_2D, m_target->shared_tex->get_id()); + // glTexImage2D( + // GL_TEXTURE_2D, 0, + // m_target->format, + // m_target->width, m_target->height, 0, + // m_target->components, GL_UNSIGNED_BYTE, m_target->image); + // + // if (Global.python_mipmaps) //{ // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // glGenerateMipmap(GL_TEXTURE_2D); //} - //else + // else //{ // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //} - // - //if (Global.python_threadedupload) + // + // if (Global.python_threadedupload) // glFlush(); } } -void render_task::cancel() { - -} +void render_task::cancel() {} // initializes the module. returns true on success -auto python_taskqueue::init() -> bool { +auto python_taskqueue::init() -> bool +{ crashreport_add_info("python.threadedupload", Global.python_threadedupload ? "yes" : "no"); crashreport_add_info("python.uploadmain", Global.python_uploadmain ? "yes" : "no"); #ifdef _WIN32 - if (sizeof(void*) == 8) - Py_SetPythonHome("python64"); + if (sizeof(void *) == 8) + Py_SetPythonHome(const_cast("python64")); else - Py_SetPythonHome("python"); + Py_SetPythonHome(const_cast("python")); #elif __linux__ - if (sizeof(void*) == 8) - Py_SetPythonHome("linuxpython64"); + if (sizeof(void *) == 8) + Py_SetPythonHome(const_cast("linuxpython64")); else - Py_SetPythonHome("linuxpython"); + Py_SetPythonHome(const_cast("linuxpython")); #elif __APPLE__ - if (sizeof(void*) == 8) - Py_SetPythonHome("macpython64"); + if (sizeof(void *) == 8) + Py_SetPythonHome(const_cast("macpython64")); else - Py_SetPythonHome("macpython"); + Py_SetPythonHome(const_cast("macpython")); #endif - Py_InitializeEx(0); + Py_InitializeEx(0); - PyEval_InitThreads(); + PyEval_InitThreads(); - PyObject *stringiomodule { nullptr }; - PyObject *stringioclassname { nullptr }; - PyObject *stringioobject { nullptr }; + PyObject *stringiomodule{nullptr}; + PyObject *stringioclassname{nullptr}; + PyObject *stringioobject{nullptr}; - // do the setup work while we hold the lock - m_main = PyImport_ImportModule("__main__"); - if (m_main == nullptr) { - ErrorLog( "Python Interpreter: __main__ module is missing" ); - goto release_and_exit; - } + // do the setup work while we hold the lock + m_main = PyImport_ImportModule("__main__"); + if (m_main == nullptr) + { + ErrorLog("Python Interpreter: __main__ module is missing"); + goto release_and_exit; + } - stringiomodule = PyImport_ImportModule( "cStringIO" ); - stringioclassname = ( - stringiomodule != nullptr ? - PyObject_GetAttrString( stringiomodule, "StringIO" ) : - nullptr ); - stringioobject = ( - stringioclassname != nullptr ? - PyObject_CallObject( stringioclassname, nullptr ) : - nullptr ); - m_stderr = { ( - stringioobject == nullptr ? nullptr : - PySys_SetObject( "stderr", stringioobject ) != 0 ? nullptr : - stringioobject ) }; + stringiomodule = PyImport_ImportModule("cStringIO"); + stringioclassname = (stringiomodule != nullptr ? PyObject_GetAttrString(stringiomodule, "StringIO") : nullptr); + stringioobject = (stringioclassname != nullptr ? PyObject_CallObject(stringioclassname, nullptr) : nullptr); + m_stderr = {(stringioobject == nullptr ? nullptr : PySys_SetObject(const_cast("stderr"), stringioobject) != 0 ? nullptr : stringioobject)}; - if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; } + if (false == run_file("abstractscreenrenderer")) + { + goto release_and_exit; + } - // release the lock, save the state for future use - m_mainthread = PyEval_SaveThread(); + // release the lock, save the state for future use + m_mainthread = PyEval_SaveThread(); - WriteLog( "Python Interpreter: setup complete" ); + WriteLog("Python Interpreter: setup complete"); - // init workers - for( auto &worker : m_workers ) { + // init workers + for (auto &worker : m_workers) + { GLFWwindow *openglcontextwindow = nullptr; if (Global.python_threadedupload) - openglcontextwindow = Application.window( -1 ); - worker = std::thread( - &python_taskqueue::run, this, - openglcontextwindow, std::ref( m_tasks ), std::ref(m_uploadtasks), std::ref( m_condition ), std::ref( m_exit ) ); + openglcontextwindow = Application.window(-1); + worker = std::jthread(&python_taskqueue::run, this, openglcontextwindow, std::ref(m_tasks), std::ref(m_uploadtasks), std::ref(m_condition), std::ref(m_exit)); - if( false == worker.joinable() ) { return false; } - } + if (false == worker.joinable()) + { + return false; + } + } - m_initialized = true; + m_initialized = true; - return true; + return true; release_and_exit: - PyEval_ReleaseLock(); - return false; + PyEval_ReleaseLock(); + return false; } // shuts down the module -void python_taskqueue::exit() { - if (!m_initialized) - return; +void python_taskqueue::exit() +{ + if (!m_initialized) + return; - // let the workers know we're done with them - m_exit = true; - m_condition.notify_all(); - // let them free up their shit before we proceed - for( auto &worker : m_workers ) { - if (worker.joinable()) - worker.join(); - } - // get rid of the leftover tasks - // with the workers dead we don't have to worry about concurrent access anymore - for( auto task : m_tasks.data ) { - task->cancel(); - } - // take a bow - acquire_lock(); - Py_Finalize(); + // let the workers know we're done with them + m_exit = true; + m_condition.notify_all(); + // let them free up their shit before we proceed + m_workers = {}; + // get rid of the leftover tasks + // with the workers dead we don't have to worry about concurrent access anymore + for (auto task : m_tasks.data) + { + task->cancel(); + } + // take a bow + acquire_lock(); + Py_Finalize(); } // adds specified task along with provided collection of data to the work queue. returns true on success -auto python_taskqueue::insert( task_request const &Task ) -> bool { +auto python_taskqueue::insert(task_request const &Task) -> bool +{ - if( !m_initialized - || ( false == Global.python_enabled ) - || ( Task.renderer.empty() ) - || ( Task.input == nullptr ) - || ( Task.target == 0 ) ) { return false; } + if (!m_initialized || (false == Global.python_enabled) || (Task.renderer.empty()) || (Task.input == nullptr) || (Task.target == 0)) + { + return false; + } - auto *renderer { fetch_renderer( Task.renderer ) }; - if( renderer == nullptr ) { return false; } + auto *renderer{fetch_renderer(Task.renderer)}; + if (renderer == nullptr) + { + return false; + } - auto newtask = std::make_shared( renderer, Task.input, Task.target ); - bool newtaskinserted { false }; - // acquire a lock on the task queue and add the new task - { - std::lock_guard lock( m_tasks.mutex ); - // check the task list for a pending request with the same target - for( auto &task : m_tasks.data ) { - if( task->target() == Task.target ) { - // replace pending task in the slot with the more recent one - task->cancel(); - task = newtask; - newtaskinserted = true; - break; - } - } - if( false == newtaskinserted ) { - m_tasks.data.emplace_back( newtask ); - } - } - // potentially wake a worker to handle the new task - m_condition.notify_one(); - // all done - return true; + auto newtask = std::make_shared(renderer, Task.input, Task.target); + bool newtaskinserted{false}; + // acquire a lock on the task queue and add the new task + { + std::lock_guard lock(m_tasks.mutex); + // check the task list for a pending request with the same target + for (auto &task : m_tasks.data) + { + if (task->target() == Task.target) + { + // replace pending task in the slot with the more recent one + task->cancel(); + task = newtask; + newtaskinserted = true; + break; + } + } + if (false == newtaskinserted) + { + m_tasks.data.emplace_back(newtask); + } + } + // potentially wake a worker to handle the new task + m_condition.notify_one(); + // all done + return true; } // executes python script stored in specified file. returns true on success -auto python_taskqueue::run_file( std::string const &File, std::string const &Path ) -> bool { +auto python_taskqueue::run_file(std::string const &File, std::string const &Path) -> bool +{ - auto const lookup { FileExists( { Path + File, "python/local/" + File }, { ".py" } ) }; - if( lookup.first.empty() ) { return false; } + auto const lookup{FileExists({Path + File, "python/local/" + File}, {".py"})}; + if (lookup.first.empty()) + { + return false; + } - std::ifstream inputfile { lookup.first + lookup.second }; - std::string input; - input.assign( std::istreambuf_iterator( inputfile ), std::istreambuf_iterator() ); + std::ifstream inputfile{lookup.first + lookup.second}; + std::string input; + input.assign(std::istreambuf_iterator(inputfile), std::istreambuf_iterator()); - if( PyRun_SimpleString( input.c_str() ) != 0 ) { - error(); - return false; - } + if (PyRun_SimpleString(input.c_str()) != 0) + { + error(); + return false; + } - return true; + return true; } // acquires the python gil and sets the main thread as current -void python_taskqueue::acquire_lock() { +void python_taskqueue::acquire_lock() +{ - PyEval_RestoreThread( m_mainthread ); + PyEval_RestoreThread(m_mainthread); } // releases the python gil and swaps the main thread out -void python_taskqueue::release_lock() { +void python_taskqueue::release_lock() +{ - PyEval_SaveThread(); + PyEval_SaveThread(); } -auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * { +auto python_taskqueue::fetch_renderer(std::string const Renderer) -> PyObject * +{ - auto const lookup { m_renderers.find( Renderer ) }; - if( lookup != std::end( m_renderers ) ) { - return lookup->second; - } - // try to load specified renderer class - auto const path { substr_path( Renderer ) }; - auto const file { Renderer.substr( path.size() ) }; - PyObject *renderer { nullptr }; - PyObject *rendererarguments { nullptr }; - PyObject *renderername { nullptr }; - acquire_lock(); - { - if( m_main == nullptr ) { - ErrorLog( "Python Renderer: __main__ module is missing" ); - goto cache_and_return; - } + auto const lookup{m_renderers.find(Renderer)}; + if (lookup != std::end(m_renderers)) + { + return lookup->second; + } + // try to load specified renderer class + auto const path{substr_path(Renderer)}; + auto const file{Renderer.substr(path.size())}; + PyObject *renderer{nullptr}; + PyObject *rendererarguments{nullptr}; + PyObject *renderername{nullptr}; + acquire_lock(); + { + if (m_main == nullptr) + { + ErrorLog("Python Renderer: __main__ module is missing"); + goto cache_and_return; + } - if( false == run_file( file, path ) ) { - goto cache_and_return; - } - renderername = PyObject_GetAttrString( m_main, file.c_str() ); - if( renderername == nullptr ) { - ErrorLog( "Python Renderer: class \"" + file + "\" not defined" ); - goto cache_and_return; - } - rendererarguments = Py_BuildValue("(s)", path.c_str()); - if( rendererarguments == nullptr ) { - ErrorLog( "Python Renderer: failed to create initialization arguments" ); - goto cache_and_return; - } - renderer = PyObject_CallObject( renderername, rendererarguments ); + if (false == run_file(file, path)) + { + goto cache_and_return; + } + renderername = PyObject_GetAttrString(m_main, file.c_str()); + if (renderername == nullptr) + { + ErrorLog("Python Renderer: class \"" + file + "\" not defined"); + goto cache_and_return; + } + rendererarguments = Py_BuildValue("(s)", path.c_str()); + if (rendererarguments == nullptr) + { + ErrorLog("Python Renderer: failed to create initialization arguments"); + goto cache_and_return; + } + renderer = PyObject_CallObject(renderername, rendererarguments); - PyObject_CallMethod(renderer, "manul_set_format", "(s)", "RGBA"); + PyObject_CallMethod(renderer, const_cast("manul_set_format"), const_cast("(s)"), "RGBA"); - if( PyErr_Occurred() != nullptr ) { - error(); - renderer = nullptr; - } + if (PyErr_Occurred() != nullptr) + { + error(); + renderer = nullptr; + } -cache_and_return: - // clean up after yourself - if( rendererarguments != nullptr ) { - Py_DECREF( rendererarguments ); - } - } - release_lock(); - // cache the failures as well so we don't try again on subsequent requests - m_renderers.emplace( Renderer, renderer ); - return renderer; + cache_and_return: + // clean up after yourself + if (rendererarguments != nullptr) + { + Py_DECREF(rendererarguments); + } + } + release_lock(); + // cache the failures as well so we don't try again on subsequent requests + m_renderers.emplace(Renderer, renderer); + return renderer; } -void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit ) { +void python_taskqueue::run(GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit) +{ if (Context) - glfwMakeContextCurrent( Context ); + glfwMakeContextCurrent(Context); - // create a state object for this thread - PyEval_AcquireLock(); - auto *threadstate { PyThreadState_New( m_mainthread->interp ) }; - PyEval_ReleaseLock(); + // create a state object for this thread + PyEval_AcquireLock(); + auto *threadstate{PyThreadState_New(m_mainthread->interp)}; + PyEval_ReleaseLock(); - std::shared_ptr task { nullptr }; + std::shared_ptr task{nullptr}; - while( false == Exit.load() ) { - // regardless of the reason we woke up prime the spurious wakeup flag for the next time - Condition.spurious( true ); - // keep working as long as there's any scheduled tasks - do { - task = nullptr; - // acquire a lock on the task queue and potentially grab a task from it - { - std::lock_guard lock( Tasks.mutex ); - if( false == Tasks.data.empty() ) { - // fifo - task = Tasks.data.front(); - Tasks.data.pop_front(); - } - } - if( task != nullptr ) { - // swap in my thread state - PyEval_RestoreThread( threadstate ); - { - // execute python code - task->run(); + while (false == Exit.load()) + { + // regardless of the reason we woke up prime the spurious wakeup flag for the next time + Condition.spurious(true); + // keep working as long as there's any scheduled tasks + do + { + task = nullptr; + // acquire a lock on the task queue and potentially grab a task from it + { + std::lock_guard lock(Tasks.mutex); + if (false == Tasks.data.empty()) + { + // fifo + task = Tasks.data.front(); + Tasks.data.pop_front(); + } + } + if (task != nullptr) + { + // swap in my thread state + PyEval_RestoreThread(threadstate); + { + // execute python code + task->run(); if (Context) task->upload(); else @@ -465,24 +510,24 @@ void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, upl std::lock_guard lock(Upload_Tasks.mutex); Upload_Tasks.data.push_back(task); } - if( PyErr_Occurred() != nullptr ) + if (PyErr_Occurred() != nullptr) error(); - } - // clear the thread state - PyEval_SaveThread(); - } - // TBD, TODO: add some idle time between tasks in case we're on a single thread cpu? - } while( task != nullptr ); - // if there's nothing left to do wait until there is - // but check every now and then on your own to minimize potential deadlock situations - Condition.wait_for( std::chrono::seconds( 5 ) ); - } - // clean up thread state data - PyEval_AcquireLock(); - PyThreadState_Swap( nullptr ); - PyThreadState_Clear( threadstate ); - PyThreadState_Delete( threadstate ); - PyEval_ReleaseLock(); + } + // clear the thread state + PyEval_SaveThread(); + } + // TBD, TODO: add some idle time between tasks in case we're on a single thread cpu? + } while (task != nullptr); + // if there's nothing left to do wait until there is + // but check every now and then on your own to minimize potential deadlock situations + Condition.wait_for(std::chrono::seconds(5)); + } + // clean up thread state data + PyEval_AcquireLock(); + PyThreadState_Swap(nullptr); + PyThreadState_Clear(threadstate); + PyThreadState_Delete(threadstate); + PyEval_ReleaseLock(); } void python_taskqueue::update() @@ -495,46 +540,53 @@ void python_taskqueue::update() m_uploadtasks.data.clear(); } -void -python_taskqueue::error() { +void python_taskqueue::error() +{ - if( m_stderr != nullptr ) { - // std err pythona jest buforowane - PyErr_Print(); - auto *errortext { PyObject_CallMethod( m_stderr, "getvalue", nullptr ) }; - ErrorLog( PyString_AsString( errortext ) ); - // czyscimy bufor na kolejne bledy - PyObject_CallMethod( m_stderr, "truncate", "i", 0 ); - } - else { - // nie dziala buffor pythona - PyObject *type, *value, *traceback; - PyErr_Fetch( &type, &value, &traceback ); - if( type == nullptr ) { - ErrorLog( "Python Interpreter: don't know how to handle null exception" ); - } - PyErr_NormalizeException( &type, &value, &traceback ); - if( type == nullptr ) { - ErrorLog( "Python Interpreter: don't know how to handle null exception" ); - } - auto *typetext { PyObject_Str( type ) }; - if( typetext != nullptr ) { - ErrorLog( PyString_AsString( typetext ) ); - } - if( value != nullptr ) { - ErrorLog( PyString_AsString( value ) ); - } - auto *tracebacktext { PyObject_Str( traceback ) }; - if( tracebacktext != nullptr ) { - ErrorLog( PyString_AsString( tracebacktext ) ); - } - else { - WriteLog( "Python Interpreter: failed to retrieve the stack traceback" ); - } - } + if (m_stderr != nullptr) + { + // std err pythona jest buforowane + PyErr_Print(); + auto *errortext{PyObject_CallMethod(m_stderr, const_cast("getvalue"), nullptr)}; + ErrorLog(PyString_AsString(errortext)); + // czyscimy bufor na kolejne bledy + PyObject_CallMethod(m_stderr, const_cast("truncate"), const_cast("i"), 0); + } + else + { + // nie dziala buffor pythona + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + if (type == nullptr) + { + ErrorLog("Python Interpreter: don't know how to handle null exception"); + } + PyErr_NormalizeException(&type, &value, &traceback); + if (type == nullptr) + { + ErrorLog("Python Interpreter: don't know how to handle null exception"); + } + auto *typetext{PyObject_Str(type)}; + if (typetext != nullptr) + { + ErrorLog(PyString_AsString(typetext)); + } + if (value != nullptr) + { + ErrorLog(PyString_AsString(value)); + } + auto *tracebacktext{PyObject_Str(traceback)}; + if (tracebacktext != nullptr) + { + ErrorLog(PyString_AsString(tracebacktext)); + } + else + { + WriteLog("Python Interpreter: failed to retrieve the stack traceback"); + } + } } - std::vector python_external_utils::PyObjectToStringArray(PyObject *pyList) { std::vector result; @@ -570,7 +622,6 @@ std::vector python_external_utils::PyObjectToStringArray(PyObject * return result; } - #ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff --git a/PyInt.h b/PyInt.h index 69cc4f84..70341227 100644 --- a/PyInt.h +++ b/PyInt.h @@ -19,8 +19,8 @@ http://mozilla.org/MPL/2.0/. #endif #ifdef _MSC_VER -#pragma warning( push ) -#pragma warning( disable : 5033 ) +#pragma warning(push) +#pragma warning(disable : 5033) #endif #ifdef __GNUC__ #pragma GCC diagnostic push @@ -44,12 +44,13 @@ http://mozilla.org/MPL/2.0/. #pragma GCC diagnostic pop #endif #ifdef _MSC_VER -#pragma warning( pop ) +#pragma warning(pop) #endif #include "Classes.h" #include "utilities.h" #include "Texture.h" +#include #define PyGetFloat(param) PyFloat_FromDouble(param) #define PyGetInt(param) PyInt_FromLong(param) @@ -57,10 +58,11 @@ http://mozilla.org/MPL/2.0/. #define PyGetString(param) PyString_FromString(param) // python rendertarget -struct python_rt { +struct python_rt +{ std::mutex mutex; - ITexture* shared_tex; + ITexture *shared_tex; int format; int components; @@ -72,83 +74,86 @@ struct python_rt { }; // TODO: extract common base and inherit specialization from it -class render_task { +class render_task +{ -public: -// constructors - render_task( PyObject *Renderer, std::shared_ptr Input, std::shared_ptr Target ) : - m_renderer( Renderer ), m_input( Input ), m_target( Target ) - {} -// methods + public: + // constructors + render_task(PyObject *Renderer, std::shared_ptr Input, std::shared_ptr Target) : m_renderer(Renderer), m_input(Input), m_target(Target) {} + // methods void run(); void upload(); - void cancel(); - auto target() const -> std::shared_ptr { return m_target; } + void cancel(); + auto target() const -> std::shared_ptr + { + return m_target; + } -private: -// members - PyObject *m_renderer {nullptr}; - std::shared_ptr m_input{nullptr}; - std::shared_ptr m_target { nullptr }; + private: + // members + PyObject *m_renderer{nullptr}; + std::shared_ptr m_input{nullptr}; + std::shared_ptr m_target{nullptr}; }; -class python_taskqueue { +class python_taskqueue +{ -public: -// types - struct task_request { + public: + // types + struct task_request + { - std::string const &renderer; - std::shared_ptr input; + std::string const &renderer; + std::shared_ptr input; std::shared_ptr target; - }; -// constructors - python_taskqueue() = default; -// methods - // initializes the module. returns true on success - auto init() -> bool; - // shuts down the module - void exit(); - // adds specified task along with provided collection of data to the work queue. returns true on success - auto insert( task_request const &Task ) -> bool; - // executes python script stored in specified file. returns true on success - auto run_file( std::string const &File, std::string const &Path = "" ) -> bool; - // acquires the python gil and sets the main thread as current - void acquire_lock(); - // releases the python gil and swaps the main thread out - void release_lock(); + }; + // constructors + python_taskqueue() = default; + // methods + // initializes the module. returns true on success + auto init() -> bool; + // shuts down the module + void exit(); + // adds specified task along with provided collection of data to the work queue. returns true on success + auto insert(task_request const &Task) -> bool; + // executes python script stored in specified file. returns true on success + auto run_file(std::string const &File, std::string const &Path = "") -> bool; + // acquires the python gil and sets the main thread as current + void acquire_lock(); + // releases the python gil and swaps the main thread out + void release_lock(); void update(); -private: -// types - static int const WORKERCOUNT { 1 }; - using worker_array = std::array; - using rendertask_sequence = threading::lockable< std::deque> >; - using uploadtask_sequence = threading::lockable< std::deque> >; -// methods - auto fetch_renderer( std::string const Renderer ) -> PyObject *; - void run(GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit ); - void error(); + private: + // types + static int const WORKERCOUNT{1}; + using worker_array = std::array; + using rendertask_sequence = threading::lockable>>; + using uploadtask_sequence = threading::lockable>>; + // methods + auto fetch_renderer(std::string const Renderer) -> PyObject *; + void run(GLFWwindow *Context, rendertask_sequence &Tasks, uploadtask_sequence &Upload_Tasks, threading::condition_variable &Condition, std::atomic &Exit); + void error(); // members - PyObject *m_main { nullptr }; - PyObject *m_stderr { nullptr }; - PyThreadState *m_mainthread{ nullptr }; - worker_array m_workers; - threading::condition_variable m_condition; // wakes up the workers - std::atomic m_exit { false }; // signals the workers to quit - std::unordered_map m_renderers; // cache of python classes - rendertask_sequence m_tasks; + PyObject *m_main{nullptr}; + PyObject *m_stderr{nullptr}; + PyThreadState *m_mainthread{nullptr}; + worker_array m_workers; + threading::condition_variable m_condition; // wakes up the workers + std::atomic m_exit{false}; // signals the workers to quit + std::unordered_map m_renderers; // cache of python classes + rendertask_sequence m_tasks; uploadtask_sequence m_uploadtasks; - bool m_initialized { false }; + bool m_initialized{false}; }; class python_external_utils { -public: - static std::vector PyObjectToStringArray(PyObject *pyList); - + public: + static std::vector PyObjectToStringArray(PyObject *pyList); }; #endif diff --git a/application.cpp b/application.cpp index 54ce470d..d8741d54 100644 --- a/application.cpp +++ b/application.cpp @@ -135,24 +135,24 @@ int eu07_application::run_crashgui() if (Global.asLang == "pl") { - ImGui::Begin(u8"Raportowanie błędów", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize); - ImGui::TextUnformatted(u8"Podczas ostatniego uruchomienia symulatora wystąpił błąd.\nWysłać raport o błędzie do deweloperów?\n"); - ImGui::TextUnformatted((u8"Usługa udostępniana przez " + crashreport_get_provider() + "\n").c_str()); - y = ImGui::Button(u8"Tak", ImVec2S(60, 0)); + ImGui::Begin("Raportowanie błędów", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize); + ImGui::TextUnformatted("Podczas ostatniego uruchomienia symulatora wystąpił błąd.\nWysłać raport o błędzie do deweloperów?\n"); + ImGui::TextUnformatted(("Usługa udostępniana przez " + crashreport_get_provider() + "\n").c_str()); + y = ImGui::Button("Tak", ImVec2S(60, 0)); ImGui::SameLine(); - ImGui::Checkbox(u8"W przyszłości przesyłaj raporty o błędach automatycznie", &autoup); + ImGui::Checkbox("W przyszłości przesyłaj raporty o błędach automatycznie", &autoup); ImGui::SameLine(); ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::TextUnformatted(u8"W celu wyłączenia tej funkcji będzie trzeba skasować plik crashdumps/autoupload_enabled.conf"); + ImGui::TextUnformatted("W celu wyłączenia tej funkcji będzie trzeba skasować plik crashdumps/autoupload_enabled.conf"); ImGui::EndTooltip(); } ImGui::NewLine(); - n = ImGui::Button(u8"Nie", ImVec2S(60, 0)); + n = ImGui::Button("Nie", ImVec2S(60, 0)); ImGui::End(); } else diff --git a/driveruipanels.cpp b/driveruipanels.cpp index fbf5ee41..2eafcce3 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -391,7 +391,7 @@ timetable_panel::update() { } else { // header - m_tablelines.emplace_back( u8"┌─────┬────────────────────────────────────┬─────────┬─────┐", Global.UITextColor ); + m_tablelines.emplace_back( "┌─────┬────────────────────────────────────┬─────────┬─────┐", Global.UITextColor ); TMTableLine const *tableline; for( int i = table.StationStart; i <= table.StationCount; ++i ) { @@ -423,11 +423,11 @@ timetable_panel::update() { auto const arrival { ( tableline->Ah >= 0 ? to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Am, true, 3 ) : - u8" │ " ) }; + " │ " ) }; auto const departure { ( tableline->Dh >= 0 ? to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Dm, true, 3 ) : - u8" │ " ) }; + " │ " ) }; auto const candepart { ( ( table.StationStart < table.StationIndex ) && ( i < table.StationIndex ) @@ -446,25 +446,25 @@ timetable_panel::update() { candepart ? colors::uitextgreen : // czas minął i odjazd był, to nazwa stacji będzie na zielono isatpassengerstop ? colors::uitextorange : Global.UITextColor ) }; - auto const trackcount{ ( tableline->TrackNo == 1 ? u8" ┃ " : u8" ║ " ) }; + auto const trackcount{ ( tableline->TrackNo == 1 ? " ┃ " : " ║ " ) }; m_tablelines.emplace_back( - ( u8"│ " + vmax + u8" │ " + station + trackcount + arrival + u8" │ " + traveltime + u8" │" ), + ( "│ " + vmax + " │ " + station + trackcount + arrival + " │ " + traveltime + " │" ), linecolor ); m_tablelines.emplace_back( - ( u8"│ │ " + location + tableline->StationWare + trackcount + departure + u8" │ │" ), + ( "│ │ " + location + tableline->StationWare + trackcount + departure + " │ │" ), linecolor ); // divider/footer if( i < table.StationCount ) { auto const *nexttableline { tableline + 1 }; - std::string const vmaxnext{ ( tableline->vmax == nexttableline->vmax ? u8"│ ├" : u8"├─────┼" ) }; - auto const trackcountnext{ ( nexttableline->TrackNo == 1 ? u8"╂" : u8"╫" ) }; + std::string const vmaxnext{ ( tableline->vmax == nexttableline->vmax ? "│ ├" : "├─────┼" ) }; + auto const trackcountnext{ ( nexttableline->TrackNo == 1 ? "╂" : "╫" ) }; m_tablelines.emplace_back( - vmaxnext + u8"────────────────────────────────────" + trackcountnext + u8"─────────┼─────┤", + vmaxnext + "────────────────────────────────────" + trackcountnext + "─────────┼─────┤", Global.UITextColor ); } else { m_tablelines.emplace_back( - u8"└─────┴────────────────────────────────────┴─────────┴─────┘", + "└─────┴────────────────────────────────────┴─────────┴─────┘", Global.UITextColor ); } } diff --git a/parser.cpp b/parser.cpp index bbba302e..7afc1091 100644 --- a/parser.cpp +++ b/parser.cpp @@ -24,259 +24,283 @@ http://mozilla.org/MPL/2.0/. // cParser -- generic class for parsing text data. // constructors -cParser::cParser( std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector Parameters, bool allowRandom ) : - mPath(Path), - LoadTraction( Loadtraction ), - allowRandomIncludes(allowRandom) { - // store to calculate sub-sequent includes from relative path - if( Type == buffertype::buffer_FILE ) { - mFile = Stream; - } - // reset pointers and attach proper type of buffer - switch (Type) { - case buffer_FILE: { - Path.append( Stream ); - mStream = std::make_shared( Path, std::ios_base::binary ); - // content of *.inc files is potentially grouped together - if( ( Stream.size() >= 4 ) - && ( ToLower( Stream.substr( Stream.size() - 4 ) ) == ".inc" ) ) { - mIncFile = true; - scene::Groups.create(); - } - break; - } - case buffer_TEXT: { - mStream = std::make_shared( Stream ); - break; - } - default: { - break; - } - } - // calculate stream size - if (mStream) - { - if( true == mStream->fail() ) { - ErrorLog( "Failed to open file \"" + Path + "\"" ); - } - else { - mSize = mStream->rdbuf()->pubseekoff( 0, std::ios_base::end ); - mStream->rdbuf()->pubseekoff( 0, std::ios_base::beg ); - mLine = 1; - } - } - // set parameter set if one was provided - if( false == Parameters.empty() ) { - parameters.swap( Parameters ); - } +cParser::cParser(std::string const &Stream, buffertype const Type, std::string Path, bool const Loadtraction, std::vector Parameters, bool allowRandom) + : mPath(Path), LoadTraction(Loadtraction), allowRandomIncludes(allowRandom) +{ + // store to calculate sub-sequent includes from relative path + if (Type == buffertype::buffer_FILE) + { + mFile = Stream; + } + // reset pointers and attach proper type of buffer + switch (Type) + { + case buffer_FILE: + { + Path.append(Stream); + mStream = std::make_shared(Path, std::ios_base::binary); + // content of *.inc files is potentially grouped together + if ((Stream.size() >= 4) && (ToLower(Stream.substr(Stream.size() - 4)) == ".inc")) + { + mIncFile = true; + scene::Groups.create(); + } + break; + } + case buffer_TEXT: + { + mStream = std::make_shared(Stream); + break; + } + default: + { + break; + } + } + // calculate stream size + if (mStream) + { + if (true == mStream->fail()) + { + ErrorLog("Failed to open file \"" + Path + "\""); + } + else + { + mSize = mStream->rdbuf()->pubseekoff(0, std::ios_base::end); + mStream->rdbuf()->pubseekoff(0, std::ios_base::beg); + mLine = 1; + } + } + // set parameter set if one was provided + if (false == Parameters.empty()) + { + parameters.swap(Parameters); + } } // destructor -cParser::~cParser() { +cParser::~cParser() +{ - if( true == mIncFile ) { - // wrap up the node group holding content of processed file - scene::Groups.close(); - } + if (true == mIncFile) + { + // wrap up the node group holding content of processed file + scene::Groups.close(); + } } -template <> -glm::vec3 -cParser::getToken( bool const ToLower, char const *Break ) { - // NOTE: this specialization ignores default arguments - getTokens( 3, false, "\n\r\t ,;[]" ); - glm::vec3 output; - *this - >> output.x - >> output.y - >> output.z; - return output; +template <> glm::vec3 cParser::getToken(bool const ToLower, char const *Break) +{ + // NOTE: this specialization ignores default arguments + getTokens(3, false, "\n\r\t ,;[]"); + glm::vec3 output; + *this >> output.x >> output.y >> output.z; + return output; }; -template<> -cParser& -cParser::operator>>( std::string &Right ) { +template <> cParser &cParser::operator>>(std::string &Right) +{ - if( true == this->tokens.empty() ) { return *this; } + if (true == this->tokens.empty()) + { + return *this; + } - Right = this->tokens.front(); - this->tokens.pop_front(); + Right = this->tokens.front(); + this->tokens.pop_front(); - return *this; + return *this; } -template<> -cParser& -cParser::operator>>( bool &Right ) { +template <> cParser &cParser::operator>>(bool &Right) +{ - if( true == this->tokens.empty() ) { return *this; } + if (true == this->tokens.empty()) + { + return *this; + } - Right = ( ( this->tokens.front() == "true" ) - || ( this->tokens.front() == "yes" ) - || ( this->tokens.front() == "1" ) ); - this->tokens.pop_front(); + Right = ((this->tokens.front() == "true") || (this->tokens.front() == "yes") || (this->tokens.front() == "1")); + this->tokens.pop_front(); - return *this; + return *this; } -template <> -bool -cParser::getToken( bool const ToLower, const char *Break ) { +template <> bool cParser::getToken(bool const ToLower, const char *Break) +{ - auto const token = getToken( true, Break ); - return ( ( token == "true" ) - || ( token == "yes" ) - || ( token == "1" ) ); + auto const token = getToken(true, Break); + return ((token == "true") || (token == "yes") || (token == "1")); } // methods -cParser & -cParser::autoclear( bool const Autoclear ) { +cParser &cParser::autoclear(bool const Autoclear) +{ - m_autoclear = Autoclear; - if( mIncludeParser ) { mIncludeParser->autoclear( Autoclear ); } + m_autoclear = Autoclear; + if (mIncludeParser) + { + mIncludeParser->autoclear(Autoclear); + } - return *this; + return *this; } bool cParser::getTokens(unsigned int Count, bool ToLower, const char *Break) { - if( true == m_autoclear ) { - // legacy parser behaviour - tokens.clear(); - } - /* - if (LoadTraction==true) - trtest="niemaproblema"; //wczytywać - else - trtest="x"; //nie wczytywać - */ -/* - int i; - this->str(""); - this->clear(); -*/ - for (unsigned int i = tokens.size(); i < Count; ++i) - { - std::string token = readToken(ToLower, Break); - if( true == token.empty() ) { - // no more tokens - break; - } - // collect parameters - tokens.emplace_back( token ); -/* - if (i == 0) - this->str(token); - else - { - std::string temp = this->str(); - temp.append("\n"); - temp.append(token); - this->str(temp); - } -*/ - } - if (tokens.size() < Count) - return false; - else - return true; + if (true == m_autoclear) + { + // legacy parser behaviour + tokens.clear(); + } + /* + if (LoadTraction==true) + trtest="niemaproblema"; //wczytywać + else + trtest="x"; //nie wczytywać + */ + /* + int i; + this->str(""); + this->clear(); + */ + for (unsigned int i = tokens.size(); i < Count; ++i) + { + std::string token = readToken(ToLower, Break); + if (true == token.empty()) + { + // no more tokens + break; + } + // collect parameters + tokens.emplace_back(token); + /* + if (i == 0) + this->str(token); + else + { + std::string temp = this->str(); + temp.append("\n"); + temp.append(token); + this->str(temp); + } + */ + } + if (tokens.size() < Count) + return false; + else + return true; } -std::string cParser::readToken( bool ToLower, const char *Break ) { +std::string cParser::readToken(bool ToLower, const char *Break) +{ - std::string token; - if( mIncludeParser ) { - // see if there's include parsing going on. clean up when it's done. - token = mIncludeParser->readToken( ToLower, Break ); - if( true == token.empty() ) { - mIncludeParser = nullptr; - } - } - if( true == token.empty() ) { - // get the token yourself if the delegation attempt failed - char c { 0 }; - do { - while( mStream->peek() != EOF && strchr( Break, c = mStream->get() ) == NULL ) { - if( ToLower ) - c = tolower( c ); - token += c; - if( findQuotes( token ) ) // do glue together words enclosed in quotes - continue; - if( skipComments && trimComments( token ) ) // don't glue together words separated with comment - break; - } - if( c == '\n' ) { - // update line counter - ++mLine; - } - } while( token == "" && mStream->peek() != EOF ); // double check in case of consecutive separators - } - // check the first token for potential presence of utf bom - if( mFirstToken ) { - mFirstToken = false; - if( token.rfind( "\xef\xbb\xbf", 0 ) == 0 ) { - token.erase( 0, 3 ); - } - if( true == token.empty() ) { - // potentially possible if our first token was standalone utf bom - token = readToken( ToLower, Break ); - } - } + std::string token; + if (mIncludeParser) + { + // see if there's include parsing going on. clean up when it's done. + token = mIncludeParser->readToken(ToLower, Break); + if (true == token.empty()) + { + mIncludeParser = nullptr; + } + } + if (true == token.empty()) + { + // get the token yourself if the delegation attempt failed + char c{0}; + do + { + while (mStream->peek() != EOF && strchr(Break, c = mStream->get()) == NULL) + { + if (ToLower) + c = tolower(c); + token += c; + if (findQuotes(token)) // do glue together words enclosed in quotes + continue; + if (skipComments && trimComments(token)) // don't glue together words separated with comment + break; + } + if (c == '\n') + { + // update line counter + ++mLine; + } + } while (token == "" && mStream->peek() != EOF); // double check in case of consecutive separators + } + // check the first token for potential presence of utf bom + if (mFirstToken) + { + mFirstToken = false; + if (token.rfind("\xef\xbb\xbf", 0) == 0) + { + token.erase(0, 3); + } + if (true == token.empty()) + { + // potentially possible if our first token was standalone utf bom + token = readToken(ToLower, Break); + } + } - if( false == parameters.empty() ) { - // if there's parameter list, check the token for potential parameters to replace - size_t pos; // początek podmienianego ciągu - while( ( pos = token.find( "(p" ) ) != std::string::npos ) { - // check if the token is a parameter which should be replaced with stored true value - auto const parameter{ token.substr( pos + 2, token.find( ")", pos ) - ( pos + 2 ) ) }; // numer parametru - token.erase( pos, token.find( ")", pos ) - pos + 1 ); // najpierw usunięcie "(pN)" - size_t nr = atoi( parameter.c_str() ) - 1; - if( nr < parameters.size() ) { - token.insert( pos, parameters.at( nr ) ); // wklejenie wartości parametru - if( ToLower ) - for( ; pos < parameters.at( nr ).size(); ++pos ) - token[ pos ] = tolower( token[ pos ] ); - } - else - token.insert( pos, "none" ); // zabezpieczenie przed brakiem parametru - } - } + if (false == parameters.empty()) + { + // if there's parameter list, check the token for potential parameters to replace + size_t pos; // początek podmienianego ciągu + while ((pos = token.find("(p")) != std::string::npos) + { + // check if the token is a parameter which should be replaced with stored true value + auto const parameter{token.substr(pos + 2, token.find(")", pos) - (pos + 2))}; // numer parametru + token.erase(pos, token.find(")", pos) - pos + 1); // najpierw usunięcie "(pN)" + size_t nr = atoi(parameter.c_str()) - 1; + if (nr < parameters.size()) + { + token.insert(pos, parameters.at(nr)); // wklejenie wartości parametru + if (ToLower) + for (; pos < parameters.at(nr).size(); ++pos) + token[pos] = tolower(token[pos]); + } + else + token.insert(pos, "none"); // zabezpieczenie przed brakiem parametru + } + } - // launch child parser if include directive found. - // NOTE: parameter collecting uses default set of token separators. - if( expandIncludes && token == "include" ) { + // launch child parser if include directive found. + // NOTE: parameter collecting uses default set of token separators. + if (expandIncludes && token == "include") + { std::string includefile = allowRandomIncludes ? deserialize_random_set(*this) : readToken(ToLower); // nazwa pliku replace_slashes(includefile); - if ((true == LoadTraction) || - ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) + if ((true == LoadTraction) || ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) { if (false == contains(includefile, "_ter.scm")) { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { // WriteLog("including: " + includefile); } mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(*this)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); - if (mIncludeParser->mSize <= 0) { + if (mIncludeParser->mSize <= 0) + { ErrorLog("Bad include: can't open file \"" + includefile + "\""); } } else { - if(true == Global.file_binary_terrain_state) + if (true == Global.file_binary_terrain_state) { WriteLog("SBT found, ignoring: " + includefile); readParameters(*this); } else { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { WriteLog("including terrain: " + includefile); } - mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, - LoadTraction, readParameters(*this)); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(*this)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); if (mIncludeParser->mSize <= 0) @@ -285,29 +309,31 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { } } } - } - else { - while( token != "end" ) { - token = readToken( true ); // minimize risk of case mismatch on comparison - } - } - token = readToken(ToLower, Break); - } - else if( ( std::strcmp( Break, "\n\r" ) == 0 ) && ( token.compare( 0, 7, "include" ) == 0 ) ) { - // HACK: if the parser reads full lines we expect this line to contain entire include directive, to make parsing easier - cParser includeparser( token.substr( 7 ) ); - std::string includefile = allowRandomIncludes ? deserialize_random_set( includeparser ) : includeparser.readToken( ToLower ); // nazwa pliku - replace_slashes(includefile); - if ((true == LoadTraction) || - ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) + } + else + { + while (token != "end") + { + token = readToken(true); // minimize risk of case mismatch on comparison + } + } + token = readToken(ToLower, Break); + } + else if ((std::strcmp(Break, "\n\r") == 0) && (token.compare(0, 7, "include") == 0)) + { + // HACK: if the parser reads full lines we expect this line to contain entire include directive, to make parsing easier + cParser includeparser(token.substr(7)); + std::string includefile = allowRandomIncludes ? deserialize_random_set(includeparser) : includeparser.readToken(ToLower); // nazwa pliku + replace_slashes(includefile); + if ((true == LoadTraction) || ((false == contains(includefile, "tr/")) && (false == contains(includefile, "tra/")))) { if (false == contains(includefile, "_ter.scm")) { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { // WriteLog("including: " + includefile); } - mIncludeParser = std::make_shared( - includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); if (mIncludeParser->mSize <= 0) @@ -324,52 +350,56 @@ std::string cParser::readToken( bool ToLower, const char *Break ) { } else { - if (Global.ParserLogIncludes) { + if (Global.ParserLogIncludes) + { WriteLog("including terrain: " + includefile); } - mIncludeParser = - std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, - readParameters(includeparser)); + mIncludeParser = std::make_shared(includefile, buffer_FILE, mPath, LoadTraction, readParameters(includeparser)); mIncludeParser->allowRandomIncludes = allowRandomIncludes; mIncludeParser->autoclear(m_autoclear); if (mIncludeParser->mSize <= 0) { ErrorLog("Bad include: can't open file \"" + includefile + "\""); } - - } + } } } - token = readToken( ToLower, Break ); - } - // all done - return token; + token = readToken(ToLower, Break); + } + // all done + return token; } -std::vector cParser::readParameters( cParser &Input ) { +std::vector cParser::readParameters(cParser &Input) +{ - std::vector includeparameters; - std::string parameter = Input.readToken( false ); // w parametrach nie zmniejszamy - while( ( parameter.empty() == false ) - && ( parameter != "end" ) ) { - includeparameters.emplace_back( parameter ); - parameter = Input.readToken( false ); - } - return includeparameters; + std::vector includeparameters; + std::string parameter = Input.readToken(false); // w parametrach nie zmniejszamy + while ((parameter.empty() == false) && (parameter != "end")) + { + includeparameters.emplace_back(parameter); + parameter = Input.readToken(false); + } + return includeparameters; } -std::string cParser::readQuotes(char const Quote) { // read the stream until specified char or stream end - std::string token = ""; - char c { 0 }; +std::string cParser::readQuotes(char const Quote) +{ // read the stream until specified char or stream end + std::string token = ""; + char c{0}; bool escaped = false; - while( mStream->peek() != EOF ) { // get all chars until the quote mark + while (mStream->peek() != EOF) + { // get all chars until the quote mark c = mStream->get(); - if (escaped) { + if (escaped) + { escaped = false; } - else { - if (c == '\\') { + else + { + if (c == '\\') + { escaped = true; continue; } @@ -380,122 +410,153 @@ std::string cParser::readQuotes(char const Quote) { // read the stream until spe if (c == '\n') ++mLine; // update line counter token += c; - } + } return token; } -void cParser::skipComment( std::string const &Endmark ) { // pobieranie znaków aż do znalezienia znacznika końca - std::string input = ""; - char c { 0 }; - auto const endmarksize = Endmark.size(); - while( mStream->peek() != EOF ) { - // o ile nie koniec pliku - c = mStream->get(); // pobranie znaku - if( c == '\n' ) { - // update line counter - ++mLine; - } - input += c; - if( input == Endmark ) // szukanie znacznika końca - break; - if( input.size() >= endmarksize ) { - // keep the read text short, to avoid pointless string re-allocations on longer comments - input = input.substr( 1 ); - } - } - return; +void cParser::skipComment(std::string const &Endmark) +{ // pobieranie znaków aż do znalezienia znacznika końca + std::string input = ""; + char c{0}; + auto const endmarksize = Endmark.size(); + while (mStream->peek() != EOF) + { + // o ile nie koniec pliku + c = mStream->get(); // pobranie znaku + if (c == '\n') + { + // update line counter + ++mLine; + } + input += c; + if (input == Endmark) // szukanie znacznika końca + break; + if (input.size() >= endmarksize) + { + // keep the read text short, to avoid pointless string re-allocations on longer comments + input = input.substr(1); + } + } + return; } -bool cParser::findQuotes( std::string &String ) { +bool cParser::findQuotes(std::string &String) +{ - if( String.back() == '\"' ) { + if (String.back() == '\"') + { - String.pop_back(); - String += readQuotes(); - return true; - } - return false; + String.pop_back(); + String += readQuotes(); + return true; + } + return false; } bool cParser::trimComments(std::string &String) { - for (auto const &comment : mComments) - { - if( String.size() < comment.first.size() ) { continue; } + for (auto const &comment : mComments) + { + if (String.size() < comment.first.size()) + { + continue; + } - if (String.compare( String.size() - comment.first.size(), comment.first.size(), comment.first ) == 0) - { - skipComment(comment.second); - String.resize(String.rfind(comment.first)); - return true; - } - } - return false; + if (String.compare(String.size() - comment.first.size(), comment.first.size(), comment.first) == 0) + { + skipComment(comment.second); + String.resize(String.rfind(comment.first)); + return true; + } + } + return false; } void cParser::injectString(const std::string &str) { - if (mIncludeParser) { + if (mIncludeParser) + { mIncludeParser->injectString(str); } - else { - mIncludeParser = std::make_shared( str, buffer_TEXT, "", LoadTraction, std::vector(), allowRandomIncludes ); - mIncludeParser->autoclear( m_autoclear ); + else + { + mIncludeParser = std::make_shared(str, buffer_TEXT, "", LoadTraction, std::vector(), allowRandomIncludes); + mIncludeParser->autoclear(m_autoclear); } } int cParser::getProgress() const { - return static_cast( mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize ); + return static_cast(mStream->rdbuf()->pubseekoff(0, std::ios_base::cur) * 100 / mSize); } -int cParser::getFullProgress() const { +int cParser::getFullProgress() const +{ - int progress = getProgress(); - if( mIncludeParser ) return progress + ( ( 100 - progress )*( mIncludeParser->getProgress() ) / 100 ); - else return progress; + int progress = getProgress(); + if (mIncludeParser) + return progress + ((100 - progress) * (mIncludeParser->getProgress()) / 100); + else + return progress; } -std::size_t cParser::countTokens( std::string const &Stream, std::string Path ) { +std::size_t cParser::countTokens(std::string const &Stream, std::string Path) +{ - return cParser( Stream, buffer_FILE, Path ).count(); + return cParser(Stream, buffer_FILE, Path).count(); } -std::size_t cParser::count() { +std::size_t cParser::count() +{ - std::string token; - size_t count { 0 }; - do { - token = ""; - token = readToken( false ); - ++count; - } while( false == token.empty() ); + std::string token; + size_t count{0}; + do + { + token = ""; + token = readToken(false); + ++count; + } while (false == token.empty()); - return count - 1; + return count - 1; } -void cParser::addCommentStyle( std::string const &Commentstart, std::string const &Commentend ) { +void cParser::addCommentStyle(std::string const &Commentstart, std::string const &Commentend) +{ - mComments.insert( commentmap::value_type(Commentstart, Commentend) ); + mComments.insert(commentmap::value_type(Commentstart, Commentend)); } // returns name of currently open file, or empty string for text type stream -std::string -cParser::Name() const { +std::string cParser::Name() const +{ - if( mIncludeParser ) { return mIncludeParser->Name(); } - else { return mPath + mFile; } + if (mIncludeParser) + { + return mIncludeParser->Name(); + } + else + { + return mPath + mFile; + } } // returns number of currently processed line -std::size_t -cParser::Line() const { +std::size_t cParser::Line() const +{ - if( mIncludeParser ) { return mIncludeParser->Line(); } - else { return mLine; } + if (mIncludeParser) + { + return mIncludeParser->Line(); + } + else + { + return mLine; + } } -int cParser::LineMain() const { +int cParser::LineMain() const +{ return mIncludeParser ? -1 : mLine; } diff --git a/utilities.cpp b/utilities.cpp index 3e25c769..917fef05 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -322,7 +322,7 @@ bool string_starts_with(const std::string &string, const std::string &begin) return string.compare(0, begin.length(), begin) == 0; } -std::string const fractionlabels[] = {" ", u8"¹", u8"²", u8"³", u8"⁴", u8"⁵", u8"⁶", u8"⁷", u8"⁸", u8"⁹"}; +std::string const fractionlabels[] = {" ", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"}; std::string to_minutes_str(float const Minutes, bool const Leadingzero, int const Width) { @@ -376,8 +376,8 @@ void win1250_to_ascii(std::string &Input) std::string win1250_to_utf8(const std::string &Input) { - std::unordered_map const charmap{{165, u8"Ą"}, {198, u8"Ć"}, {202, u8"Ę"}, {163, u8"Ł"}, {209, u8"Ń"}, {211, u8"Ó"}, {140, u8"Ś"}, {143, u8"Ź"}, {175, u8"Ż"}, - {185, u8"ą"}, {230, u8"ć"}, {234, u8"ę"}, {179, u8"ł"}, {241, u8"ń"}, {243, u8"ó"}, {156, u8"ś"}, {159, u8"ź"}, {191, u8"ż"}}; + std::unordered_map const charmap{{165, "Ą"}, {198, "Ć"}, {202, "Ę"}, {163, "Ł"}, {209, "Ń"}, {211, "Ó"}, {140, "Ś"}, {143, "Ź"}, {175, "Ż"}, + {185, "ą"}, {230, "ć"}, {234, "ę"}, {179, "ł"}, {241, "ń"}, {243, "ó"}, {156, "ś"}, {159, "ź"}, {191, "ż"}}; std::string output; std::unordered_map::const_iterator lookup; for (auto &input : Input) diff --git a/widgets/trainingcard.cpp b/widgets/trainingcard.cpp index 9acea1d8..81bce900 100644 --- a/widgets/trainingcard.cpp +++ b/widgets/trainingcard.cpp @@ -7,10 +7,9 @@ #include #endif -trainingcard_panel::trainingcard_panel() - : ui_panel("Raport szkolenia", false) +trainingcard_panel::trainingcard_panel() : ui_panel("Raport szkolenia", false) { - //size = {400, 500}; + // size = {400, 500}; clear(); } @@ -58,21 +57,25 @@ void trainingcard_panel::save_thread_func() std::fstream input("report_template.html", std::ios_base::in | std::ios_base::binary); std::string in_line; - while (std::getline(input, in_line)) { + while (std::getline(input, in_line)) + { const std::string magic("{{CONTENT}}"); - if (in_line.compare(0, magic.size(), magic) == 0) { + if (in_line.compare(0, magic.size(), magic) == 0) + { temp << "
Miejsce: " << (std::string(place.c_str())) << "

" << std::endl; temp << "
Data: " << (date) << "

" << std::endl; temp << "
Czas: " << (from) << " - " << (to) << "

" << std::endl; temp << "
Imię (imiona) i nazwisko szkolonego: " << (trainee_name) << "

" << std::endl; temp << "
Data urodzenia: " << (trainee_birthdate) << "

" << std::endl; temp << "
Firma: " << (trainee_company) << "

" << std::endl; - temp << "
Imię i nazwisko instruktora: " << (instructor_name) << "

" << std::endl; - temp << "
Odcinek trasy: " << (track_segment) << "

" << std::endl; + temp << "
Imię i nazwisko instruktora: " << (instructor_name) << "

" << std::endl; + temp << "
Odcinek trasy: " << (track_segment) << "

" << std::endl; if (distance > 0.0f) - temp << "
Przebyta odległość: " << std::round(distance) << " km

" << std::endl; - temp << "
Uwagi:
" << (remarks) << "
" << std::endl; - } else { + temp << "
Przebyta odległość: " << std::round(distance) << " km

" << std::endl; + temp << "
Uwagi:
" << (remarks) << "
" << std::endl; + } + else + { temp << in_line; } } @@ -85,37 +88,42 @@ void trainingcard_panel::save_thread_func() void trainingcard_panel::render_contents() { - if (ImGui::BeginPopupModal("Zapisywanie danych")) { + if (ImGui::BeginPopupModal("Zapisywanie danych")) + { ImGui::SetWindowSize(ImVec2(-1, -1)); int s = state.load(); - if (s == 1) { + if (s == 1) + { ImGui::CloseCurrentPopup(); clear(); } - if (s < 1) { + if (s < 1) + { if (s == 0) ImGui::TextUnformatted("Error occured please contact with administrator!"); if (s == -1) ImGui::TextUnformatted("The recording of the training has not been archived, please do not start the next training before manually archiving the file!"); - if (ImGui::Button("OK")) { + if (ImGui::Button("OK")) + { ImGui::CloseCurrentPopup(); clear(); } } if (s == 2) - ImGui::TextUnformatted(u8"Proszę czekać, trwa archiwizacja nagrania..."); + ImGui::TextUnformatted("Proszę czekać, trwa archiwizacja nagrania..."); ImGui::EndPopup(); } - if (start_time_wall) { + if (start_time_wall) + { std::tm *tm = std::localtime(&(*start_time_wall)); - std::string rep = u8"Czas rozpoczęcia: " + std::to_string(tm->tm_year + 1900) + "-" + std::to_string(tm->tm_mon + 1) + "-" + std::to_string(tm->tm_mday) - + " " + std::to_string(tm->tm_hour) + ":" + std::to_string(tm->tm_min); + std::string rep = "Czas rozpoczęcia: " + std::to_string(tm->tm_year + 1900) + "-" + std::to_string(tm->tm_mon + 1) + "-" + std::to_string(tm->tm_mday) + " " + std::to_string(tm->tm_hour) + + ":" + std::to_string(tm->tm_min); ImGui::TextUnformatted(rep.c_str()); } @@ -146,22 +154,27 @@ void trainingcard_panel::render_contents() ImGui::TextUnformatted("Uwagi"); ImGui::InputTextMultiline("##remarks", &remarks[0], remarks.size(), ImVec2(-1.0f, 200.0f)); - if (!start_time_wall) { - if (ImGui::Button("Rozpocznij szkolenie")) { + if (!start_time_wall) + { + if (ImGui::Button("Rozpocznij szkolenie")) + { start_time_wall = std::time(nullptr); std::tm *tm = std::localtime(&(*start_time_wall)); - recording_timestamp = std::to_string(tm->tm_year + 1900) + std::to_string(tm->tm_mon + 1) + std::to_string(tm->tm_mday) - + std::to_string(tm->tm_hour) + std::to_string(tm->tm_min) + "_" + std::string(trainee_name.c_str()) + "_" + std::string(instructor_name.c_str()); + recording_timestamp = std::to_string(tm->tm_year + 1900) + std::to_string(tm->tm_mon + 1) + std::to_string(tm->tm_mday) + std::to_string(tm->tm_hour) + std::to_string(tm->tm_min) + "_" + + std::string(trainee_name.c_str()) + "_" + std::string(instructor_name.c_str()); int ret = StartRecording(); - if (ret != 1) { + if (ret != 1) + { state.store(ret); ImGui::OpenPopup("Zapisywanie danych"); } } } - else { - if (ImGui::Button(u8"Zakończ szkolenie")) { + else + { + if (ImGui::Button("Zakończ szkolenie")) + { state.store(2); if (simulation::Trains.sequence().size() > 0) distance = simulation::Trains.sequence()[0]->Dynamic()->MoverParameters->DistCounter; diff --git a/widgets/vehicleparams.cpp b/widgets/vehicleparams.cpp index ddcd81b1..dbefb4b1 100644 --- a/widgets/vehicleparams.cpp +++ b/widgets/vehicleparams.cpp @@ -5,27 +5,26 @@ #include "Driver.h" #include "Train.h" -ui::vehicleparams_panel::vehicleparams_panel(const std::string &vehicle) - : ui_panel(std::string(STR("Vehicle parameters")) + ": " + vehicle, false), m_vehicle_name(vehicle) +ui::vehicleparams_panel::vehicleparams_panel(const std::string &vehicle) : ui_panel(std::string(STR("Vehicle parameters")) + ": " + vehicle, false), m_vehicle_name(vehicle) { - vehicle_mini = GfxRenderer->Fetch_Texture("vehicle_mini"); + vehicle_mini = GfxRenderer->Fetch_Texture("vehicle_mini"); } -void screen_window_callback(ImGuiSizeCallbackData *data) { - auto config = static_cast(data->UserData); +void screen_window_callback(ImGuiSizeCallbackData *data) +{ + auto config = static_cast(data->UserData); data->DesiredSize.y = data->DesiredSize.x * (float)config->size.y / (float)config->size.x; } void ui::vehicleparams_panel::draw_infobutton(const char *str, ImVec2 pos, const ImVec4 color) { - if (pos.x != -1.0f) { + if (pos.x != -1.0f) + { ImVec2 window_size = ImGui::GetWindowSize(); ImGuiStyle &style = ImGui::GetStyle(); ImVec2 text_size = ImGui::CalcTextSize(str); - ImVec2 button_size = ImVec2( - text_size.x + style.FramePadding.x * 2.0f, - text_size.y + style.FramePadding.y * 2.0f); + ImVec2 button_size = ImVec2(text_size.x + style.FramePadding.x * 2.0f, text_size.y + style.FramePadding.y * 2.0f); pos.x = pos.x * window_size.x / 512.0f - button_size.x / 2.0f; pos.y = pos.y * window_size.y / 118.0f - button_size.y / 2.0f; @@ -52,7 +51,7 @@ void ui::vehicleparams_panel::draw_mini(const TMoverParameters &mover) if (vehicle_mini == null_handle) return; - auto &tex = GfxRenderer->Texture(vehicle_mini); + auto &tex = GfxRenderer->Texture(vehicle_mini); tex.create(); ImVec2 size = ImGui::GetContentRegionAvail(); @@ -61,12 +60,12 @@ void ui::vehicleparams_panel::draw_mini(const TMoverParameters &mover) if (ImGui::BeginChild("mini", ImVec2(x, y))) { - ImGui::Image(reinterpret_cast(tex.get_id()), ImVec2(x, y), ImVec2(0, 1), ImVec2(1, 0)); + ImGui::Image(reinterpret_cast(tex.get_id()), ImVec2(x, y), ImVec2(0, 1), ImVec2(1, 0)); - if (mover.Pantographs[end::rear].is_active) - draw_infobutton(u8"╨╨╨", ImVec2(126, 10)); - if (mover.Pantographs[end::front].is_active) - draw_infobutton(u8"╨╨╨", ImVec2(290, 10)); + if (mover.Pantographs[end::rear].is_active) + draw_infobutton("╨╨╨", ImVec2(126, 10)); + if (mover.Pantographs[end::front].is_active) + draw_infobutton("╨╨╨", ImVec2(290, 10)); if (mover.Battery) draw_infobutton(STR_C("bat."), ImVec2(120, 55)); @@ -99,24 +98,28 @@ void ui::vehicleparams_panel::draw_mini(const TMoverParameters &mover) void ui::vehicleparams_panel::render_contents() { TDynamicObject *vehicle_ptr = simulation::Vehicles.find(m_vehicle_name); - if (!vehicle_ptr) { + if (!vehicle_ptr) + { is_open = false; return; } TTrain *train_ptr = simulation::Trains.find(m_vehicle_name); - if (train_ptr) { + if (train_ptr) + { const TTrain::screenentry_sequence &screens = train_ptr->get_screens(); - for (const auto &viewport : Global.python_viewports) { - for (auto const &entry : screens) { - if (entry.script != viewport.surface) + for (const auto &viewport : Global.python_viewports) + { + for (auto const &entry : screens) + { + if (entry.script != viewport.surface) continue; std::string window_name = STR("Screen") + "##" + viewport.surface; - ImGui::SetNextWindowSizeConstraints(ImVec2(200, 200), ImVec2(2500, 2500), screen_window_callback, - const_cast(&viewport)); - if (ImGui::Begin(window_name.c_str())) { + ImGui::SetNextWindowSizeConstraints(ImVec2(200, 200), ImVec2(2500, 2500), screen_window_callback, const_cast(&viewport)); + if (ImGui::Begin(window_name.c_str())) + { float aspect = (float)viewport.size.y / viewport.size.x; glm::mat3 proj = glm::translate(glm::scale(glm::mat3(), 1.0f / viewport.scale), viewport.offset); @@ -126,7 +129,7 @@ void ui::vehicleparams_panel::render_contents() ImVec2 size = ImGui::GetContentRegionAvail(); - ImGui::Image(reinterpret_cast(entry.rt->shared_tex), size, ImVec2(uv0.x, uv0.y), ImVec2(uv1.x, uv1.y)); + ImGui::Image(reinterpret_cast(entry.rt->shared_tex), size, ImVec2(uv0.x, uv0.y), ImVec2(uv1.x, uv1.y)); } ImGui::End(); } @@ -139,178 +142,137 @@ void ui::vehicleparams_panel::render_contents() std::vector lines; std::array buffer; - auto const isdieselenginepowered { ( mover.EngineType == TEngineType::DieselElectric ) || ( mover.EngineType == TEngineType::DieselEngine ) }; - auto const isdieselinshuntmode { mover.ShuntMode && mover.EngineType == TEngineType::DieselElectric }; + auto const isdieselenginepowered{(mover.EngineType == TEngineType::DieselElectric) || (mover.EngineType == TEngineType::DieselEngine)}; + auto const isdieselinshuntmode{mover.ShuntMode && mover.EngineType == TEngineType::DieselElectric}; - std::snprintf( - buffer.data(), buffer.size(), - STR_C("Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s%s%.0f@%.0f"), - // devices - ( mover.Battery ? 'B' : '.' ), - ( mover.Mains ? 'M' : '.' ), - ( mover.FuseFlag ? '!' : '.' ), - ( mover.Pantographs[end::rear].is_active ? ( mover.PantRearVolt > 0.0 ? 'O' : 'o' ) : '.' ), - ( mover.Pantographs[end::front].is_active ? ( mover.PantFrontVolt > 0.0 ? 'P' : 'p' ) : '.' ), - ( mover.PantPressLockActive ? '!' : ( mover.PantPressSwitchActive ? '*' : '.' ) ), - ( mover.WaterPump.is_active ? 'W' : ( false == mover.WaterPump.breaker ? '-' : ( mover.WaterPump.is_enabled ? 'w' : '.' ) ) ), - ( true == mover.WaterHeater.is_damaged ? '!' : ( mover.WaterHeater.is_active ? 'H' : ( false == mover.WaterHeater.breaker ? '-' : ( mover.WaterHeater.is_enabled ? 'h' : '.' ) ) ) ), - ( mover.FuelPump.is_active ? 'F' : ( mover.FuelPump.is_enabled ? 'f' : '.' ) ), - ( mover.OilPump.is_active ? 'O' : ( mover.OilPump.is_enabled ? 'o' : '.' ) ), - ( false == mover.ConverterAllowLocal ? '-' : ( mover.ConverterAllow ? ( mover.ConverterFlag ? 'X' : 'x' ) : '.' ) ), - ( mover.ConvOvldFlag ? '!' : '.' ), - ( mover.CompressorFlag ? 'C' : ( false == mover.CompressorAllowLocal ? '-' : ( ( mover.CompressorAllow || mover.CompressorStart == start_t::automatic ) ? 'c' : '.' ) ) ), - ( mover.CompressorGovernorLock ? '!' : '.' ), - "", - std::string( isdieselenginepowered ? STR(" oil pressure: ") + to_string( mover.OilPump.pressure, 2 ) : "" ).c_str(), - // power transfers - mover.Couplers[ end::front ].power_high.voltage, - mover.Couplers[ end::front ].power_high.current, - std::string( mover.Couplers[ end::front ].power_high.is_local ? "" : "-" ).c_str(), - std::string( vehicle.DirectionGet() ? ":<<:" : ":>>:" ).c_str(), - std::string( mover.Couplers[ end::rear ].power_high.is_local ? "" : "-" ).c_str(), - mover.Couplers[ end::rear ].power_high.voltage, - mover.Couplers[ end::rear ].power_high.current ); + std::snprintf(buffer.data(), buffer.size(), STR_C("Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s%s%.0f@%.0f"), + // devices + (mover.Battery ? 'B' : '.'), (mover.Mains ? 'M' : '.'), (mover.FuseFlag ? '!' : '.'), (mover.Pantographs[end::rear].is_active ? (mover.PantRearVolt > 0.0 ? 'O' : 'o') : '.'), + (mover.Pantographs[end::front].is_active ? (mover.PantFrontVolt > 0.0 ? 'P' : 'p') : '.'), (mover.PantPressLockActive ? '!' : (mover.PantPressSwitchActive ? '*' : '.')), + (mover.WaterPump.is_active ? 'W' : (false == mover.WaterPump.breaker ? '-' : (mover.WaterPump.is_enabled ? 'w' : '.'))), + (true == mover.WaterHeater.is_damaged ? '!' : (mover.WaterHeater.is_active ? 'H' : (false == mover.WaterHeater.breaker ? '-' : (mover.WaterHeater.is_enabled ? 'h' : '.')))), + (mover.FuelPump.is_active ? 'F' : (mover.FuelPump.is_enabled ? 'f' : '.')), (mover.OilPump.is_active ? 'O' : (mover.OilPump.is_enabled ? 'o' : '.')), + (false == mover.ConverterAllowLocal ? '-' : (mover.ConverterAllow ? (mover.ConverterFlag ? 'X' : 'x') : '.')), (mover.ConvOvldFlag ? '!' : '.'), + (mover.CompressorFlag ? 'C' : (false == mover.CompressorAllowLocal ? '-' : ((mover.CompressorAllow || mover.CompressorStart == start_t::automatic) ? 'c' : '.'))), + (mover.CompressorGovernorLock ? '!' : '.'), "", std::string(isdieselenginepowered ? STR(" oil pressure: ") + to_string(mover.OilPump.pressure, 2) : "").c_str(), + // power transfers + mover.Couplers[end::front].power_high.voltage, mover.Couplers[end::front].power_high.current, std::string(mover.Couplers[end::front].power_high.is_local ? "" : "-").c_str(), + std::string(vehicle.DirectionGet() ? ":<<:" : ":>>:").c_str(), std::string(mover.Couplers[end::rear].power_high.is_local ? "" : "-").c_str(), + mover.Couplers[end::rear].power_high.voltage, mover.Couplers[end::rear].power_high.current); ImGui::TextUnformatted(buffer.data()); - std::snprintf( - buffer.data(), buffer.size(), - STR_C("Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, cooling fans: %.0f+%.0f"), - // controllers - mover.MainCtrlPos, - mover.MainCtrlActualPos, - std::string( isdieselinshuntmode ? to_string( mover.AnPos, 2 ) + STR(" (shunt mode)") : std::to_string( mover.ScndCtrlPos ) + "(" + std::to_string( mover.ScndCtrlActualPos ) + ")" ).c_str(), - // engine - mover.EnginePower, - std::abs( mover.TrainType == dt_EZT ? mover.ShowCurrent( 0 ) : mover.Im ), - // revolutions - std::abs( mover.enrot ) * 60, - std::abs( mover.nrot ) * mover.Transmision.Ratio * 60, - mover.RventRot * 60, - std::abs( mover.MotorBlowers[end::front].revolutions ), - std::abs( mover.MotorBlowers[end::rear].revolutions ), - mover.dizel_heat.rpmw, - mover.dizel_heat.rpmw2 ); + std::snprintf(buffer.data(), buffer.size(), + STR_C("Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, " + "cooling fans: %.0f+%.0f"), + // controllers + mover.MainCtrlPos, mover.MainCtrlActualPos, + std::string(isdieselinshuntmode ? to_string(mover.AnPos, 2) + STR(" (shunt mode)") : std::to_string(mover.ScndCtrlPos) + "(" + std::to_string(mover.ScndCtrlActualPos) + ")").c_str(), + // engine + mover.EnginePower, std::abs(mover.TrainType == dt_EZT ? mover.ShowCurrent(0) : mover.Im), + // revolutions + std::abs(mover.enrot) * 60, std::abs(mover.nrot) * mover.Transmision.Ratio * 60, mover.RventRot * 60, std::abs(mover.MotorBlowers[end::front].revolutions), + std::abs(mover.MotorBlowers[end::rear].revolutions), mover.dizel_heat.rpmw, mover.dizel_heat.rpmw2); ImGui::TextUnformatted(buffer.data()); - if( isdieselenginepowered ) { - std::snprintf( - buffer.data(), buffer.size(), - STR_C("\nTemperatures:\n engine: %.2f, oil: %.2f, water: %.2f%c%.2f"), - mover.dizel_heat.Ts, - mover.dizel_heat.To, - mover.dizel_heat.temperatura1, - ( mover.WaterCircuitsLink ? '-' : '|' ), - mover.dizel_heat.temperatura2 ); + if (isdieselenginepowered) + { + std::snprintf(buffer.data(), buffer.size(), STR_C("\nTemperatures:\n engine: %.2f, oil: %.2f, water: %.2f%c%.2f"), mover.dizel_heat.Ts, mover.dizel_heat.To, mover.dizel_heat.temperatura1, + (mover.WaterCircuitsLink ? '-' : '|'), mover.dizel_heat.temperatura2); ImGui::TextUnformatted(buffer.data()); } std::string brakedelay; { - std::vector> delays { - { bdelay_G, "G" }, - { bdelay_P, "P" }, - { bdelay_R, "R" }, - { bdelay_M, "+Mg" } }; + std::vector> delays{{bdelay_G, "G"}, {bdelay_P, "P"}, {bdelay_R, "R"}, {bdelay_M, "+Mg"}}; - for( auto const &delay : delays ) { - if( ( mover.BrakeDelayFlag & delay.first ) == delay.first ) { + for (auto const &delay : delays) + { + if ((mover.BrakeDelayFlag & delay.first) == delay.first) + { brakedelay += delay.second; } } } - std::snprintf( - buffer.data(), buffer.size(), - STR_C("Brakes:\n train: %.2f, independent: %.2f, mode: %d, delay: %s, load flag: %d\nBrake cylinder pressures:\n train: %.2f, independent: %.2f, status: 0x%.2x\nPipe pressures:\n brake: %.2f (hat: %.2f), main: %.2f, control: %.2f\nTank pressures:\n auxiliary: %.2f, main: %.2f, control: %.2f"), - // brakes - mover.fBrakeCtrlPos, - mover.LocalBrakePosA, - mover.BrakeOpModeFlag, - brakedelay.c_str(), - mover.LoadFlag, - // cylinders - mover.BrakePress, - mover.LocBrakePress, - mover.Hamulec->GetBrakeStatus(), - // pipes - mover.PipePress, - mover.BrakeCtrlPos2, - mover.ScndPipePress, - mover.CntrlPipePress, - // tanks - mover.Hamulec->GetBRP(), - mover.Compressor, - mover.Hamulec->GetCRP() ); + std::snprintf(buffer.data(), buffer.size(), + STR_C("Brakes:\n train: %.2f, independent: %.2f, mode: %d, delay: %s, load flag: %d\nBrake cylinder pressures:\n train: %.2f, independent: %.2f, status: 0x%.2x\nPipe pressures:\n " + "brake: %.2f (hat: %.2f), main: %.2f, control: %.2f\nTank pressures:\n auxiliary: %.2f, main: %.2f, control: %.2f"), + // brakes + mover.fBrakeCtrlPos, mover.LocalBrakePosA, mover.BrakeOpModeFlag, brakedelay.c_str(), mover.LoadFlag, + // cylinders + mover.BrakePress, mover.LocBrakePress, mover.Hamulec->GetBrakeStatus(), + // pipes + mover.PipePress, mover.BrakeCtrlPos2, mover.ScndPipePress, mover.CntrlPipePress, + // tanks + mover.Hamulec->GetBRP(), mover.Compressor, mover.Hamulec->GetCRP()); ImGui::TextUnformatted(buffer.data()); - if( mover.EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { - std::snprintf( - buffer.data(), buffer.size(), - STR_C(" pantograph: %.2f%cMT"), - mover.PantPress, - ( mover.bPantKurek3 ? '-' : '|' ) ); + if (mover.EnginePowerSource.SourceType == TPowerSource::CurrentCollector) + { + std::snprintf(buffer.data(), buffer.size(), STR_C(" pantograph: %.2f%cMT"), mover.PantPress, (mover.bPantKurek3 ? '-' : '|')); ImGui::TextUnformatted(buffer.data()); } - std::snprintf( - buffer.data(), buffer.size(), - STR_C("Forces:\n tractive: %.1f, brake: %.1f, friction: %.2f%s\nAcceleration:\n tangential: %.2f, normal: %.2f (path radius: %s)\nVelocity: %.2f, distance traveled: %.2f\nPosition: [%.2f, %.2f, %.2f]"), - // forces - mover.Ft * 0.001f * ( mover.CabActive ? mover.CabActive : vehicle.ctOwner ? vehicle.ctOwner->Controlling()->CabActive : 1 ) + 0.001f, - mover.Fb * 0.001f, - mover.Adhesive( mover.RunningTrack.friction ), - ( mover.SlippingWheels ? " (!)" : "" ), - // acceleration - mover.AccSVBased, - mover.AccN + 0.001f, - std::string( std::abs( mover.RunningShape.R ) > 10000.0 ? "~0" : to_string( mover.RunningShape.R, 0 ) ).c_str(), - // velocity - vehicle.GetVelocity(), - mover.DistCounter, - // position - vehicle.GetPosition().x, - vehicle.GetPosition().y, - vehicle.GetPosition().z ); + std::snprintf(buffer.data(), buffer.size(), + STR_C("Forces:\n tractive: %.1f, brake: %.1f, friction: %.2f%s\nAcceleration:\n tangential: %.2f, normal: %.2f (path radius: %s)\nVelocity: %.2f, distance traveled: %.2f\nPosition: " + "[%.2f, %.2f, %.2f]"), + // forces + mover.Ft * 0.001f * + (mover.CabActive ? mover.CabActive : + vehicle.ctOwner ? vehicle.ctOwner->Controlling()->CabActive : + 1) + + 0.001f, + mover.Fb * 0.001f, mover.Adhesive(mover.RunningTrack.friction), (mover.SlippingWheels ? " (!)" : ""), + // acceleration + mover.AccSVBased, mover.AccN + 0.001f, std::string(std::abs(mover.RunningShape.R) > 10000.0 ? "~0" : to_string(mover.RunningShape.R, 0)).c_str(), + // velocity + vehicle.GetVelocity(), mover.DistCounter, + // position + vehicle.GetPosition().x, vehicle.GetPosition().y, vehicle.GetPosition().z); ImGui::TextUnformatted(buffer.data()); - std::pair TrainsetPowerMeter; + std::pair TrainsetPowerMeter; - TDynamicObject *vehicle_iter = vehicle_ptr; - while (vehicle_iter) { - if (vehicle_iter->Next()) - vehicle_iter = vehicle_iter->Next(); - else - break; - } + TDynamicObject *vehicle_iter = vehicle_ptr; + while (vehicle_iter) + { + if (vehicle_iter->Next()) + vehicle_iter = vehicle_iter->Next(); + else + break; + } - while (vehicle_iter) { - TrainsetPowerMeter.first += vehicle_iter->MoverParameters->EnergyMeter.first; - TrainsetPowerMeter.second += vehicle_iter->MoverParameters->EnergyMeter.second; - vehicle_iter = vehicle_iter->Prev(); - } + while (vehicle_iter) + { + TrainsetPowerMeter.first += vehicle_iter->MoverParameters->EnergyMeter.first; + TrainsetPowerMeter.second += vehicle_iter->MoverParameters->EnergyMeter.second; + vehicle_iter = vehicle_iter->Prev(); + } - if (TrainsetPowerMeter.first != 0.0 || TrainsetPowerMeter.second != 0.0) { - std::snprintf(buffer.data(), buffer.size(), STR_C("Electricity usage:\n drawn: %.1f kWh\n returned: %.1f kWh\n balance: %.1f kWh"), - TrainsetPowerMeter.first, -TrainsetPowerMeter.second, TrainsetPowerMeter.first + TrainsetPowerMeter.second); + if (TrainsetPowerMeter.first != 0.0 || TrainsetPowerMeter.second != 0.0) + { + std::snprintf(buffer.data(), buffer.size(), STR_C("Electricity usage:\n drawn: %.1f kWh\n returned: %.1f kWh\n balance: %.1f kWh"), TrainsetPowerMeter.first, -TrainsetPowerMeter.second, + TrainsetPowerMeter.first + TrainsetPowerMeter.second); - ImGui::TextUnformatted(buffer.data()); - } + ImGui::TextUnformatted(buffer.data()); + } draw_mini(mover); if (ImGui::Button(STR_C("Radiostop"))) - m_relay.post(user_command::radiostopsend, 0.0, 0.0, GLFW_PRESS, 0, vehicle_ptr->GetPosition()); + m_relay.post(user_command::radiostopsend, 0.0, 0.0, GLFW_PRESS, 0, vehicle_ptr->GetPosition()); ImGui::SameLine(); if (ImGui::Button(STR_C("Stop and repair"))) m_relay.post(user_command::resetconsist, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &vehicle_ptr->name()); ImGui::SameLine(); - if (ImGui::Button(STR_C("Reset position"))) { + if (ImGui::Button(STR_C("Reset position"))) + { std::string payload = vehicle_ptr->name() + '%' + vehicle_ptr->initial_track->name(); m_relay.post(user_command::consistteleport, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &payload); m_relay.post(user_command::resetconsist, 0.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &vehicle_ptr->name()); @@ -330,7 +292,8 @@ void ui::vehicleparams_panel::render_contents() if (ImGui::IsItemDeactivated()) m_relay.post(user_command::consistreleaser, 0.0, 0.0, GLFW_RELEASE, 0, glm::vec3(0.0f), &vehicle_ptr->name()); - if (vehicle_ptr->MoverParameters->V < 0.01) { + if (vehicle_ptr->MoverParameters->V < 0.01) + { if (ImGui::Button(STR_C("Move +500m"))) m_relay.post(user_command::dynamicmove, 500.0, 0.0, GLFW_PRESS, 0, glm::vec3(0.0f), &vehicle_ptr->name()); ImGui::SameLine();