From 4fcf89d4995921b89b579d06052df11b66e4879f Mon Sep 17 00:00:00 2001 From: FormerLurker Date: Sat, 25 Apr 2020 17:20:39 -0500 Subject: Add project files. --- PyArcWelder/PyArcWelder.vcxproj | 171 +++++++++++++++++++ PyArcWelder/PyArcWelder.vcxproj.filters | 45 +++++ PyArcWelder/py_arc_welder.cpp | 48 ++++++ PyArcWelder/py_arc_welder.h | 49 ++++++ PyArcWelder/py_arc_welder_extension.cpp | 281 ++++++++++++++++++++++++++++++++ PyArcWelder/py_arc_welder_extension.h | 78 +++++++++ PyArcWelder/py_logger.cpp | 246 ++++++++++++++++++++++++++++ PyArcWelder/py_logger.h | 66 ++++++++ PyArcWelder/python_helpers.cpp | 116 +++++++++++++ PyArcWelder/python_helpers.h | 40 +++++ 10 files changed, 1140 insertions(+) create mode 100644 PyArcWelder/PyArcWelder.vcxproj create mode 100644 PyArcWelder/PyArcWelder.vcxproj.filters create mode 100644 PyArcWelder/py_arc_welder.cpp create mode 100644 PyArcWelder/py_arc_welder.h create mode 100644 PyArcWelder/py_arc_welder_extension.cpp create mode 100644 PyArcWelder/py_arc_welder_extension.h create mode 100644 PyArcWelder/py_logger.cpp create mode 100644 PyArcWelder/py_logger.h create mode 100644 PyArcWelder/python_helpers.cpp create mode 100644 PyArcWelder/python_helpers.h (limited to 'PyArcWelder') diff --git a/PyArcWelder/PyArcWelder.vcxproj b/PyArcWelder/PyArcWelder.vcxproj new file mode 100644 index 0000000..a17c4c6 --- /dev/null +++ b/PyArcWelder/PyArcWelder.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {DB476DBA-77D5-40A7-ADAB-D9901F32B270} + PyArcWelder + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + C:\Python27\include;$(SolutionDir)\GcodeProcessorLib\;$(SolutionDir)\ArcWelder\;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + true + C:\Python27\include;$(SolutionDir)\GcodeProcessorLib\;$(SolutionDir)\ArcWelder\;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + false + C:\Python27\include;$(SolutionDir)\GcodeProcessorLib\;$(SolutionDir)\ArcWelder\;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + false + C:\Python27\include;$(SolutionDir)\GcodeProcessorLib\;$(SolutionDir)\ArcWelder\;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + C:\Python27\libs;%(AdditionalLibraryDirectories) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + C:\Python27\libs;%(AdditionalLibraryDirectories) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + C:\Python27\libs;%(AdditionalLibraryDirectories) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + C:\Python27\libs;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + {1a4dbab1-bb42-4db1-b168-f113784efcef} + + + {31478bae-104b-4cc3-9876-42fa90cbd5fe} + + + + + + \ No newline at end of file diff --git a/PyArcWelder/PyArcWelder.vcxproj.filters b/PyArcWelder/PyArcWelder.vcxproj.filters new file mode 100644 index 0000000..8544f81 --- /dev/null +++ b/PyArcWelder/PyArcWelder.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/PyArcWelder/py_arc_welder.cpp b/PyArcWelder/py_arc_welder.cpp new file mode 100644 index 0000000..5768b40 --- /dev/null +++ b/PyArcWelder/py_arc_welder.cpp @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "py_arc_welder.h" + + +bool py_arc_welder::on_progress_(double percent_complete, double seconds_elapsed, double estimated_seconds_remaining, int gcodes_processed, int current_line, int points_compressed, int arcs_created) +{ + PyObject* funcArgs = Py_BuildValue("(d,d,d,i,i,i,i)", percent_complete, seconds_elapsed, estimated_seconds_remaining, gcodes_processed, current_line, points_compressed, arcs_created); + if (funcArgs == NULL) + { + return false; + } + + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* pContinueProcessing = PyObject_CallObject(py_progress_callback_, funcArgs); + Py_DECREF(funcArgs); + bool continue_processing = PyLong_AsLong(pContinueProcessing) > 0; + Py_DECREF(pContinueProcessing); + PyGILState_Release(gstate); + + if (pContinueProcessing == NULL || pContinueProcessing == Py_None) + { + // no return value was supply, assume true + + return true; + } + return continue_processing; +} diff --git a/PyArcWelder/py_arc_welder.h b/PyArcWelder/py_arc_welder.h new file mode 100644 index 0000000..64bfd71 --- /dev/null +++ b/PyArcWelder/py_arc_welder.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#include +#include +#include "py_logger.h" +#ifdef _DEBUG +#undef _DEBUG +#include +#define _DEBUG +#else +#include +#endif +class py_arc_welder : public arc_welder +{ +public: + py_arc_welder(std::string source_path, std::string target_path, py_logger* logger, double resolution_mm, bool g90_g91_influences_extruder, int buffer_size, PyObject* py_progress_callback):arc_welder(source_path, target_path, logger, resolution_mm, g90_g91_influences_extruder, buffer_size) + { + py_progress_callback_ = py_progress_callback; + } + virtual ~py_arc_welder() { + + } +protected: + virtual bool on_progress_(double percent_complete, double seconds_elapsed, double estimated_seconds_remaining, int gcodes_processed, int current_line, int points_compressed, int arcs_created); +private: + PyObject* py_progress_callback_; +}; + diff --git a/PyArcWelder/py_arc_welder_extension.cpp b/PyArcWelder/py_arc_welder_extension.cpp new file mode 100644 index 0000000..eaf9923 --- /dev/null +++ b/PyArcWelder/py_arc_welder_extension.cpp @@ -0,0 +1,281 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "py_arc_welder_extension.h" +#include "py_arc_welder.h" +#include +#include +#include +#include "arc_welder.h" +#include "py_logger.h" +#include "python_helpers.h" + +#if PY_MAJOR_VERSION >= 3 +int main(int argc, char* argv[]) +{ + wchar_t* program = Py_DecodeLocale(argv[0], NULL); + if (program == NULL) { + fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + exit(1); + } + + // Add a built-in module, before Py_Initialize + PyImport_AppendInittab("PyArcWelder", PyInit_PyGcodeArcConverter); + + // Pass argv[0] to the Python interpreter + Py_SetProgramName(program); + + // Initialize the Python interpreter. Required. + Py_Initialize(); + std::cout << "Initializing threads..."; + if (!PyEval_ThreadsInitialized()) { + PyEval_InitThreads(); + } + // Optionally import the module; alternatively, import can be deferred until the embedded script imports it. + PyImport_ImportModule("PyArcWelder"); + PyMem_RawFree(program); + return 0; +} + +#else + +int main(int argc, char* argv[]) +{ + Py_SetProgramName(argv[0]); + Py_Initialize(); + if (!PyEval_ThreadsInitialized()) { + PyEval_InitThreads(); + } + initPyGcodeArcConverter(); + return 0; + +} +#endif + +struct module_state { + PyObject* error; +}; +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +// Python 2 module method definition +static PyMethodDef PyGcodeArcConverterMethods[] = { + { "ConvertFile", (PyCFunction)ConvertFile, METH_VARARGS ,"Converts segmented curve approximations to actual G2/G3 arcs within the supplied resolution." }, + { NULL, NULL, 0, NULL } +}; + +// Python 3 module method definition +#if PY_MAJOR_VERSION >= 3 +static int PyGcodeArcConverter_traverse(PyObject* m, visitproc visit, void* arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int PyGcodeArcConverter_clear(PyObject* m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "PyGcodeArcConverter", + NULL, + sizeof(struct module_state), + PyGcodeArcConverterMethods, + NULL, + PyGcodeArcConverter_traverse, + PyGcodeArcConverter_clear, + NULL +}; + +#define INITERROR return NULL + +PyMODINIT_FUNC +PyInit_PyGcodeArcConverter(void) + +#else +#define INITERROR return + +extern "C" void initPyGcodeArcConverter(void) +#endif +{ + std::cout << "Initializing PyGcodeArcConverter V0.1.0rc1.dev0 - Copyright (C) 2019 Brad Hochgesang..."; + +#if PY_MAJOR_VERSION >= 3 + std::cout << "Python 3+ Detected..."; + PyObject* module = PyModule_Create(&moduledef); +#else + std::cout << "Python 2 Detected..."; + PyObject* module = Py_InitModule("PyGcodeArcConverter", PyGcodeArcConverterMethods); +#endif + + if (module == NULL) + INITERROR; + struct module_state* st = GETSTATE(module); + + st->error = PyErr_NewException((char*)"PyGcodeArcConverter.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } + std::vector logger_names; + logger_names.push_back("arc_welder.gcode_conversion"); + std::vector logger_levels; + logger_levels.push_back(log_levels::DEBUG); + p_py_logger = new py_logger(logger_names, logger_levels); + p_py_logger->initialize_loggers(); + std::string message = "PyArcWelder V0.1.0rc1.dev0 imported - Copyright (C) 2019 Brad Hochgesang..."; + p_py_logger->log(GCODE_CONVERSION, INFO, message); + p_py_logger->set_log_level_by_value(DEBUG); + std::cout << "complete\r\n"; + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + +extern "C" +{ + static PyObject* ConvertFile(PyObject* self, PyObject* py_args) + { + PyObject* py_convert_file_args; + if (!PyArg_ParseTuple( + py_args, + "O", + &py_convert_file_args + )) + { + std::string message = "py_gcode_arc_converter.ConvertFile - Cound not extract the parameters dictionary."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return NULL; + } + + py_gcode_arc_args args; + PyObject* py_progress_callback = NULL; + + if (!ParseArgs(py_convert_file_args, args, &py_progress_callback)) + { + return NULL; + } + p_py_logger->set_log_level_by_value(args.log_level); + std::stringstream stream; + stream << "py_gcode_arc_converter.ConvertFile - Parameters received: source_file_path: '" << + args.source_file_path << "', target_file_path:'" << args.target_file_path << "' resolution_mm:" << + args.resolution_mm << ", g90_91_influences_extruder: " << (args.g90_g91_influences_extruder ? "True" : "False") << "\n"; + p_py_logger->log(GCODE_CONVERSION, INFO, stream.str()); + + std::string message = "py_gcode_arc_converter.ConvertFile - Beginning Arc Conversion."; + p_py_logger->log(GCODE_CONVERSION, INFO, message); + + py_arc_welder arc_welder_obj(args.source_file_path, args.target_file_path, p_py_logger, args.resolution_mm, args.g90_g91_influences_extruder, 50, py_progress_callback); + arc_welder_obj.process(); + message = "py_gcode_arc_converter.ConvertFile - Arc Conversion Complete."; + p_py_logger->log(GCODE_CONVERSION, INFO, message); + Py_XDECREF(py_progress_callback); + // For now just return py_none + return PyTuple_Pack(1, Py_None); + } +} + +static bool ParseArgs(PyObject* py_args, py_gcode_arc_args& args, PyObject** py_progress_callback) +{ + p_py_logger->log( + GCODE_CONVERSION, INFO, + "Parsing GCode Conversion Args." + ); + + // Extract the source file path + PyObject* py_source_file_path = PyDict_GetItemString(py_args, "source_file_path"); + if (py_source_file_path == NULL) + { + std::string message = "ParseArgs - Unable to retrieve the source_file_path parameter from the args."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return false; + } + args.source_file_path = gcode_arc_converter::PyUnicode_SafeAsString(py_source_file_path); + + // Extract the target file path + PyObject* py_target_file_path = PyDict_GetItemString(py_args, "target_file_path"); + if (py_target_file_path == NULL) + { + std::string message = "ParseArgs - Unable to retrieve the target_file_path parameter from the args."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return false; + } + args.target_file_path = gcode_arc_converter::PyUnicode_SafeAsString(py_target_file_path); + + // Extract the resolution in millimeters + PyObject* py_resolution_mm = PyDict_GetItemString(py_args, "resolution_mm"); + if (py_resolution_mm == NULL) + { + std::string message = "ParseArgs - Unable to retrieve the resolution_mm parameter from the args."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return false; + } + args.resolution_mm = gcode_arc_converter::PyFloatOrInt_AsDouble(py_resolution_mm); + if (args.resolution_mm <= 0) + { + args.resolution_mm = 0.05; // Set to the default if no resolution is provided, or if it is less than 0. + } + // Extract G90/G91 influences extruder + // g90_influences_extruder + PyObject* py_g90_g91_influences_extruder = PyDict_GetItemString(py_args, "g90_g91_influences_extruder"); + if (py_g90_g91_influences_extruder == NULL) + { + std::string message = "ParseArgs - Unable to retrieve g90_g91_influences_extruder from the args."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return false; + } + args.g90_g91_influences_extruder = PyLong_AsLong(py_g90_g91_influences_extruder) > 0; + + // on_progress_received + PyObject* py_on_progress_received = PyDict_GetItemString(py_args, "on_progress_received"); + if (py_on_progress_received == NULL) + { + std::string message = "ParseArgs - Unable to retrieve on_progress_received from the stabilization args."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return false; + } + // need to incref this so it doesn't vanish later (borrowed reference we are saving) + Py_XINCREF(py_on_progress_received); + *py_progress_callback = py_on_progress_received; + + // Extract log_level + PyObject* py_log_level = PyDict_GetItemString(py_args, "log_level"); + if (py_log_level == NULL) + { + std::string message = "ParseArgs - Unable to retrieve log_level from the args."; + p_py_logger->log_exception(GCODE_CONVERSION, message); + return false; + } + + int log_level_value = static_cast(PyLong_AsLong(py_log_level)); + // determine the log level as an index rather than as a value + args.log_level = p_py_logger->get_log_level_for_value(log_level_value); + + return true; +} + diff --git a/PyArcWelder/py_arc_welder_extension.h b/PyArcWelder/py_arc_welder_extension.h new file mode 100644 index 0000000..7a3786d --- /dev/null +++ b/PyArcWelder/py_arc_welder_extension.h @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#ifdef _DEBUG +#undef _DEBUG +#include +#define _DEBUG +#else +#include +#endif +#include +#include "py_logger.h" +extern "C" +{ +#if PY_MAJOR_VERSION >= 3 + PyMODINIT_FUNC PyInit_PyGcodeArcConverter(void); +#else + extern "C" void initPyGcodeArcConverter(void); +#endif + static PyObject* ConvertFile(PyObject* self, PyObject* args); +} + +struct py_gcode_arc_args { + py_gcode_arc_args() { + source_file_path = ""; + target_file_path = ""; + resolution_mm = 0.05; + g90_g91_influences_extruder = false; + log_level = 0; + } + py_gcode_arc_args(std::string source_file_path_, std::string target_file_path_, double resolution_mm_, bool g90_g91_influences_extruder_, int log_level_) { + source_file_path = source_file_path_; + target_file_path = target_file_path_; + resolution_mm = resolution_mm_; + g90_g91_influences_extruder = g90_g91_influences_extruder_; + log_level = log_level_; + } + std::string source_file_path; + std::string target_file_path; + double resolution_mm; + bool g90_g91_influences_extruder; + int log_level; +}; + +static bool ParseArgs(PyObject* py_args, py_gcode_arc_args& args, PyObject** p_py_progress_callback); + +// global logger +py_logger* p_py_logger = NULL; +/* +static void AtExit() +{ + if (p_py_logger != NULL) delete p_py_logger; +}*/ + + + + + diff --git a/PyArcWelder/py_logger.cpp b/PyArcWelder/py_logger.cpp new file mode 100644 index 0000000..d05e059 --- /dev/null +++ b/PyArcWelder/py_logger.cpp @@ -0,0 +1,246 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "py_logger.h" + +py_logger::py_logger(std::vector names, std::vector levels) : logger(names, levels) +{ + loggers_created_ = false; + check_log_levels_real_time = true; + py_logging_module = NULL; + py_logging_configurator_name = NULL; + py_logging_configurator = NULL; + py_arc_welder_gcode_conversion_logger = NULL; + gcode_conversion_log_level = 0; + py_info_function_name = NULL; + py_warn_function_name = NULL; + py_error_function_name = NULL; + py_debug_function_name = NULL; + py_verbose_function_name = NULL; + py_critical_function_name = NULL; + py_get_effective_level_function_name = NULL; +} +void py_logger::initialize_loggers() +{ + // Create all of the objects necessary for logging + // Import the arc_welder.log module + py_logging_module = PyImport_ImportModuleNoBlock("octoprint_arc_welder.log"); + if (py_logging_module == NULL) + { + PyErr_SetString(PyExc_ImportError, "Could not import module 'arc_welder.log'."); + return; + } + + // Get the logging configurator attribute string + py_logging_configurator_name = PyObject_GetAttrString(py_logging_module, "LoggingConfigurator"); + if (py_logging_configurator_name == NULL) + { + PyErr_SetString(PyExc_ImportError, "Could not acquire the LoggingConfigurator attribute string."); + return; + } + + // Create a logging configurator + PyGILState_STATE gstate = PyGILState_Ensure(); + std::cout << "Creating arguments for LoggingConfigurator creation.\r\n"; + PyObject* funcArgs = Py_BuildValue("(s,s,s)", "arc_welder", "arc_welder.", "octoprint_arc_welder."); + if (funcArgs == NULL) + { + std::cout << "Unable to create LoggingConfigurator arguments, exiting.\r\n"; + PyErr_SetString(PyExc_ImportError, "Could not create LoggingConfigurator arguments."); + return; + } + std::cout << "Creating LoggingConfigurator..."; + py_logging_configurator = PyObject_CallObject(py_logging_configurator_name, funcArgs); + std::cout << "Complete.\r\n"; + Py_DECREF(funcArgs); + PyGILState_Release(gstate); + if (py_logging_configurator == NULL) + { + std::cout << "The LoggingConfigurator is null, exiting.\r\n"; + PyErr_SetString(PyExc_ImportError, "Could not create a new instance of LoggingConfigurator."); + return; + } + + // Create the gcode_parser logging object + py_arc_welder_gcode_conversion_logger = PyObject_CallMethod(py_logging_configurator, (char*)"get_logger", (char*)"s", "octoprint_arc_welder.gcode_conversion"); + if (py_arc_welder_gcode_conversion_logger == NULL) + { + std::cout << "No child logger was created, exiting.\r\n"; + PyErr_SetString(PyExc_ImportError, "Could not create the arc_welder.gcode_parser child logger."); + return; + } + + // create the function name py objects + py_info_function_name = gcode_arc_converter::PyString_SafeFromString("info"); + py_warn_function_name = gcode_arc_converter::PyString_SafeFromString("warn"); + py_error_function_name = gcode_arc_converter::PyString_SafeFromString("error"); + py_debug_function_name = gcode_arc_converter::PyString_SafeFromString("debug"); + py_verbose_function_name = gcode_arc_converter::PyString_SafeFromString("verbose"); + py_critical_function_name = gcode_arc_converter::PyString_SafeFromString("critical"); + py_get_effective_level_function_name = gcode_arc_converter::PyString_SafeFromString("getEffectiveLevel"); + loggers_created_ = true; + std::cout << "Logger created successfully.\r\n"; + +} + +void py_logger::set_internal_log_levels(bool check_real_time) +{ + check_log_levels_real_time = check_real_time; + if (!check_log_levels_real_time) + { + + PyObject* py_gcode_conversion_log_level = PyObject_CallMethodObjArgs(py_arc_welder_gcode_conversion_logger, py_get_effective_level_function_name, NULL); + if (py_gcode_conversion_log_level == NULL) + { + PyErr_Print(); + PyErr_SetString(PyExc_ValueError, "Logging.arc_welder - Could not retrieve the log level for the gcode parser logger."); + } + gcode_conversion_log_level = gcode_arc_converter::PyIntOrLong_AsLong(py_gcode_conversion_log_level); + + Py_XDECREF(py_gcode_conversion_log_level); + } +} + +void py_logger::log_exception(const int logger_type, const std::string& message) +{ + log(logger_type, ERROR, message, true); +} + +void py_logger::log(const int logger_type, const int log_level, const std::string& message) +{ + log(logger_type, log_level, message, false); +} + +void py_logger::log(const int logger_type, const int log_level, const std::string& message, bool is_exception) +{ + if (!loggers_created_) + return; + + // Get the appropriate logger + PyObject* py_logger; + long current_log_level = 0; + switch (logger_type) + { + case GCODE_CONVERSION: + py_logger = py_arc_welder_gcode_conversion_logger; + current_log_level = gcode_conversion_log_level; + break; + default: + std::cout << "Logging.arc_welder_log - unknown logger_type.\r\n"; + PyErr_SetString(PyExc_ValueError, "Logging.arc_welder_log - unknown logger_type."); + return; + } + + if (!check_log_levels_real_time) + { + //std::cout << "Current Log Level: " << current_log_level << " requested:" << log_level; + // For speed we are going to check the log levels here before attempting to send any logging info to Python. + if (current_log_level > log_level) + { + return; + } + } + + PyObject* pyFunctionName = NULL; + + PyObject* error_type = NULL; + PyObject* error_value = NULL; + PyObject* error_traceback = NULL; + bool error_occurred = false; + if (is_exception) + { + // if an error has occurred, use the exception function to log the entire error + pyFunctionName = py_error_function_name; + if (PyErr_Occurred()) + { + error_occurred = true; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyErr_NormalizeException(&error_type, &error_value, &error_traceback); + } + } + else + { + switch (log_level) + { + case INFO: + pyFunctionName = py_info_function_name; + break; + case WARNING: + pyFunctionName = py_warn_function_name; + break; + case ERROR: + pyFunctionName = py_error_function_name; + break; + case DEBUG: + pyFunctionName = py_debug_function_name; + break; + case VERBOSE: + pyFunctionName = py_verbose_function_name; + break; + case CRITICAL: + pyFunctionName = py_critical_function_name; + break; + } + } + PyObject* pyMessage = gcode_arc_converter::PyUnicode_SafeFromString(message); + if (pyMessage == NULL) + { + std::cout << "Unable to convert the log message '" << message.c_str() << "' to a PyString/Unicode message.\r\n"; + PyErr_Format(PyExc_ValueError, + "Unable to convert the log message '%s' to a PyString/Unicode message.", message.c_str()); + return; + } + PyGILState_STATE state = PyGILState_Ensure(); + PyObject* ret_val = PyObject_CallMethodObjArgs(py_logger, pyFunctionName, pyMessage, NULL); + // We need to decref our message so that the GC can remove it. Maybe? + Py_DECREF(pyMessage); + PyGILState_Release(state); + if (ret_val == NULL) + { + if (!PyErr_Occurred()) + { + std::cout << "Logging.arc_welder_log - null was returned from the specified logger.\r\n"; + PyErr_SetString(PyExc_ValueError, "Logging.arc_welder_log - null was returned from the specified logger."); + } + else + { + std::cout << "Logging.arc_welder_log - null was returned from the specified logger and an error was detected.\r\n"; + // I'm not sure what else to do here since I can't log the error. I will print it + // so that it shows up in the console, but I can't log it, and there is no way to + // return an error. + PyErr_Print(); + PyErr_Clear(); + } + } + else + { + // Set the exception if we are doing exception logging. + if (is_exception) + { + if (error_occurred) + PyErr_Restore(error_type, error_value, error_traceback); + else + PyErr_SetString(PyExc_Exception, message.c_str()); + } + } + Py_XDECREF(ret_val); +} diff --git a/PyArcWelder/py_logger.h b/PyArcWelder/py_logger.h new file mode 100644 index 0000000..15fa5fa --- /dev/null +++ b/PyArcWelder/py_logger.h @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once + +#include +#include +#include +#include "logger.h" +#ifdef _DEBUG +#undef _DEBUG +#include +#define _DEBUG +#else +#include +#endif +#include +#include "python_helpers.h" +#include +enum py_loggers { GCODE_CONVERSION }; + +class py_logger : public logger { +public: + py_logger(std::vector names, std::vector levels); + virtual ~py_logger() { + } + void initialize_loggers(); + void set_internal_log_levels(bool check_real_time); + virtual void log(const int logger_type, const int log_level, const std::string& message); + virtual void log(const int logger_type, const int log_level, const std::string& message, bool is_exception); + virtual void log_exception(const int logger_type, const std::string& message); +private: + bool check_log_levels_real_time; + PyObject* py_logging_module; + PyObject* py_logging_configurator_name; + PyObject* py_logging_configurator; + PyObject* py_arc_welder_gcode_conversion_logger; + long gcode_conversion_log_level; + PyObject* py_info_function_name; + PyObject* py_warn_function_name; + PyObject* py_error_function_name; + PyObject* py_debug_function_name; + PyObject* py_verbose_function_name; + PyObject* py_critical_function_name; + PyObject* py_get_effective_level_function_name; +}; + diff --git a/PyArcWelder/python_helpers.cpp b/PyArcWelder/python_helpers.cpp new file mode 100644 index 0000000..acaae19 --- /dev/null +++ b/PyArcWelder/python_helpers.cpp @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "python_helpers.h" +namespace gcode_arc_converter { + + int PyUnicode_SafeCheck(PyObject* py) + { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_Check(py); +#else + return PyUnicode_Check(py); +#endif + } + + const char* PyUnicode_SafeAsString(PyObject* py) + { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_AsUTF8(py); +#else + return (char*)PyString_AsString(py); +#endif + } + + PyObject* PyString_SafeFromString(const char* str) + { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(str); +#else + return PyString_FromString(str); +#endif + } + + PyObject* PyUnicode_SafeFromString(std::string str) + { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromString(str.c_str()); +#else + // TODO: try PyUnicode_DecodeUnicodeEscape maybe? + //return PyUnicode_DecodeUTF8(str.c_str(), NULL, "replace"); + PyObject* pyString = PyString_FromString(str.c_str()); + if (pyString == NULL) + { + PyErr_Print(); + std::string message = "Unable to convert the c_str to a python string: "; + message += str; + PyErr_SetString(PyExc_ValueError, message.c_str()); + return NULL; + } + PyObject* pyUnicode = PyUnicode_FromEncodedObject(pyString, NULL, "replace"); + Py_DECREF(pyString); + return pyUnicode; +#endif + } + + double PyFloatOrInt_AsDouble(PyObject* py_double_or_int) + { + if (PyFloat_CheckExact(py_double_or_int)) + return PyFloat_AsDouble(py_double_or_int); +#if PY_MAJOR_VERSION < 3 + else if (PyInt_CheckExact(py_double_or_int)) + return static_cast(PyInt_AsLong(py_double_or_int)); +#endif + else if (PyLong_CheckExact(py_double_or_int)) + return static_cast(PyLong_AsLong(py_double_or_int)); + return 0; + } + + long PyIntOrLong_AsLong(PyObject* value) + { + long ret_val; +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(value)) + { + ret_val = PyInt_AsLong(value); + } + else + { + ret_val = PyLong_AsLong(value); + } +#else + ret_val = PyLong_AsLong(value); +#endif + return ret_val; + } + + bool PyFloatLongOrInt_Check(PyObject* py_object) + { + return ( + PyFloat_Check(py_object) || PyLong_Check(py_object) +#if PY_MAJOR_VERSION < 3 + || PyInt_Check(py_object) +#endif + ); + + } +} \ No newline at end of file diff --git a/PyArcWelder/python_helpers.h b/PyArcWelder/python_helpers.h new file mode 100644 index 0000000..93ad23d --- /dev/null +++ b/PyArcWelder/python_helpers.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once +#ifdef _DEBUG +#undef _DEBUG +#include +#define _DEBUG +#else +#include +#endif +#include +namespace gcode_arc_converter { + int PyUnicode_SafeCheck(PyObject* py); + const char* PyUnicode_SafeAsString(PyObject* py); + PyObject* PyString_SafeFromString(const char* str); + PyObject* PyUnicode_SafeFromString(std::string str); + double PyFloatOrInt_AsDouble(PyObject* py_double_or_int); + long PyIntOrLong_AsLong(PyObject* value); + bool PyFloatLongOrInt_Check(PyObject* value); +} \ No newline at end of file -- cgit v1.2.3