From 047cd38c2485dcb91e0e2a2734a264f1d9fcebdd Mon Sep 17 00:00:00 2001 From: Billy Huddleston Date: Sun, 30 Nov 2025 13:53:40 -0500 Subject: [PATCH] CAM: Add annotations support to Command constructor and Python bindings This commit introduces support for passing an 'annotations' dictionary to the Command class constructor and its Python bindings. The annotations dictionary can contain string or numeric values for each key, allowing for richer metadata on CAM commands. Each annotation must be provided as a key-value pair within a dictionary. Examples: cmd = Command("G1", {"X": 10.0, "Y": 5.0}, {"note": "Rapid move"}) cmd = Command("G2", {"X": 20.0, "Y": 15.0}, {"priority": 1}) cmd = Command("G3", {"X": 30.0, "Y": 25.0}, {"note": "Arc move", "speed": 1500}) cmd = Command("G0", {"X": 0.0, "Y": 0.0}, {}) cmd = Command("G0", {"X": 0.0, "Y": 0.0}) cmd = Command("G1", {"X": 10.0, "Y": 5.0}, annotations={"note": "Rapid move"}) src/Mod/CAM/App/Command.cpp: - Added new Command constructor accepting annotations src/Mod/CAM/App/Command.h: - Declared new Command constructor with annotations parameter src/Mod/CAM/App/Command.pyi: - Updated docstring to describe annotations argument src/Mod/CAM/App/CommandPyImp.cpp: - Extended Python init to parse and set annotations dictionary --- src/Mod/CAM/App/Command.cpp | 10 ++++++ src/Mod/CAM/App/Command.h | 5 +++ src/Mod/CAM/App/Command.pyi | 3 +- src/Mod/CAM/App/CommandPyImp.cpp | 55 ++++++++++++++++++++++++++++++-- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/Mod/CAM/App/Command.cpp b/src/Mod/CAM/App/Command.cpp index 5162b5f019..968159f843 100644 --- a/src/Mod/CAM/App/Command.cpp +++ b/src/Mod/CAM/App/Command.cpp @@ -47,6 +47,16 @@ Command::Command(const char* name, const std::map& paramete , Annotations() {} +Command::Command( + const char* name, + const std::map& parameters, + const std::map>& annotations +) + : Name(name) + , Parameters(parameters) + , Annotations(annotations) +{} + Command::Command() : Annotations() {} diff --git a/src/Mod/CAM/App/Command.h b/src/Mod/CAM/App/Command.h index 6c7cc3c52f..f93ca2fbab 100644 --- a/src/Mod/CAM/App/Command.h +++ b/src/Mod/CAM/App/Command.h @@ -44,6 +44,11 @@ public: // constructors Command(); Command(const char* name, const std::map& parameters); + Command( + const char* name, + const std::map& parameters, + const std::map>& annotations + ); ~Command() override; // from base class unsigned int getMemSize() const override; diff --git a/src/Mod/CAM/App/Command.pyi b/src/Mod/CAM/App/Command.pyi index ac6d9747f3..7cf426a027 100644 --- a/src/Mod/CAM/App/Command.pyi +++ b/src/Mod/CAM/App/Command.pyi @@ -13,10 +13,11 @@ from Base.Placement import Placement @class_declarations("mutable Py::Dict parameters_copy_dict;") class Command(Persistence): """ - Command([name],[parameters]): Represents a basic Gcode command + Command([name],[parameters],[annotations]): Represents a basic Gcode command name (optional) is the name of the command, ex. G1 parameters (optional) is a dictionary containing string:number pairs, or a placement, or a vector + annotations (optional) is a dictionary containing string:string or string:number pairs """ @constmethod diff --git a/src/Mod/CAM/App/CommandPyImp.cpp b/src/Mod/CAM/App/CommandPyImp.cpp index 53a9253727..81cc040d63 100644 --- a/src/Mod/CAM/App/CommandPyImp.cpp +++ b/src/Mod/CAM/App/CommandPyImp.cpp @@ -71,9 +71,20 @@ PyObject* CommandPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Pytho int CommandPy::PyInit(PyObject* args, PyObject* kwd) { PyObject* parameters = nullptr; + PyObject* annotations = nullptr; const char* name = ""; - static const std::array kwlist {"name", "parameters", nullptr}; - if (Base::Wrapped_ParseTupleAndKeywords(args, kwd, "|sO!", kwlist, &name, &PyDict_Type, ¶meters)) { + static const std::array kwlist {"name", "parameters", "annotations", nullptr}; + if (Base::Wrapped_ParseTupleAndKeywords( + args, + kwd, + "|sO!O!", + kwlist, + &name, + &PyDict_Type, + ¶meters, + &PyDict_Type, + &annotations + )) { std::string sname(name); boost::to_upper(sname); try { @@ -112,16 +123,54 @@ int CommandPy::PyInit(PyObject* args, PyObject* kwd) } getCommandPtr()->Parameters[ckey] = cvalue; } + + // Parse annotations + pos = 0; + while (annotations && PyDict_Next(annotations, &pos, &key, &value)) { + std::string ckey; + if (PyUnicode_Check(key)) { + ckey = PyUnicode_AsUTF8(key); + } + else { + PyErr_SetString( + PyExc_TypeError, + "The annotations dictionary can only contain string keys" + ); + return -1; + } + + if (PyUnicode_Check(value)) { + std::string cvalue = PyUnicode_AsUTF8(value); + getCommandPtr()->setAnnotation(ckey, cvalue); + } + else if (PyObject_TypeCheck(value, &(PyLong_Type))) { + double cvalue = (double)PyLong_AsLong(value); + getCommandPtr()->setAnnotation(ckey, cvalue); + } + else if (PyObject_TypeCheck(value, &(PyFloat_Type))) { + double cvalue = PyFloat_AsDouble(value); + getCommandPtr()->setAnnotation(ckey, cvalue); + } + else { + PyErr_SetString( + PyExc_TypeError, + "The annotations dictionary can only contain string or number values" + ); + return -1; + } + } + parameters_copy_dict.clear(); return 0; } PyErr_Clear(); // set by PyArg_ParseTuple() + static const std::array kwlist_placement {"name", "parameters", nullptr}; if (Base::Wrapped_ParseTupleAndKeywords( args, kwd, "|sO!", - kwlist, + kwlist_placement, &name, &(Base::PlacementPy::Type), ¶meters