diff --git a/src/GCodes/GCodeBuffer/ExpressionParser.h b/src/GCodes/GCodeBuffer/ExpressionParser.h index 38de851733..30e4148cea 100644 --- a/src/GCodes/GCodeBuffer/ExpressionParser.h +++ b/src/GCodes/GCodeBuffer/ExpressionParser.h @@ -61,7 +61,7 @@ class ExpressionParser void ParseDriverIdArray(DriverId arr[], size_t& length) THROWS(GCodeException); void CheckForExtraCharacters() THROWS(GCodeException); - const char *GetEndptr() const noexcept { return currentp; } + const char *_ecv_array GetEndptr() const noexcept { return currentp; } private: [[noreturn]] void __attribute__((noinline)) ThrowParseException(const char *_ecv_array str) const THROWS(GCodeException); diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp index 9f9c541bb9..151275b3fc 100644 --- a/src/GCodes/GCodeBuffer/StringParser.cpp +++ b/src/GCodes/GCodeBuffer/StringParser.cpp @@ -961,7 +961,7 @@ void StringParser::DecodeCommand() noexcept if (cl == '\'') // check for a lowercase axis letter in Fanuc mode { ++commandStart; - cl = tolower(gb.buffer[commandStart]); + cl = (char)tolower(gb.buffer[commandStart]); } else { @@ -1202,7 +1202,7 @@ FilePosition StringParser::GetFilePosition() const noexcept return noFilePosition; } -const char* StringParser::DataStart() const noexcept +const char *_ecv_array StringParser::DataStart() const noexcept { return gb.buffer + commandStart; } @@ -1259,7 +1259,7 @@ bool StringParser::Seen(char c) noexcept if ( inBrackets == 0 && (char)toupper(b) == c && escaped == wantLowerCase - && (c != 'E' || (unsigned int)readPointer == parameterStart || !isdigit(gb.buffer[readPointer - 1])) + && (c != 'E' || (unsigned int)readPointer == parameterStart || !(bool)isdigit(gb.buffer[readPointer - 1])) ) { ++readPointer; @@ -1506,10 +1506,10 @@ void StringParser::InternalGetQuotedString(const StringRef& str) THROWS(GCodeExc } else if (c == '\'') { - if (isalpha(gb.buffer[readPointer])) + if ((bool)isalpha(gb.buffer[readPointer])) { // Single quote before an alphabetic character forces that character to lower case - c = tolower(gb.buffer[readPointer++]); + c = (char)tolower(gb.buffer[readPointer++]); } else if (gb.buffer[readPointer] == c) { @@ -1921,7 +1921,7 @@ bool StringParser::FileEnded() noexcept } // Check that a number was found. If it was, advance readPointer past it. Otherwise throw an exception. -void StringParser::CheckNumberFound(const char *endptr) THROWS(GCodeException) +void StringParser::CheckNumberFound(const char *_ecv_array endptr) THROWS(GCodeException) { if (endptr == gb.buffer + readPointer) { @@ -1941,7 +1941,7 @@ float StringParser::ReadFloatValue() THROWS(GCodeException) return val; } - const char *endptr; + const char *_ecv_array endptr; const float rslt = SafeStrtof(gb.buffer + readPointer, &endptr); CheckNumberFound(endptr); return rslt; @@ -1958,7 +1958,7 @@ uint32_t StringParser::ReadUIValue() THROWS(GCodeException) } // Allow "0xNNNN" or "xNNNN" where NNNN are hex digits. We could stop supporting this because we already support {0xNNNN}. - const char *endptr; + const char *_ecv_array endptr; const uint32_t rslt = StrToU32(gb.buffer + readPointer, &endptr); CheckNumberFound(endptr); return rslt; @@ -1974,7 +1974,7 @@ int32_t StringParser::ReadIValue() THROWS(GCodeException) return val; } - const char *endptr; + const char *_ecv_array endptr; const int32_t rslt = StrToI32(gb.buffer + readPointer, &endptr); CheckNumberFound(endptr); return rslt; @@ -2064,7 +2064,7 @@ void StringParser::AddParameters(VariableSet& vs, int codeRunning) THROWS(GCodeE { throw ConstructParseException("invalid value for parameter '%c'", (uint32_t)c); } - ExpressionParser parser(gb, &gb.buffer[readPointer], &gb.buffer[commandEnd]); + ExpressionParser parser(gb, gb.buffer + readPointer, gb.buffer + commandEnd); ExpressionValue ev = parser.Parse(); char paramName[2] = { letter, 0 }; vs.InsertNewParameter(paramName, ev); diff --git a/src/GCodes/GCodeBuffer/StringParser.h b/src/GCodes/GCodeBuffer/StringParser.h index 6c64b83e5c..4f8cc64158 100644 --- a/src/GCodes/GCodeBuffer/StringParser.h +++ b/src/GCodes/GCodeBuffer/StringParser.h @@ -79,7 +79,7 @@ class StringParser FilePosition GetFilePosition() const noexcept; // Get the file position at the start of the current command - const char* DataStart() const noexcept; // Get the start of the current command + const char *_ecv_array DataStart() const noexcept; // Get the start of the current command size_t DataLength() const noexcept; // Get the length of the current command void PrintCommand(const StringRef& s) const noexcept; @@ -107,7 +107,7 @@ class StringParser int32_t ReadIValue() THROWS(GCodeException); DriverId ReadDriverIdValue() THROWS(GCodeException); void CheckArrayLength(size_t actualLength, size_t maxLength) THROWS(GCodeException); - void CheckNumberFound(const char *endptr) THROWS(GCodeException); + void CheckNumberFound(const char *_ecv_array endptr) THROWS(GCodeException); void CheckForMixedSpacesAndTabs() noexcept; bool ProcessConditionalGCode(const StringRef& reply, BlockType skippedBlockType, bool doingFile) THROWS(GCodeException); diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index c35621c4e0..167599411e 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -4451,7 +4451,7 @@ void GCodes::StopPrint(GCodeBuffer *gbp, StopPrintReason reason) noexcept simulationMode = SimulationMode::off; // do this after we append the simulation info to the file so that DWC doesn't try to reload the file info too soon reprap.GetMove().Simulate(simulationMode); EndSimulation(nullptr); - reprap.GetPrintMonitor().StoppedPrint(); // must do this after printing the simulation details not before, because it clears the filename and pause time + reprap.GetPrintMonitor().StoppedPrint(); const uint32_t simMinutes = lrintf(simSeconds/60.0); if (reason == StopPrintReason::normalCompletion) diff --git a/src/GCodes/GCodes4.cpp b/src/GCodes/GCodes4.cpp index 76babd3314..340d55f9d5 100644 --- a/src/GCodes/GCodes4.cpp +++ b/src/GCodes/GCodes4.cpp @@ -1559,7 +1559,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept ms.checkEndstops = true; ms.reduceAcceleration = true; straightProbeSettings.SetCoordsToTarget(ms.coords); - ms.feedRate = zp->GetProbingSpeed(0); + ms.feedRate = (straightProbeSettings.GetFeedRateOverride() > 0.0) ? straightProbeSettings.GetFeedRateOverride() : zp->GetProbingSpeed(0); ms.linearAxesMentioned = ms.rotationalAxesMentioned = true; NewSingleSegmentMoveAvailable(ms); gb.AdvanceState(); diff --git a/src/GCodes/GCodes6.cpp b/src/GCodes/GCodes6.cpp index 00b14a1f16..da1319fb7c 100644 --- a/src/GCodes/GCodes6.cpp +++ b/src/GCodes/GCodes6.cpp @@ -659,6 +659,12 @@ GCodeResult GCodes::StraightProbe(GCodeBuffer& gb, const StringRef& reply) THROW } straightProbeSettings.SetZProbeToUse(probeToUse); + // Check if feed rate has been specified + if (gb.Seen('F')) + { + straightProbeSettings.SetFeedRateOverride(gb.GetSpeedFromMm(false)); + } + gb.SetState(GCodeState::straightProbe0); return GCodeResult::ok; } diff --git a/src/GCodes/StraightProbeSettings.cpp b/src/GCodes/StraightProbeSettings.cpp index c690f9cd27..bb18ef3bf3 100644 --- a/src/GCodes/StraightProbeSettings.cpp +++ b/src/GCodes/StraightProbeSettings.cpp @@ -15,6 +15,7 @@ StraightProbeSettings::StraightProbeSettings() noexcept void StraightProbeSettings::Reset() noexcept { + feedRateOverride = 0.0; movingAxes = AxesBitmap(); type = StraightProbeType::unset; for (size_t axis = 0; axis < MaxAxes; ++axis) diff --git a/src/GCodes/StraightProbeSettings.h b/src/GCodes/StraightProbeSettings.h index 4983eac210..8dcbf90937 100644 --- a/src/GCodes/StraightProbeSettings.h +++ b/src/GCodes/StraightProbeSettings.h @@ -40,12 +40,16 @@ class StraightProbeSettings const size_t GetZProbeToUse() const noexcept { return probeToUse; } void SetZProbeToUse(const size_t probeNumber) noexcept { probeToUse = probeNumber; } + const float GetFeedRateOverride() const noexcept { return feedRateOverride; } + void SetFeedRateOverride(const float feedRate) noexcept { feedRateOverride = feedRate; } + const bool ProbingAway() const noexcept; const bool SignalError() const noexcept; private: AxesBitmap movingAxes; // Axes supposed to move - this is only used for manual probing size_t probeToUse; // Use this ZProbe + float feedRateOverride; // Feed rate for the probing move float target[MaxAxes]; // G38 target coordinates for straight probe moves StraightProbeType type; // Type of move }; diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index 28e22f1d90..e8d9e1711f 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -355,7 +355,6 @@ Move::Move() noexcept heightController(nullptr), #endif jerkPolicy(0), - stepErrorState(StepErrorState::noError), numCalibratedFactors(0) { #if VARIABLE_NUM_DRIVERS @@ -2328,12 +2327,18 @@ void Move::AddLinearSegments(const DDA& dda, size_t logicalDrive, uint32_t start { if (tail->GetFlags().executing) { - const uint32_t now = StepTimer::GetMovementTimerTicks(); - const int32_t overlap = endTime - startTime; +#if 1 // temp for debugging + stepErrorDetails.executingStartTime = segStartTime; + stepErrorDetails.executingDuration = tail->GetDuration(); + stepErrorDetails.newSegmentStartTime = startTime; + stepErrorDetails.timeNow = StepTimer::GetMovementTimerTicks(); +#endif LogStepError(3); RestoreBasePriority(oldPrio); if (reprap.Debug(Module::Move)) { + const uint32_t now = StepTimer::GetMovementTimerTicks(); + const int32_t overlap = endTime - startTime; debugPrintf("overlaps executing seg by %" PRIi32 " while trying to add segment(s) starting at %" PRIu32 ", time now %" PRIu32 "\n", overlap, startTime, now); MoveSegment::DebugPrintList(tail); diff --git a/src/Movement/Move.h b/src/Movement/Move.h index 6eb6ae6c57..e43caec49a 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -98,6 +98,16 @@ struct NonlinearExtrusion #endif +// Collection of data reported to help debug step errors +struct StepErrorDetails +{ + uint32_t executingStartTime; + uint32_t executingDuration; + uint32_t newSegmentStartTime; + uint32_t timeNow; + uint8_t stepErrorType; +}; + // This is the master movement class. It controls all movement in the machine. class Move INHERIT_OBJECT_MODEL { @@ -463,7 +473,7 @@ class Move INHERIT_OBJECT_MODEL // Movement error handling void LogStepError(uint8_t type) noexcept; // stop all movement because of a step error - uint8_t GetStepErrorType() const noexcept { return stepErrorType; } + StepErrorDetails GetStepErrorDetails() const noexcept { return stepErrorDetails; } bool HasMovementError() const noexcept; void ResetAfterError() noexcept; void GenerateMovementErrorDebug() noexcept; @@ -492,12 +502,12 @@ class Move INHERIT_OBJECT_MODEL timing // no moves being executed or in queue, motors are at full current }; - enum class StepErrorState : uint8_t - { - noError = 0, // no error - haveError, // had an error, movement is stopped - resetting // had an error, ready to reset it - }; +enum class StepErrorState : uint8_t +{ + noError = 0, // no error + haveError, // had an error, movement is stopped + resetting // had an error, ready to reset it +}; #if SUPPORT_SCANNING_PROBES struct ScanningProbeControl @@ -665,9 +675,6 @@ class Move INHERIT_OBJECT_MODEL float specialMoveCoords[MaxDriversPerAxis]; // Amounts by which to move individual Z motors (leadscrew adjustment move) - volatile uint8_t stepErrorType; - volatile StepErrorState stepErrorState; - // Drives #if VARIABLE_NUM_DRIVERS && SUPPORT_DIRECT_LCD size_t numActualDirectDrivers; @@ -765,6 +772,10 @@ class Move INHERIT_OBJECT_MODEL bool bedLevellingMoveAvailable; // True if a leadscrew adjustment move is pending 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 + + // Reporting of step errors + volatile StepErrorState stepErrorState = StepErrorState::noError; + StepErrorDetails stepErrorDetails; }; //****************************************************************************************************** @@ -982,7 +993,7 @@ inline void Move::InsertDM(DriveMovement *dm) noexcept inline void Move::LogStepError(uint8_t type) noexcept { - stepErrorType = type; + stepErrorDetails.stepErrorType = type; stepErrorState = StepErrorState::haveError; } diff --git a/src/Movement/RawMove.h b/src/Movement/RawMove.h index a48d9d95df..5a8c7b7536 100644 --- a/src/Movement/RawMove.h +++ b/src/Movement/RawMove.h @@ -55,7 +55,7 @@ struct RawMove // If adding any more fields, keep the total size a multiple of 4 bytes so that we can use our optimised assignment operator // GCC normally calls memcpy to assign objects of this class. We can do better because we know they must be 32-bit aligned. - RawMove& operator=(const RawMove& arg) noexcept + RawMove &_ecv_from operator=(const RawMove& arg) noexcept { memcpyu32(reinterpret_cast(this), reinterpret_cast(&arg), sizeof(*this)/4); return *this; diff --git a/src/Platform/Platform.cpp b/src/Platform/Platform.cpp index 6377e47bc3..83058abcdf 100644 --- a/src/Platform/Platform.cpp +++ b/src/Platform/Platform.cpp @@ -893,7 +893,15 @@ void Platform::Spin() noexcept Move& move = reprap.GetMove(); if (move.HasMovementError()) { - MessageF(AddError(MessageType::GenericMessage), "Movement halted because a step timing error occurred (code %u). Please reset the controller.\n", move.GetStepErrorType()); + const StepErrorDetails details = move.GetStepErrorDetails(); + MessageF(AddError(MessageType::GenericMessage), "Movement halted because a step timing error occurred (code %u). Please reset the controller.\n", details.stepErrorType); + if (details.stepErrorType == 3) + { + MessageF(AddError(MessageType::GenericMessage), "Existing: start=%" PRIu32 " length=%" PRIu32 ", new: start=%" PRIu32 ", overlap=%" PRIu32 " time now=%" PRIu32 "\n", + details.executingStartTime, details.executingDuration, details.newSegmentStartTime, + details.executingStartTime + details.executingDuration - details.newSegmentStartTime, + details.timeNow); + } move.GenerateMovementErrorDebug(); move.ResetAfterError(); }