diff options
author | David Crocker <dcrocker@eschertech.com> | 2020-01-22 23:46:13 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2020-01-22 23:46:13 +0300 |
commit | f11710c9cb9f16901437f39b5e07326f3d1ad6bf (patch) | |
tree | 27b6c95c661a56e3a5c3d24b85e934616697cf6b | |
parent | ee1ccb5074a1824175c29e3e28fc47d888c15091 (diff) |
Bug fix and more Object Model fields
Fixed bad JSON in M408 reports when no fans are configured
Added more object model fields: kinematics, machine coordinates
Refactored code that generates M408 responses
32 files changed, 484 insertions, 360 deletions
diff --git a/src/Configuration.h b/src/Configuration.h index 47c56bcd..c022fc94 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -243,7 +243,7 @@ constexpr size_t SHORT_GCODE_LENGTH = 61; // maximum length of a GCode that w // for the HTTP responder to return a status response. Otherwise DWC never gets to know that it needs to make a rr_reply call and the system deadlocks. #if SAME70 constexpr size_t OUTPUT_BUFFER_SIZE = 256; // How many bytes does each OutputBuffer hold? -constexpr size_t OUTPUT_BUFFER_COUNT = 32; // How many OutputBuffer instances do we have? +constexpr size_t OUTPUT_BUFFER_COUNT = 40; // How many OutputBuffer instances do we have? constexpr size_t RESERVED_OUTPUT_BUFFERS = 4; // Number of reserved output buffers after long responses, enough to hold a status response #elif SAM4E || SAM4S constexpr size_t OUTPUT_BUFFER_SIZE = 256; // How many bytes does each OutputBuffer hold? diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index e6c2abcf..1dbe5564 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -218,19 +218,13 @@ void GCodes::Reset() noexcept for (size_t i = 0; i < MaxAxes; ++i) { axisScaleFactors[i] = 1.0; -#if SUPPORT_WORKPLACE_COORDINATES for (size_t j = 0; j < NumCoordinateSystems; ++j) { workplaceCoordinates[j][i] = 0.0; } -#else - axisOffsets[i] = 0.0; -#endif } -#if SUPPORT_WORKPLACE_COORDINATES currentCoordinateSystem = 0; -#endif for (float& f : moveBuffer.coords) { @@ -2787,14 +2781,10 @@ void GCodes::GetCurrentCoordinates(const StringRef& s) const noexcept // Now the virtual extruder position, for Octoprint s.catf("E:%.3f ", (double)virtualExtruderPosition); - // Get the live machine coordinates, we'll need them later - float liveCoordinates[MaxAxesPlusExtruders]; - reprap.GetMove().LiveCoordinates(liveCoordinates, reprap.GetCurrentTool()); - // Now the extruder coordinates for (size_t i = 0; i < numExtruders; i++) { - s.catf("E%u:%.1f ", i, (double)liveCoordinates[ExtruderToLogicalDrive(i)]); + s.catf("E%u:%.1f ", i, (double)reprap.GetMove().LiveCoordinate(ExtruderToLogicalDrive(i), reprap.GetCurrentTool())); } // Print the axis stepper motor positions as Marlin does, as an aid to debugging. diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index df2e78ec..3df21230 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -195,8 +195,19 @@ public: GCodeResult StartSDTiming(GCodeBuffer& gb, const StringRef& reply) noexcept; // Start timing SD card file writing #endif -#if SUPPORT_WORKPLACE_COORDINATES unsigned int GetWorkplaceCoordinateSystemNumber() const noexcept { return currentCoordinateSystem + 1; } + + // This function is called by other functions to account correctly for workplace coordinates + inline float GetWorkplaceOffset(size_t axis) const noexcept + { + return workplaceCoordinates[currentCoordinateSystem][axis]; + } + +#if SUPPORT_OBJECT_MODEL + inline float GetWorkplaceOffset(size_t axis, size_t workplaceNumber) const noexcept + { + return workplaceCoordinates[workplaceNumber][axis]; + } #endif private: @@ -368,16 +379,6 @@ private: #endif Pwm_t ConvertLaserPwm(float reqVal) const noexcept; - // This function is called by other functions to account correctly for workplace coordinates, depending on whether the build configuration supports them. - inline float GetWorkplaceOffset(size_t axis) const noexcept - { -#if SUPPORT_WORKPLACE_COORDINATES - return workplaceCoordinates[currentCoordinateSystem][axis]; -#else - return axisOffsets[axis]; -#endif - } - #ifdef SERIAL_AUX_DEVICE static bool emergencyStopCommanded; static void CommandEmergencyStop(UARTClass *p); @@ -469,13 +470,8 @@ private: float rawExtruderTotalByDrive[MaxExtruders]; // Extrusion amount in the last G1 command with an E parameter when in absolute extrusion mode float rawExtruderTotal; // Total extrusion amount fed to Move class since starting print, before applying extrusion factor, summed over all drives -#if SUPPORT_WORKPLACE_COORDINATES - static const size_t NumCoordinateSystems = 9; unsigned int currentCoordinateSystem; // This is zero-based, where as the P parameter in the G10 command is 1-based float workplaceCoordinates[NumCoordinateSystems][MaxAxes]; // Workplace coordinate offsets -#else - float axisOffsets[MaxAxes]; // M206 axis offsets -#endif #if HAS_MASS_STORAGE FileData fileToPrint; // The next file to print diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp index cb7ffda6..5e6dad81 100644 --- a/src/GCodes/GCodes3.cpp +++ b/src/GCodes/GCodes3.cpp @@ -126,12 +126,7 @@ GCodeResult GCodes::OffsetAxes(GCodeBuffer& gb, const StringRef& reply) { if (gb.Seen(axisLetters[axis])) { -#if SUPPORT_WORKPLACE_COORDINATES - workplaceCoordinates[currentCoordinateSystem][axis] -#else - axisOffsets[axis] -#endif - = -gb.GetDistance(); + workplaceCoordinates[currentCoordinateSystem][axis] = -gb.GetDistance(); seen = true; } } @@ -141,13 +136,7 @@ GCodeResult GCodes::OffsetAxes(GCodeBuffer& gb, const StringRef& reply) reply.printf("Axis offsets:"); for (size_t axis = 0; axis < numVisibleAxes; axis++) { - reply.catf(" %c%.2f", axisLetters[axis], -#if SUPPORT_WORKPLACE_COORDINATES - -(double)(gb.InverseConvertDistance(workplaceCoordinates[0][axis])) -#else - -(double)(gb.InverseConvertDistance(axisOffsets[axis])) -#endif - ); + reply.catf(" %c%.2f", axisLetters[axis], -(double)(gb.InverseConvertDistance(workplaceCoordinates[0][axis]))); } } diff --git a/src/Movement/DDARing.cpp b/src/Movement/DDARing.cpp index 6be3bcd0..292a4526 100644 --- a/src/Movement/DDARing.cpp +++ b/src/Movement/DDARing.cpp @@ -408,6 +408,7 @@ void DDARing::CurrentMoveCompleted() noexcept { // Save the current motor coordinates, and the machine Cartesian coordinates if known liveCoordinatesValid = currentDda->FetchEndPosition(const_cast<int32_t*>(liveEndPoints), const_cast<float *>(liveCoordinates)); + liveCoordinatesChanged = true; const size_t numExtruders = reprap.GetGCodes().GetNumExtruders(); for (size_t extruder = 0; extruder < numExtruders; ++extruder) { @@ -479,12 +480,18 @@ void DDARing::AdjustMotorPositions(const float adjustment[], size_t numMotors) n } liveCoordinatesValid = false; // force the live XYZ position to be recalculated + liveCoordinatesChanged = true; } -// Return the current live XYZ and extruder coordinates +// Fetch the current live XYZ and extruder coordinates if they have changed since this was lass called // Interrupts are assumed enabled on entry -void DDARing::LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept +bool DDARing::LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept { + if (!liveCoordinatesChanged) + { + return false; + } + // The live coordinates and live endpoints are modified by the ISR, so be careful to get a self-consistent set of them const size_t numVisibleAxes = reprap.GetGCodes().GetVisibleAxes(); // do this before we disable interrupts const size_t numTotalAxes = reprap.GetGCodes().GetTotalAxes(); // do this before we disable interrupts @@ -493,6 +500,7 @@ void DDARing::LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept { // All coordinates are valid, so copy them across memcpy(m, const_cast<const float *>(liveCoordinates), sizeof(m[0]) * MaxAxesPlusExtruders); + liveCoordinatesChanged = false; cpu_irq_enable(); } else @@ -511,9 +519,11 @@ void DDARing::LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept { memcpy(const_cast<float *>(liveCoordinates), m, sizeof(m[0]) * numVisibleAxes); liveCoordinatesValid = true; + liveCoordinatesChanged = false; } cpu_irq_enable(); } + return true; } // These are the actual numbers that we want to be the coordinates, so don't transform them. @@ -525,6 +535,7 @@ void DDARing::SetLiveCoordinates(const float coords[MaxAxesPlusExtruders]) noexc liveCoordinates[drive] = coords[drive]; } liveCoordinatesValid = true; + liveCoordinatesChanged = true; reprap.GetMove().EndPointToMachine(coords, const_cast<int32_t *>(liveEndPoints), reprap.GetGCodes().GetVisibleAxes()); } @@ -536,6 +547,7 @@ void DDARing::ResetExtruderPositions() noexcept liveCoordinates[eDrive] = 0.0; } cpu_irq_enable(); + liveCoordinatesChanged = true; } float DDARing::GetRequestedSpeed() const noexcept diff --git a/src/Movement/DDARing.h b/src/Movement/DDARing.h index f5456145..b4e2a668 100644 --- a/src/Movement/DDARing.h +++ b/src/Movement/DDARing.h @@ -66,7 +66,7 @@ public: void GetCurrentMachinePosition(float m[MaxAxes], bool disableMotorMapping) const noexcept; // Get the current position in untransformed coords void SetPositions(const float move[MaxAxesPlusExtruders]) noexcept; // Force the machine coordinates to be these void AdjustMotorPositions(const float adjustment[], size_t numMotors) noexcept; // Perform motor endpoint adjustment - void LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept; // Gives the last point at the end of the last complete DDA transformed to user coords + bool LiveCoordinates(float m[MaxAxesPlusExtruders]) noexcept; // Fetch the last point at the end of the last completed DDA if it has changed since we last called this void SetLiveCoordinates(const float coords[MaxAxesPlusExtruders]) noexcept; // Force the live coordinates (see above) to be these void ResetExtruderPositions() noexcept; // Resets the extrusion amounts of the live coordinates @@ -96,7 +96,6 @@ private: StepTimer timer; // Timer object to control getting step interrupts volatile float liveCoordinates[MaxAxesPlusExtruders]; // The endpoint that the machine moved to in the last completed move - volatile bool liveCoordinatesValid; // True if the XYZ live coordinates are reliable (the extruder ones always are) volatile int32_t liveEndPoints[MaxAxesPlusExtruders]; // The XYZ endpoints of the last completed move in motor coordinates unsigned int numDdasInRing; @@ -114,7 +113,10 @@ private: float extrusionPending[MaxExtruders]; // Extrusion not done due to rounding to nearest step volatile int32_t extrusionAccumulators[MaxExtruders]; // Accumulated extruder motor steps volatile uint32_t extrudersPrintingSince; // The milliseconds clock time when extrudersPrinting was set to true + volatile bool extrudersPrinting; // Set whenever an extruder starts a printing move, cleared by a non-printing extruder move + volatile bool liveCoordinatesValid; // True if the XYZ live coordinates in liveCoordinates are reliable (the extruder ones always are) + volatile bool liveCoordinatesChanged; // True if the live coordinates have changed since LiveCoordinates was last called }; // Start the next move. Return true if laser or IO bits need to be active diff --git a/src/Movement/Kinematics/CoreKinematics.cpp b/src/Movement/Kinematics/CoreKinematics.cpp index 23b4c3d2..91b63794 100644 --- a/src/Movement/Kinematics/CoreKinematics.cpp +++ b/src/Movement/Kinematics/CoreKinematics.cpp @@ -12,6 +12,60 @@ #include "GCodes/GCodeBuffer/GCodeBuffer.h" #include "Movement/DDA.h" +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(CoreKinematics, __VA_ARGS__) + +constexpr ObjectModelArrayDescriptor CoreKinematics::forwardMatrixElementArrayDescriptor = +{ + nullptr, // no lock needed + [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue + { return ExpressionValue(((const CoreKinematics*)self)->forwardMatrix(context.GetIndex(1), context.GetIndex(0)), 3); } +}; + +constexpr ObjectModelArrayDescriptor CoreKinematics::inverseMatrixElementArrayDescriptor = +{ + nullptr, // no lock needed + [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue + { return ExpressionValue(((const CoreKinematics*)self)->forwardMatrix(context.GetIndex(1), context.GetIndex(0)), 3); } +}; + +constexpr ObjectModelArrayDescriptor CoreKinematics::forwardMatrixArrayDescriptor = +{ + nullptr, // no lock needed + [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetVisibleAxes(); }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&forwardMatrixElementArrayDescriptor); } +}; + +constexpr ObjectModelArrayDescriptor CoreKinematics::inverseMatrixArrayDescriptor = +{ + nullptr, // no lock needed + [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetGCodes().GetTotalAxes(); }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&inverseMatrixElementArrayDescriptor); } +}; + +constexpr ObjectModelTableEntry CoreKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "forwardMatrix", OBJECT_MODEL_FUNC_NOSELF(&forwardMatrixArrayDescriptor), ObjectModelEntryFlags::none }, + { "inverseMatrix", OBJECT_MODEL_FUNC_NOSELF(&inverseMatrixArrayDescriptor), ObjectModelEntryFlags::none }, + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t CoreKinematics::objectModelTableDescriptor[] = { 1, 3 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(CoreKinematics) + +#endif + // Recalculate internal variables following a configuration change void CoreKinematics::Recalc() noexcept { diff --git a/src/Movement/Kinematics/CoreKinematics.h b/src/Movement/Kinematics/CoreKinematics.h index a44dde09..27bcc5b4 100644 --- a/src/Movement/Kinematics/CoreKinematics.h +++ b/src/Movement/Kinematics/CoreKinematics.h @@ -28,6 +28,13 @@ public: AxesBitmap GetConnectedAxes(size_t axis) const noexcept override; AxesBitmap GetLinearAxes() const noexcept override; +protected: + DECLARE_OBJECT_MODEL + OBJECT_MODEL_ARRAY(forwardMatrix) + OBJECT_MODEL_ARRAY(forwardMatrixElement) + OBJECT_MODEL_ARRAY(inverseMatrix) + OBJECT_MODEL_ARRAY(inverseMatrixElement) + private: void Recalc() noexcept; // recalculate internal variables following a configuration change bool HasSharedMotor(size_t axis) const noexcept; // return true if the axis doesn't have a single dedicated motor diff --git a/src/Movement/Kinematics/FiveBarScaraKinematics.cpp b/src/Movement/Kinematics/FiveBarScaraKinematics.cpp index b35fd3b3..735b88e8 100644 --- a/src/Movement/Kinematics/FiveBarScaraKinematics.cpp +++ b/src/Movement/Kinematics/FiveBarScaraKinematics.cpp @@ -18,6 +18,28 @@ //#define debugPrintf if(0) debugPrintf +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(FiveBarScaraKinematics, __VA_ARGS__) + +constexpr ObjectModelTableEntry FiveBarScaraKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t FiveBarScaraKinematics::objectModelTableDescriptor[] = { 1, 1 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(FiveBarScaraKinematics) + +#endif + FiveBarScaraKinematics::FiveBarScaraKinematics() noexcept : ZLeadscrewKinematics(KinematicsType::scara, DefaultSegmentsPerSecond, DefaultMinSegmentSize, true) { diff --git a/src/Movement/Kinematics/FiveBarScaraKinematics.h b/src/Movement/Kinematics/FiveBarScaraKinematics.h index 337e0bcb..a88b419b 100644 --- a/src/Movement/Kinematics/FiveBarScaraKinematics.h +++ b/src/Movement/Kinematics/FiveBarScaraKinematics.h @@ -45,6 +45,9 @@ public: AxesBitmap GetLinearAxes() const noexcept; AxesBitmap GetConnectedAxes(size_t axis) const noexcept; +protected: + DECLARE_OBJECT_MODEL + private: static constexpr float DefaultSegmentsPerSecond = 100.0; static constexpr float DefaultMinSegmentSize = 0.2; diff --git a/src/Movement/Kinematics/HangprinterKinematics.cpp b/src/Movement/Kinematics/HangprinterKinematics.cpp index 717888e3..fadff2bf 100644 --- a/src/Movement/Kinematics/HangprinterKinematics.cpp +++ b/src/Movement/Kinematics/HangprinterKinematics.cpp @@ -20,6 +20,28 @@ constexpr float DefaultAnchorC[3] = {-2000.0, 1000.0, -100.0}; constexpr float DefaultAnchorDz = 3000.0; constexpr float DefaultPrintRadius = 1500.0; +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(HangprinterKinematics, __VA_ARGS__) + +constexpr ObjectModelTableEntry HangprinterKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t HangprinterKinematics::objectModelTableDescriptor[] = { 1, 1 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(HangprinterKinematics) + +#endif + // Constructor HangprinterKinematics::HangprinterKinematics() noexcept : Kinematics(KinematicsType::scara, DefaultSegmentsPerSecond, DefaultMinSegmentSize, true) diff --git a/src/Movement/Kinematics/HangprinterKinematics.h b/src/Movement/Kinematics/HangprinterKinematics.h index 4c585eac..7dc00de5 100644 --- a/src/Movement/Kinematics/HangprinterKinematics.h +++ b/src/Movement/Kinematics/HangprinterKinematics.h @@ -44,6 +44,9 @@ public: void LimitSpeedAndAcceleration(DDA& dda, const float *normalisedDirectionVector, size_t numVisibleAxes, bool continuousRotationShortcut) const noexcept override; AxesBitmap GetLinearAxes() const noexcept override; +protected: + DECLARE_OBJECT_MODEL + private: static constexpr float DefaultSegmentsPerSecond = 100.0; static constexpr float DefaultMinSegmentSize = 0.2; diff --git a/src/Movement/Kinematics/Kinematics.h b/src/Movement/Kinematics/Kinematics.h index 79a3d65b..0ecdc062 100644 --- a/src/Movement/Kinematics/Kinematics.h +++ b/src/Movement/Kinematics/Kinematics.h @@ -8,8 +8,9 @@ #ifndef SRC_MOVEMENT_KINEMATICS_H_ #define SRC_MOVEMENT_KINEMATICS_H_ -#include "RepRapFirmware.h" -#include "Math/Matrix.h" +#include <RepRapFirmware.h> +#include <Math/Matrix.h> +#include <ObjectModel/ObjectModel.h> inline floatc_t fcsquare(floatc_t a) { @@ -61,7 +62,7 @@ enum class LimitPositionResult : uint8_t adjustedAndIntermediateUnreachable // we adjusted the final position to make it reachable, but intermediate positions are still urreachable }; -class Kinematics +class Kinematics INHERIT_OBJECT_MODEL { public: // Functions that must be defined in each derived class that implements a kinematics @@ -208,6 +209,8 @@ public: float GetMinSegmentLength() const noexcept pre(UseSegmentation()) { return minSegmentLength; } protected: + DECLARE_OBJECT_MODEL_VIRTUAL + // Constructor. Pass segsPerSecond <= 0.0 to get non-segmented motion. Kinematics(KinematicsType t, float segsPerSecond, float minSegLength, bool doUseRawG0) noexcept; diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.cpp b/src/Movement/Kinematics/LinearDeltaKinematics.cpp index a284be8e..8e27dec5 100644 --- a/src/Movement/Kinematics/LinearDeltaKinematics.cpp +++ b/src/Movement/Kinematics/LinearDeltaKinematics.cpp @@ -13,6 +13,47 @@ #include "GCodes/GCodeBuffer/GCodeBuffer.h" #include <Math/Deviation.h> +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(LinearDeltaKinematics, __VA_ARGS__) + +constexpr ObjectModelArrayDescriptor LinearDeltaKinematics::towersArrayDescriptor = +{ + nullptr, // no lock needed + [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const LinearDeltaKinematics*)self)->numTowers; }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(self, 2); } +}; + +constexpr ObjectModelTableEntry LinearDeltaKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "deltaRadius", OBJECT_MODEL_FUNC(self->radius, 3), ObjectModelEntryFlags::none }, + { "homedHeight", OBJECT_MODEL_FUNC(self->homedHeight, 3), ObjectModelEntryFlags::none }, + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, + { "printRadius", OBJECT_MODEL_FUNC(self->printRadius, 1), ObjectModelEntryFlags::none }, + { "towers", OBJECT_MODEL_FUNC_NOSELF(&towersArrayDescriptor), ObjectModelEntryFlags::none }, + { "xTilt", OBJECT_MODEL_FUNC(self->xTilt, 3), ObjectModelEntryFlags::none }, + { "yTilt", OBJECT_MODEL_FUNC(self->yTilt, 3), ObjectModelEntryFlags::none }, + + // 1. tower members + { "diagonal", OBJECT_MODEL_FUNC(self->diagonals[context.GetLastIndex()], 3), ObjectModelEntryFlags::none }, + { "endstopAdjustment", OBJECT_MODEL_FUNC(self->endstopAdjustments[context.GetLastIndex()], 3), ObjectModelEntryFlags::none }, + { "xPos", OBJECT_MODEL_FUNC(self->towerX[context.GetLastIndex()], 3), ObjectModelEntryFlags::none }, + { "yPos", OBJECT_MODEL_FUNC(self->towerY[context.GetLastIndex()], 3), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t LinearDeltaKinematics::objectModelTableDescriptor[] = { 2, 7, 4 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(LinearDeltaKinematics) + +#endif + LinearDeltaKinematics::LinearDeltaKinematics() noexcept : Kinematics(KinematicsType::linearDelta, -1.0, 0.0, true), numTowers(UsualNumTowers) { Init(); diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.h b/src/Movement/Kinematics/LinearDeltaKinematics.h index 9765a0e2..40203863 100644 --- a/src/Movement/Kinematics/LinearDeltaKinematics.h +++ b/src/Movement/Kinematics/LinearDeltaKinematics.h @@ -57,6 +57,10 @@ public: float GetTowerX(size_t axis) const noexcept { return towerX[axis]; } float GetTowerY(size_t axis) const noexcept { return towerY[axis]; } +protected: + DECLARE_OBJECT_MODEL + OBJECT_MODEL_ARRAY(towers) + private: void Init() noexcept; void Recalc() noexcept; diff --git a/src/Movement/Kinematics/PolarKinematics.cpp b/src/Movement/Kinematics/PolarKinematics.cpp index 578d37df..b8dffce6 100644 --- a/src/Movement/Kinematics/PolarKinematics.cpp +++ b/src/Movement/Kinematics/PolarKinematics.cpp @@ -13,6 +13,28 @@ #include "GCodes/GCodeBuffer/GCodeBuffer.h" #include "Movement/DDA.h" +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(PolarKinematics, __VA_ARGS__) + +constexpr ObjectModelTableEntry PolarKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t PolarKinematics::objectModelTableDescriptor[] = { 1, 1 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(PolarKinematics) + +#endif + // Constructor PolarKinematics::PolarKinematics() noexcept : Kinematics(KinematicsType::polar, DefaultSegmentsPerSecond, DefaultMinSegmentSize, true), diff --git a/src/Movement/Kinematics/PolarKinematics.h b/src/Movement/Kinematics/PolarKinematics.h index 811f2789..cbbac0a8 100644 --- a/src/Movement/Kinematics/PolarKinematics.h +++ b/src/Movement/Kinematics/PolarKinematics.h @@ -34,6 +34,9 @@ public: bool IsContinuousRotationAxis(size_t axis) const noexcept override; AxesBitmap GetLinearAxes() const noexcept override; +protected: + DECLARE_OBJECT_MODEL + private: static constexpr float DefaultSegmentsPerSecond = 100.0; static constexpr float DefaultMinSegmentSize = 0.2; diff --git a/src/Movement/Kinematics/RotaryDeltaKinematics.cpp b/src/Movement/Kinematics/RotaryDeltaKinematics.cpp index 3f58c7c1..e6a1da3c 100644 --- a/src/Movement/Kinematics/RotaryDeltaKinematics.cpp +++ b/src/Movement/Kinematics/RotaryDeltaKinematics.cpp @@ -13,6 +13,28 @@ const float RotaryDeltaKinematics::NormalTowerAngles[DELTA_AXES] = { -150.0, -30.0, 90.0 }; +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(RotaryDeltaKinematics, __VA_ARGS__) + +constexpr ObjectModelTableEntry RotaryDeltaKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t RotaryDeltaKinematics::objectModelTableDescriptor[] = { 1, 1 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(RotaryDeltaKinematics) + +#endif + // Constructor RotaryDeltaKinematics::RotaryDeltaKinematics() noexcept : Kinematics(KinematicsType::rotaryDelta, DefaultSegmentsPerSecond, DefaultMinSegmentSize, false) { diff --git a/src/Movement/Kinematics/RotaryDeltaKinematics.h b/src/Movement/Kinematics/RotaryDeltaKinematics.h index e2e80f38..8283c9c9 100644 --- a/src/Movement/Kinematics/RotaryDeltaKinematics.h +++ b/src/Movement/Kinematics/RotaryDeltaKinematics.h @@ -44,6 +44,9 @@ public: void LimitSpeedAndAcceleration(DDA& dda, const float *normalisedDirectionVector, size_t numVisibleAxes, bool continuousRotationShortcut) const noexcept override; AxesBitmap GetLinearAxes() const noexcept override; +protected: + DECLARE_OBJECT_MODEL + private: void Init() noexcept; void Recalc() noexcept; diff --git a/src/Movement/Kinematics/ScaraKinematics.cpp b/src/Movement/Kinematics/ScaraKinematics.cpp index 156ecd1f..21deab11 100644 --- a/src/Movement/Kinematics/ScaraKinematics.cpp +++ b/src/Movement/Kinematics/ScaraKinematics.cpp @@ -14,6 +14,28 @@ #include <limits> +#if SUPPORT_OBJECT_MODEL + +// Object model table and functions +// Note: if using GCC version 7.3.1 20180622 and lambda functions are used in this table, you must compile this file with option -std=gnu++17. +// Otherwise the table will be allocated in RAM instead of flash, which wastes too much RAM. + +// Macro to build a standard lambda function that includes the necessary type conversions +#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(ScaraKinematics, __VA_ARGS__) + +constexpr ObjectModelTableEntry ScaraKinematics::objectModelTable[] = +{ + // Within each group, these entries must be in alphabetical order + // 0. kinematics members + { "name", OBJECT_MODEL_FUNC(self->GetName(false)), ObjectModelEntryFlags::none }, +}; + +constexpr uint8_t ScaraKinematics::objectModelTableDescriptor[] = { 1, 1 }; + +DEFINE_GET_OBJECT_MODEL_TABLE(ScaraKinematics) + +#endif + ScaraKinematics::ScaraKinematics() noexcept : ZLeadscrewKinematics(KinematicsType::scara, DefaultSegmentsPerSecond, DefaultMinSegmentSize, true), proximalArmLength(DefaultProximalArmLength), distalArmLength(DefaultDistalArmLength), xOffset(0.0), yOffset(0.0) diff --git a/src/Movement/Kinematics/ScaraKinematics.h b/src/Movement/Kinematics/ScaraKinematics.h index 9cba53f6..0bdbd644 100644 --- a/src/Movement/Kinematics/ScaraKinematics.h +++ b/src/Movement/Kinematics/ScaraKinematics.h @@ -45,6 +45,9 @@ public: bool IsContinuousRotationAxis(size_t axis) const noexcept override; AxesBitmap GetLinearAxes() const noexcept override; +protected: + DECLARE_OBJECT_MODEL + private: static constexpr float DefaultSegmentsPerSecond = 100.0; static constexpr float DefaultMinSegmentSize = 0.2; diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index 14600eff..3cc7ad00 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -79,10 +79,12 @@ constexpr ObjectModelTableEntry Move::objectModelTable[] = { "extruders", OBJECT_MODEL_FUNC_NOSELF(&extrudersArrayDescriptor), ObjectModelEntryFlags::none }, { "idle", OBJECT_MODEL_FUNC(self, 2), ObjectModelEntryFlags::none }, { "initialDeviation", OBJECT_MODEL_FUNC(self, 5), ObjectModelEntryFlags::none }, + { "kinematics", OBJECT_MODEL_FUNC(self->kinematics), ObjectModelEntryFlags::none }, { "meshDeviation", OBJECT_MODEL_FUNC(self, 6), ObjectModelEntryFlags::none }, { "printingAcceleration", OBJECT_MODEL_FUNC(self->maxPrintingAcceleration, 1), ObjectModelEntryFlags::none }, { "speedFactor", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetSpeedFactor(), 1), ObjectModelEntryFlags::live }, { "travelAcceleration", OBJECT_MODEL_FUNC(self->maxTravelAcceleration, 1), ObjectModelEntryFlags::none }, + { "workspaceNumber", OBJECT_MODEL_FUNC_NOSELF((int32_t)reprap.GetGCodes().GetWorkplaceCoordinateSystemNumber()), ObjectModelEntryFlags::none }, // 1. Move.Daa members { "enabled", OBJECT_MODEL_FUNC(self->drcEnabled), ObjectModelEntryFlags::none }, @@ -112,7 +114,7 @@ constexpr ObjectModelTableEntry Move::objectModelTable[] = { "mean", OBJECT_MODEL_FUNC(self->latestMeshDeviation.GetMean(), 3), ObjectModelEntryFlags::none }, }; -constexpr uint8_t Move::objectModelTableDescriptor[] = { 7, 11, 3, 2, 4, 2, 2, 2 }; +constexpr uint8_t Move::objectModelTableDescriptor[] = { 7, 13, 3, 2, 4, 2, 2, 2 }; DEFINE_GET_OBJECT_MODEL_TABLE(Move) @@ -160,7 +162,7 @@ void Move::Init() noexcept simulationMode = 0; longestGcodeWaitInterval = 0; - bedLevellingMoveAvailable = false; + bedLevellingMoveAvailable = liveCoordinatesUpToDate = false; active = true; } @@ -991,6 +993,19 @@ GCodeResult Move::ConfigureDynamicAcceleration(GCodeBuffer& gb, const StringRef& return GCodeResult::ok; } +// Return the current live XYZ and extruder coordinates +// Interrupts are assumed enabled on entry +float Move::LiveCoordinate(unsigned int axisOrExtruder, const Tool *tool) noexcept +{ + if (!liveCoordinatesUpToDate) + { + mainDDARing.LiveCoordinates(latestLiveCoordinates); + InverseAxisAndBedTransform(latestLiveCoordinates, tool); + liveCoordinatesUpToDate = true; + } + return latestLiveCoordinates[axisOrExtruder]; +} + #if SUPPORT_LASER || SUPPORT_IOBITS // Laser and IOBits support diff --git a/src/Movement/Move.h b/src/Movement/Move.h index de59f43d..0bcf1639 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -62,7 +62,7 @@ public: void GetCurrentUserPosition(float m[MaxAxes], uint8_t moveType, const Tool *tool) const noexcept; // Return the position (after all queued moves have been executed) in transformed coords int32_t GetEndPoint(size_t drive) const noexcept; // Get the current position of a motor - void LiveCoordinates(float m[MaxAxesPlusExtruders], const Tool *tool) noexcept; // Gives the last point at the end of the last complete DDA transformed to user coords + float LiveCoordinate(unsigned int axisOrExtruder, const Tool *tool) noexcept; // Gives the last point at the end of the last complete DDA bool AllMovesAreFinished() noexcept; // Is the look-ahead ring empty? Stops more moves being added as well. void DoLookAhead() noexcept __attribute__ ((hot)); // Run the look-ahead procedure void SetNewPosition(const float positionNow[MaxAxesPlusExtruders], bool doBedCompensation) noexcept; // Set the current position to be this @@ -252,18 +252,21 @@ private: Deviation initialCalibrationDeviation; Deviation latestMeshDeviation; - bool usingMesh; // True if we are using the height map, false if we are using the random probe point set - bool useTaper; // True to taper off the compensation - uint32_t idleTimeout; // How long we wait with no activity before we reduce motor currents to idle, in milliseconds uint32_t lastStateChangeTime; // The approximate time at which the state last changed, except we don't record timing->idle Kinematics *kinematics; // What kinematics we are using + StraightProbeSettings straightProbeSettings; // G38 straight probe settings + + float latestLiveCoordinates[MaxAxesPlusExtruders]; float specialMoveCoords[MaxDriversPerAxis]; // Amounts by which to move individual Z motors (leadscrew adjustment move) + bool bedLevellingMoveAvailable; // True if a leadscrew adjustment move is pending + bool liveCoordinatesUpToDate; + bool usingMesh; // True if we are using the height map, false if we are using the random probe point set + bool useTaper; // True to taper off the compensation - StraightProbeSettings straightProbeSettings; // G38 straight probe settings #if SUPPORT_LASER || SUPPORT_IOBITS static constexpr size_t LaserTaskStackWords = 100; // stack size in dwords for the laser and IOBits task @@ -292,14 +295,6 @@ inline void Move::AdjustMotorPositions(const float adjustment[], size_t numMotor mainDDARing.AdjustMotorPositions(adjustment, numMotors); } -// Return the current live XYZ and extruder coordinates -// Interrupts are assumed enabled on entry -inline void Move::LiveCoordinates(float m[MaxAxesPlusExtruders], const Tool *tool) noexcept -{ - mainDDARing.LiveCoordinates(m); - InverseAxisAndBedTransform(m, tool); -} - // These are the actual numbers that we want to be the coordinates, so don't transform them. // The caller must make sure that no moves are in progress or pending when calling this inline void Move::SetLiveCoordinates(const float coords[MaxAxesPlusExtruders]) noexcept diff --git a/src/ObjectModel/ObjectModel.cpp b/src/ObjectModel/ObjectModel.cpp index 7cd35a93..fc9a096a 100644 --- a/src/ObjectModel/ObjectModel.cpp +++ b/src/ObjectModel/ObjectModel.cpp @@ -14,14 +14,6 @@ #include <cstring> #include <General/SafeStrtod.h> -// Get the format string to use assuming this is a floating point number -const char *ExpressionValue::GetFloatFormatString() const noexcept -{ - static constexpr const char *FormatStrings[] = { "%.7f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f", "%.7f" }; - static_assert(ARRAY_SIZE(FormatStrings) == MaxFloatDigitsDisplayedAfterPoint + 1); - return FormatStrings[min<unsigned int>(param, MaxFloatDigitsDisplayedAfterPoint)]; -} - void ObjectExplorationContext::AddIndex(int32_t index) { if (numIndicesCounted == MaxIndices) @@ -244,7 +236,14 @@ void ObjectModel::ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& break; case TYPE_OF(float): - buf->catf(val.GetFloatFormatString(), (double)val.fVal); + if (val.fVal == 0.0) + { + buf->cat('0'); // replace 0.000... in JSON by 0. This is mostly to save space when writing workplace coordinates. + } + else + { + buf->catf(val.GetFloatFormatString(), (double)val.fVal); + } break; case TYPE_OF(uint32_t): diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h index 3d73d7d9..ad404be4 100644 --- a/src/ObjectModel/ObjectModel.h +++ b/src/ObjectModel/ObjectModel.h @@ -109,7 +109,7 @@ struct ExpressionValue uint64_t Get56BitValue() const noexcept { return ((uint64_t)param << 32) | uVal; } // Get the format string to use assuming this is a floating point number - const char *GetFloatFormatString() const noexcept; + const char *GetFloatFormatString() const noexcept { return ::GetFloatFormatString(param); } }; // Flags field of a table entry @@ -263,6 +263,9 @@ public: static const ObjectModelTableEntry objectModelTable[]; \ static const uint8_t objectModelTableDescriptor[]; +#define DECLARE_OBJECT_MODEL_VIRTUAL \ + virtual const ObjectModelTableEntry *GetObjectModelTable(const uint8_t*& descriptor) const noexcept override = 0; + #define DESCRIPTOR_OK(_class) (ARRAY_SIZE(_class::objectModelTableDescriptor) == _class::objectModelTableDescriptor[0] + 1) #define OMT_SIZE_OK(_class) (ARRAY_SIZE(_class::objectModelTable) == ArraySum(_class::objectModelTableDescriptor + 1, ARRAY_SIZE(_class::objectModelTableDescriptor) - 1)) #define OMT_ORDERING_OK(_class) (ObjectModelTableEntry::IsOrdered(_class::objectModelTableDescriptor, _class::objectModelTable)) @@ -288,6 +291,7 @@ public: #define INHERIT_OBJECT_MODEL // nothing #define DECLARE_OBJECT_MODEL // nothing +#define DECLARE_OBJECT_MODEL_VIRTUAL // nothing #define DEFINE_GET_OBJECT_MODEL_TABLE // nothing #define OBJECT_MODEL_ARRAY(_name) // nothing diff --git a/src/Platform.cpp b/src/Platform.cpp index 7be8d693..eee1e2c6 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -202,6 +202,14 @@ constexpr ObjectModelArrayDescriptor Platform::axisDriversArrayDescriptor = { return ExpressionValue(((const Platform*)self)->axisDrivers[context.GetIndex(1)].driverNumbers[context.GetLastIndex()]); } }; +constexpr ObjectModelArrayDescriptor Platform::workplaceOffsetsArrayDescriptor = +{ + nullptr, // no lock needed + [] (const ObjectModel *self, const ObjectExplorationContext& context) noexcept -> size_t { return NumCoordinateSystems; }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue + { return ExpressionValue(reprap.GetGCodes().GetWorkplaceOffset(context.GetIndex(1), context.GetIndex(0)), 3); } +}; + constexpr ObjectModelTableEntry Platform::objectModelTable[] = { // 0. boards[] members @@ -242,11 +250,13 @@ constexpr ObjectModelTableEntry Platform::objectModelTable[] = { "homed", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().IsAxisHomed(context.GetLastIndex())), ObjectModelEntryFlags::live }, { "jerk", OBJECT_MODEL_FUNC(MinutesToSeconds * self->GetInstantDv(context.GetLastIndex()), 1), ObjectModelEntryFlags::none }, { "letter", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetAxisLetters()[context.GetLastIndex()]), ObjectModelEntryFlags::none }, + { "machinePosition", OBJECT_MODEL_FUNC_NOSELF(reprap.GetMove().LiveCoordinate(context.GetLastIndex(), reprap.GetCurrentTool()), 3), ObjectModelEntryFlags::live }, { "max", OBJECT_MODEL_FUNC(self->AxisMaximum(context.GetLastIndex()), 1), ObjectModelEntryFlags::none }, { "min", OBJECT_MODEL_FUNC(self->AxisMinimum(context.GetLastIndex()), 1), ObjectModelEntryFlags::none }, { "speed", OBJECT_MODEL_FUNC(MinutesToSeconds * self->MaxFeedrate(context.GetLastIndex()), 1), ObjectModelEntryFlags::none }, { "userPosition", OBJECT_MODEL_FUNC_NOSELF(reprap.GetGCodes().GetUserCoordinate(context.GetLastIndex()), 3), ObjectModelEntryFlags::live }, { "visible", OBJECT_MODEL_FUNC_NOSELF(context.GetLastIndex() < (int32_t)reprap.GetGCodes().GetVisibleAxes()), ObjectModelEntryFlags::none }, + { "workplaceOffsets", OBJECT_MODEL_FUNC_NOSELF(&workplaceOffsetsArrayDescriptor), ObjectModelEntryFlags::none }, // 4. move.extruders[] members { "driver", OBJECT_MODEL_FUNC(self->extruderDrivers[context.GetLastIndex()]), ObjectModelEntryFlags::none }, @@ -274,7 +284,7 @@ constexpr uint8_t Platform::objectModelTableDescriptor[] = 9 + HAS_LINUX_INTERFACE + HAS_12V_MONITOR, // section 0: boards[] 3, // section 1: mcuTemp 3, // section 2: vIn - 10, // section 3: move.axes[] + 12, // section 3: move.axes[] 4, // section 4: move.extruders[] 3, // section 5: move.extruders[].nonlinear #if HAS_12V_MONITOR @@ -1236,9 +1246,7 @@ void Platform::Spin() noexcept String<StringLength100> scratchString; ListDrivers(scratchString.GetRef(), stalledDriversToLog); stalledDriversToLog.Clear(); - float liveCoordinates[MaxAxesPlusExtruders]; - reprap.GetMove().LiveCoordinates(liveCoordinates, reprap.GetCurrentTool()); - MessageF(WarningMessage, "Driver(s)%s stalled at Z height %.2f", scratchString.c_str(), (double)liveCoordinates[Z_AXIS]); + MessageF(WarningMessage, "Driver(s)%s stalled at Z height %.2f", scratchString.c_str(), (double)reprap.GetMove().LiveCoordinate(Z_AXIS, reprap.GetCurrentTool())); reported = true; } #endif diff --git a/src/Platform.h b/src/Platform.h index 7f387070..220ddd28 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -558,6 +558,7 @@ public: protected: DECLARE_OBJECT_MODEL OBJECT_MODEL_ARRAY(axisDrivers) + OBJECT_MODEL_ARRAY(workplaceOffsets) private: const char* InternalGetSysDir() const noexcept; // where the system files are - not thread-safe! diff --git a/src/RepRap.cpp b/src/RepRap.cpp index fb8c34fb..a4f3240f 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -1006,19 +1006,11 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe } // Machine status - char ch = GetStatusCharacter(); - response->printf("{\"status\":\"%c\",\"coords\":{", ch); + response->printf("{\"status\":\"%c\",\"coords\":{", GetStatusCharacter()); - // Coordinates - const size_t numVisibleAxes = gCodes->GetVisibleAxes(); // Homed axes - response->cat("\"axesHomed\":"); - ch = '['; - for (size_t axis = 0; axis < numVisibleAxes; ++axis) - { - response->catf("%c%d", ch, (gCodes->IsAxisHomed(axis)) ? 1 : 0); - ch = ','; - } + const size_t numVisibleAxes = gCodes->GetVisibleAxes(); + AppendIntArray(response, "axesHomed", numVisibleAxes, [this](size_t axis) noexcept { return (gCodes->IsAxisHomed(axis)) ? 1 : 0; }); // XYZ positions // Coordinates may be NaNs or infinities, for example when delta or SCARA homing fails. We must replace any NaNs or infinities to avoid JSON parsing errors. @@ -1027,57 +1019,23 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe // First the user coordinates #if SUPPORT_WORKPLACE_COORDINATES - response->catf("],\"wpl\":%u,\"xyz\":", gCodes->GetWorkplaceCoordinateSystemNumber()); + response->catf(",\"wpl\":%u,", gCodes->GetWorkplaceCoordinateSystemNumber()); #else - response->cat("],\"xyz\":"); + response->cat(','); #endif - ch = '['; - for (size_t axis = 0; axis < numVisibleAxes; axis++) - { - response->catf("%c%.3f", ch, HideNan(gCodes->GetUserCoordinate(axis))); - ch = ','; - } - // Now the machine coordinates and the extruder coordinates - { - float liveCoordinates[MaxAxesPlusExtruders]; -#if SUPPORT_ROLAND - if (roland->Active()) - { - roland->GetCurrentRolandPosition(liveCoordinates); - } - else -#endif - { - move->LiveCoordinates(liveCoordinates, currentTool); - } + AppendFloatArray(response, "xyz", numVisibleAxes, [this](size_t axis) noexcept { return gCodes->GetUserCoordinate(axis); }, 3); - // Machine coordinates - response->catf("],\"machine\":"); // announce the machine position - ch = '['; - for (size_t drive = 0; drive < numVisibleAxes; drive++) - { - response->catf("%c%.3f", ch, HideNan(liveCoordinates[drive])); - ch = ','; - } + // Machine coordinates + response->cat(','); + AppendFloatArray(response, "machine", numVisibleAxes, [this](size_t axis) noexcept { return move->LiveCoordinate(axis, currentTool); }, 3); - // Actual and theoretical extruder positions since power up, last G92 or last M23 - response->catf("],\"extr\":"); // announce actual extruder positions - ch = '['; - for (size_t extruder = 0; extruder < GetExtrudersInUse(); extruder++) - { - response->catf("%c%.1f", ch, HideNan(liveCoordinates[ExtruderToLogicalDrive(extruder)])); - ch = ','; - } - if (ch == '[') // we may have no extruders - { - response->cat(ch); - } - } + // Actual extruder positions since power up, last G92 or last M23 + response->cat(','); + AppendFloatArray(response, "extr", GetExtrudersInUse(), [this](size_t extruder) noexcept { return move->LiveCoordinate(ExtruderToLogicalDrive(extruder), currentTool); }, 1); // Current speeds - response->catf("]},\"speeds\":{\"requested\":%.1f,\"top\":%.1f}", - (double)move->GetRequestedSpeed(), (double)move->GetTopSpeed()); + response->catf("},\"speeds\":{\"requested\":%.1f,\"top\":%.1f}", (double)move->GetRequestedSpeed(), (double)move->GetTopSpeed()); // Current tool number response->catf(",\"currentTool\":%d", GetCurrentToolNumber()); @@ -1141,42 +1099,27 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe // Parameters { // Cooling fan values + response->cat(','); const size_t numFans = fansManager->GetNumFansToReport(); - response->cat(",\"fanPercent\":"); - ch = '['; - for (size_t i = 0; i < numFans; i++) - { - const float fanValue = fansManager->GetFanValue(i); - response->catf("%c%d", ch, (fanValue < 0.0) ? -1 : (int)lrintf(fanValue * 100.0)); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); + AppendIntArray(response, "fanPercent", numFans, + [this](size_t fan) noexcept + { + const float fanValue = fansManager->GetFanValue(fan); + return (fanValue < 0.0) ? -1 : (int)lrintf(fanValue * 100.0); + }); // Cooling fan names if (type == 2) { - response->cat(",\"fanNames\":"); - ch = '['; - for (size_t fan = 0; fan < numFans; fan++) - { - response->cat(ch); - ch = ','; - - const char *fanName = fansManager->GetFanName(fan); - response->EncodeString(fanName, true); - } - response->cat((ch == '[') ? "[]" : "]"); + response->cat(','); + AppendStringArray(response, "fanNames", numFans, [this](size_t fan) noexcept { return fansManager->GetFanName(fan); }); } // Speed and Extrusion factors in % - response->catf(",\"speedFactor\":%.1f,\"extrFactors\":", (double)(gCodes->GetSpeedFactor())); - ch = '['; - for (size_t extruder = 0; extruder < GetExtrudersInUse(); extruder++) - { - response->catf("%c%.1f", ch, (double)(gCodes->GetExtrusionFactor(extruder))); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); + response->catf(",\"speedFactor\":%.1f,", (double)(gCodes->GetSpeedFactor())); + AppendFloatArray(response, "extrFactors", GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetExtrusionFactor(extruder); }, 1); + + // Z babystepping response->catf(",\"babystep\":%.3f}", (double)gCodes->GetTotalBabyStepOffset(Z_AXIS)); // G-code reply sequence for webserver (sequence number for AUX is handled later) @@ -1195,22 +1138,16 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe switch (zp->GetSecondaryValues(v1)) { case 1: - response->catf("\"probeValue\":%d,\"probeSecondary\":[%d]", v0, v1); + response->catf("\"probeValue\":%d,\"probeSecondary\":[%d],", v0, v1); break; default: - response->catf("\"probeValue\":%d", v0); + response->catf("\"probeValue\":%d,", v0); break; } // Send fan RPM value(s) - response->cat(",\"fanRPM\":"); - char ch = '['; - for (size_t i = 0; i < numFans; ++i) - { - response->catf("%c%" PRIi32, ch, fansManager->GetFanRPM(i)); - ch = ','; - } - response->cat("]}"); // end fan RPMs and sensors + AppendIntArray(response, "fanRPM", numFans, [this](size_t fan) noexcept { return (int)fansManager->GetFanRPM(fan); }); + response->cat('}'); } /* Temperatures */ @@ -1247,39 +1184,19 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe /* Heaters */ // Current temperatures - response->cat("\"current\":"); - ch = '['; { const size_t numHeaters = heat->GetNumHeatersToReport(); - for (size_t heater = 0; heater < numHeaters; heater++) - { - response->catf("%c%.1f", ch, (double)heat->GetHeaterTemperature(heater)); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); + AppendFloatArray(response, "current", numHeaters, [this](size_t heater) noexcept { return heat->GetHeaterTemperature(heater); }, 1); // Current states - response->cat(",\"state\":"); - ch = '['; - for (size_t heater = 0; heater < numHeaters; heater++) - { - response->catf("%c%u", ch, heat->GetStatus(heater).ToBaseType()); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); + response->cat(','); + AppendIntArray(response, "state", numHeaters, [this](size_t heater) noexcept { return (int)heat->GetStatus(heater).ToBaseType(); }); // Names of the sensors use to control heaters if (type == 2) { - response->cat(",\"names\":"); - ch = '['; - for (size_t heater = 0; heater < numHeaters; heater++) - { - response->cat(ch); - ch = ','; - response->EncodeString(GetHeat().GetHeaterSensorName(heater), true); - } - response->cat((ch == '[') ? "[]" : "]"); + response->cat(','); + AppendStringArray(response, "names", numHeaters, [this](size_t heater) noexcept { return heat->GetHeaterSensorName(heater); }); } } @@ -1289,14 +1206,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe ReadLocker lock(toolListLock); for (const Tool *tool = toolList; tool != nullptr; tool = tool->Next()) { - ch = '['; - for (size_t heater = 0; heater < tool->heaterCount; heater++) - { - response->catf("%c%.1f", ch, (double)tool->activeTemperatures[heater]); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); - + AppendFloatArray(response, nullptr, tool->heaterCount, [tool](unsigned int n) noexcept { return tool->activeTemperatures[n]; }, 1); if (tool->Next() != nullptr) { response->cat(','); @@ -1306,14 +1216,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe response->cat("],\"standby\":["); for (const Tool *tool = toolList; tool != nullptr; tool = tool->Next()) { - ch = '['; - for (size_t heater = 0; heater < tool->heaterCount; heater++) - { - response->catf("%c%.1f", ch, (double)tool->standbyTemperatures[heater]); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); - + AppendFloatArray(response, nullptr, tool->heaterCount, [tool](unsigned int n) noexcept { return tool->standbyTemperatures[n]; }, 1); if (tool->Next() != nullptr) { response->cat(','); @@ -1368,21 +1271,18 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe // Spindles if (gCodes->GetMachineType() == MachineType::cnc || type == 2) { - int lastConfiguredSpindle = -1; - for (size_t spindle = 0; spindle < MaxSpindles; spindle++) + size_t numSpindles = MaxSpindles; + while (numSpindles != 0 && platform->AccessSpindle(numSpindles - 1).GetToolNumber() == -1) { - if (platform->AccessSpindle(spindle).GetToolNumber() != -1) - { - lastConfiguredSpindle = spindle; - } + --numSpindles; } - if (lastConfiguredSpindle != -1) + if (numSpindles != 0) { response->cat(",\"spindles\":["); - for (int i = 0; i <= lastConfiguredSpindle; i++) + for (size_t i = 0; i < numSpindles; i++) { - if (i > 0) + if (i != 0) { response->cat(','); } @@ -1497,40 +1397,26 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe for (Tool *tool = toolList; tool != nullptr; tool = tool->Next()) { // Number - response->catf("{\"number\":%d", tool->Number()); + response->catf("{\"number\":%d,", tool->Number()); // Name const char *toolName = tool->GetName(); if (toolName[0] != 0) { - response->cat(",\"name\":"); + response->cat("\"name\":"); response->EncodeString(toolName, false); + response->cat(','); } // Heaters - response->cat(",\"heaters\":["); - for (size_t heater = 0; heater < tool->HeaterCount(); heater++) - { - response->catf("%d", tool->Heater(heater)); - if (heater + 1 < tool->HeaterCount()) - { - response->cat(','); - } - } + AppendIntArray(response, "heaters", tool->HeaterCount(), [tool](size_t heater) noexcept { return tool->Heater(heater); }); // Extruder drives - response->cat("],\"drives\":["); - for (size_t drive = 0; drive < tool->DriveCount(); drive++) - { - response->catf("%d", tool->Drive(drive)); - if (drive + 1 < tool->DriveCount()) - { - response->cat(','); - } - } + response->cat(','); + AppendIntArray(response, "drives", tool->DriveCount(), [tool](size_t drive) noexcept { return tool->Drive(drive); }); // Axis mapping - response->cat("],\"axisMap\":[["); + response->cat(",\"axisMap\":[["); tool->GetXAxisMap().Iterate ([&response](unsigned int xi, bool first) noexcept { @@ -1567,14 +1453,11 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe } // Offsets - response->cat(",\"offsets\":["); - for (size_t i = 0; i < numVisibleAxes; i++) - { - response->catf((i == 0) ? "%.2f" : ",%.2f", (double)tool->GetOffset(i)); - } + response->cat(','); + AppendFloatArray(response, "offsets", numVisibleAxes, [tool](size_t axis) noexcept { return tool->GetOffset(axis); }, 2); // Do we have any more tools? - response->cat((tool->Next() != nullptr) ? "]}," : "]}"); + response->cat((tool->Next() != nullptr) ? "}," : "}"); } response->cat(']'); } @@ -1609,23 +1492,13 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe response->catf(",\"currentLayer\":%d", printMonitor->GetCurrentLayer()); // Current Layer Time - response->catf(",\"currentLayerTime\":%.1f", (double)(printMonitor->GetCurrentLayerTime())); + response->catf(",\"currentLayerTime\":%.1f,", (double)(printMonitor->GetCurrentLayerTime())); // Raw Extruder Positions - response->cat(",\"extrRaw\":"); - ch = '['; - for (size_t extruder = 0; extruder < GetExtrudersInUse(); extruder++) // loop through extruders - { - response->catf("%c%.1f", ch, (double)(gCodes->GetRawExtruderTotalByDrive(extruder))); - ch = ','; - } - if (ch == '[') - { - response->cat(ch); // no extruders - } + AppendFloatArray(response, "extrRaw", GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetRawExtruderTotalByDrive(extruder); }, 1); // Fraction of file printed - response->catf("],\"fractionPrinted\":%.1f", (double)((printMonitor->IsPrinting()) ? (printMonitor->FractionOfFilePrinted() * 100.0) : 0.0)); + response->catf(",\"fractionPrinted\":%.1f", (double)((printMonitor->IsPrinting()) ? (printMonitor->FractionOfFilePrinted() * 100.0) : 0.0)); // Byte position of the file being printed response->catf(",\"filePosition\":%lu", gCodes->GetFilePosition()); @@ -1646,13 +1519,10 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) noe /* Print Time Estimations */ { // Based on file progress - response->catf(",\"timesLeft\":{\"file\":%.1f", (double)(printMonitor->EstimateTimeLeft(fileBased))); - - // Based on filament usage - response->catf(",\"filament\":%.1f", (double)(printMonitor->EstimateTimeLeft(filamentBased))); - - // Based on layers - response->catf(",\"layer\":%.1f}", (double)(printMonitor->EstimateTimeLeft(layerBased))); + response->catf(",\"timesLeft\":{\"file\":%.1f,\"filament\":%.1f,\"layer\":%.1f}", + (double)(printMonitor->EstimateTimeLeft(fileBased)), + (double)(printMonitor->EstimateTimeLeft(filamentBased)), + (double)(printMonitor->EstimateTimeLeft(layerBased))); } } @@ -1690,43 +1560,23 @@ OutputBuffer *RepRap::GetConfigResponse() noexcept const size_t numAxes = gCodes->GetVisibleAxes(); // Axis minima - response->copy("{\"axisMins\":"); - char ch = '['; - for (size_t axis = 0; axis < numAxes; axis++) - { - response->catf("%c%.2f", ch, (double)(platform->AxisMinimum(axis))); - ch = ','; - } + response->copy('{'); + AppendFloatArray(response, "axisMins", numAxes, [this](size_t axis) noexcept { return platform->AxisMinimum(axis); }, 2); // Axis maxima - response->cat("],\"axisMaxes\":"); - ch = '['; - for (size_t axis = 0; axis < numAxes; axis++) - { - response->catf("%c%.2f", ch, (double)(platform->AxisMaximum(axis))); - ch = ','; - } + response->cat(','); + AppendFloatArray(response, "axisMaxes", numAxes, [this](size_t axis) noexcept { return platform->AxisMaximum(axis); }, 2); // Accelerations - response->cat("],\"accelerations\":"); - ch = '['; - for (size_t drive = 0; drive < MaxAxesPlusExtruders; drive++) - { - response->catf("%c%.2f", ch, (double)(platform->Acceleration(drive))); - ch = ','; - } + response->cat(','); + AppendFloatArray(response, "accelerations", MaxAxesPlusExtruders, [this](size_t drive) noexcept { return platform->Acceleration(drive); }, 2); // Motor currents - response->cat("],\"currents\":"); - ch = '['; - for (size_t drive = 0; drive < MaxAxesPlusExtruders; drive++) - { - response->catf("%c%d", ch, (int)platform->GetMotorCurrent(drive, 906)); - ch = ','; - } + response->cat(','); + AppendIntArray(response, "currents", MaxAxesPlusExtruders, [this](size_t drive) noexcept { return (int)platform->GetMotorCurrent(drive, 906); }); // Firmware details - response->catf("],\"firmwareElectronics\":\"%s", platform->GetElectronicsString()); + response->catf(",\"firmwareElectronics\":\"%s", platform->GetElectronicsString()); #ifdef DUET_NG const char* expansionName = DuetExpansion::GetExpansionBoardName(); if (expansionName != nullptr) @@ -1774,28 +1624,17 @@ OutputBuffer *RepRap::GetConfigResponse() noexcept // Motor idle parameters response->catf(",\"idleCurrentFactor\":%.1f", (double)(platform->GetIdleCurrentFactor() * 100.0)); - response->catf(",\"idleTimeout\":%.1f", (double)(move->IdleTimeout())); + response->catf(",\"idleTimeout\":%.1f,", (double)(move->IdleTimeout())); // Minimum feedrates - response->cat(",\"minFeedrates\":"); - ch = '['; - for (size_t drive = 0; drive < MaxAxesPlusExtruders; drive++) - { - response->catf("%c%.2f", ch, (double)(platform->GetInstantDv(drive))); - ch = ','; - } + AppendFloatArray(response, "minFeedrates", MaxAxesPlusExtruders, [this](size_t drive) noexcept { return platform->GetInstantDv(drive); }, 2); // Maximum feedrates - response->cat("],\"maxFeedrates\":"); - ch = '['; - for (size_t drive = 0; drive < MaxAxesPlusExtruders; drive++) - { - response->catf("%c%.2f", ch, (double)(platform->MaxFeedrate(drive))); - ch = ','; - } + response->cat(','); + AppendFloatArray(response, "maxFeedrates", MaxAxesPlusExtruders, [this](size_t drive) noexcept { return platform->MaxFeedrate(drive); }, 2); // Config file is no longer included, because we can use rr_configfile or M503 instead - response->cat("]}"); + response->cat('}'); return response; } @@ -1861,41 +1700,19 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) noexcept { response->catf(",%u", heat->GetStatus(heater).ToBaseType()); } - response->cat(']'); + response->cat("],"); - // Send XYZ positions + // User coordinates const size_t numVisibleAxes = gCodes->GetVisibleAxes(); + AppendFloatArray(response, "pos", numVisibleAxes, [this](size_t axis) noexcept { return gCodes->GetUserCoordinate(axis); }, 3); - // First the user coordinates - response->catf(",\"pos\":"); // announce the user position - ch = '['; - for (size_t axis = 0; axis < numVisibleAxes; axis++) - { - // Coordinates may be NaNs, for example when delta or SCARA homing fails. Replace any NaNs or infinities by 9999.9 to prevent JSON parsing errors. - response->catf("%c%.3f", ch, HideNan(gCodes->GetUserCoordinate(axis))); - ch = ','; - } - - // Now the machine coordinates - float liveCoordinates[MaxAxesPlusExtruders]; - move->LiveCoordinates(liveCoordinates, currentTool); - response->catf("],\"machine\":"); // announce the machine position - ch = '['; - for (size_t drive = 0; drive < numVisibleAxes; drive++) - { - response->catf("%c%.3f", ch, HideNan(liveCoordinates[drive])); - ch = ','; - } + // Machine coordinates + response->cat(','); + AppendFloatArray(response, "machine", numVisibleAxes, [this](size_t axis) noexcept { return move->LiveCoordinate(axis, currentTool); }, 3); // Send the speed and extruder override factors - response->catf("],\"sfactor\":%.2f,\"efactor\":", (double)(gCodes->GetSpeedFactor())); - ch = '['; - for (size_t i = 0; i < GetExtrudersInUse(); ++i) - { - response->catf("%c%.2f", ch, (double)(gCodes->GetExtrusionFactor(i))); - ch = ','; - } - response->cat((ch == '[') ? "[]" : "]"); + response->catf(",\"sfactor\":%.2f,", (double)(gCodes->GetSpeedFactor())); + AppendFloatArray(response, "efactor", GetExtrudersInUse(), [this](size_t extruder) noexcept { return gCodes->GetExtrusionFactor(extruder); }, 2); // Send the baby stepping offset response->catf(",\"babystep\":%.03f", (double)(gCodes->GetTotalBabyStepOffset(Z_AXIS))); @@ -1925,27 +1742,14 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) noexcept const float fanValue = fansManager->GetFanValue(i); response->catf(",%d", (fanValue < 0.0) ? -1 : (int)lrintf(fanValue * 100.0)); } - response->cat(']'); + response->cat("],"); // Send fan RPM value(s) - response->cat(",\"fanRPM\":"); - ch = '['; - for (size_t i = 0; i < MaxFans; ++i) - { - response->catf("%c%" PRIi32, ch, fansManager->GetFanRPM(i)); - ch = ','; - } - response->cat(']'); + AppendIntArray(response, "fanRPM", fansManager->GetNumFansToReport(), [this](size_t fan) { return (int)fansManager->GetFanRPM(fan);}); // Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false. - response->cat(",\"homed\":"); - ch = '['; - for (size_t axis = 0; axis < numVisibleAxes; ++axis) - { - response->catf("%c%d", ch, (gCodes->IsAxisHomed(axis)) ? 1 : 0); - ch = ','; - } - response->cat(']'); + response->cat(','); + AppendIntArray(response, "homed", numVisibleAxes, [this](size_t axis) noexcept { return (gCodes->IsAxisHomed(axis)) ? 1 : 0; }); if (printMonitor->IsPrinting()) { @@ -2276,6 +2080,62 @@ bool RepRap::GetFileInfoResponse(const char *filename, OutputBuffer *&response, return true; } +// Helper functions to write JSON arrays +// Append float array using 1 decimal place +void RepRap::AppendFloatArray(OutputBuffer *buf, const char *name, size_t numValues, std::function<float(size_t)> func, unsigned int numDecimalDigits) +{ + if (name != nullptr) + { + buf->catf("\"%s\":", name); + } + buf->cat('['); + for (size_t i = 0; i < numValues; ++i) + { + if (i != 0) + { + buf->cat(','); + } + buf->catf(GetFloatFormatString(numDecimalDigits), HideNan(func(i))); + } + buf->cat(']'); +} + +void RepRap::AppendIntArray(OutputBuffer *buf, const char *name, size_t numValues, std::function<int(size_t)> func) +{ + if (name != nullptr) + { + buf->catf("\"%s\":", name); + } + buf->cat('['); + for (size_t i = 0; i < numValues; ++i) + { + if (i != 0) + { + buf->cat(','); + } + buf->catf("%d", func(i)); + } + buf->cat(']'); +} + +void RepRap::AppendStringArray(OutputBuffer *buf, const char *name, size_t numValues, std::function<const char *(size_t)> func) +{ + if (name != nullptr) + { + buf->catf("\"%s\":", name); + } + buf->cat('['); + for (size_t i = 0; i < numValues; ++i) + { + if (i != 0) + { + buf->cat(','); + } + buf->EncodeString(func(i), true); + } + buf->cat(']'); +} + #if SUPPORT_OBJECT_MODEL // Return a query into the object model, or return nullptr if no buffer available diff --git a/src/RepRap.h b/src/RepRap.h index 6993bd2f..a4c15066 100644 --- a/src/RepRap.h +++ b/src/RepRap.h @@ -29,6 +29,7 @@ Licence: GPL #include "Fans/FansManager.h" #include "Tools/Tool.h" #include "SoftwareReset.h" +#include <functional> enum class ResponseSource { @@ -175,6 +176,9 @@ protected: private: static void EncodeString(StringRef& response, const char* src, size_t spaceToLeave, bool allowControlChars = false, char prefix = 0) noexcept; + static void AppendFloatArray(OutputBuffer *buf, const char *name, size_t numValues, std::function<float(size_t)> func, unsigned int numDecimalDigits); + static void AppendIntArray(OutputBuffer *buf, const char *name, size_t numValues, std::function<int(size_t)> func); + static void AppendStringArray(OutputBuffer *buf, const char *name, size_t numValues, std::function<const char *(size_t)> func); size_t GetStatusIndex() const noexcept; char GetStatusCharacter() const noexcept; diff --git a/src/RepRapFirmware.cpp b/src/RepRapFirmware.cpp index c01fea1f..3e17f4f0 100644 --- a/src/RepRapFirmware.cpp +++ b/src/RepRapFirmware.cpp @@ -172,6 +172,14 @@ Licence: GPL RepRap reprap; +// Get the format string to use for printing a floating point number to the specified number of decimal digits. Zero means the maximum sensible number. +const char *GetFloatFormatString(unsigned int numDigitsAfterPoint) noexcept +{ + static constexpr const char *FormatStrings[] = { "%.7f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f", "%.7f" }; + static_assert(ARRAY_SIZE(FormatStrings) == MaxFloatDigitsDisplayedAfterPoint + 1); + return FormatStrings[min<unsigned int>(numDigitsAfterPoint, MaxFloatDigitsDisplayedAfterPoint)]; +} + static const char * const moduleName[] = { "Platform", diff --git a/src/RepRapFirmware.h b/src/RepRapFirmware.h index 5fab76eb..6bf9e952 100644 --- a/src/RepRapFirmware.h +++ b/src/RepRapFirmware.h @@ -439,6 +439,13 @@ constexpr float DegreesToRadians = 3.141592653589793/180.0; constexpr float RadiansToDegrees = 180.0/3.141592653589793; constexpr unsigned int MaxFloatDigitsDisplayedAfterPoint = 7; +const char *GetFloatFormatString(unsigned int numDigitsAfterPoint) noexcept; + +#if SUPPORT_WORKPLACE_COORDINATES +constexpr size_t NumCoordinateSystems = 9; // G54 up to G59.3 +#else +constexpr size_t NumCoordinateSystems = 1; +#endif #define DEGREE_SYMBOL "\xC2\xB0" // degree-symbol encoding in UTF8 diff --git a/src/Version.h b/src/Version.h index e545e367..3ac41602 100644 --- a/src/Version.h +++ b/src/Version.h @@ -20,7 +20,7 @@ #endif #ifndef DATE -# define DATE "2020-01-20b1" +# define DATE "2020-01-22b2" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman, printm3d" |