Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2022-11-02 21:54:17 +0300
committerDavid Crocker <dcrocker@eschertech.com>2022-11-02 21:54:17 +0300
commitfec1720d684d6fb42485b197850372fd9a412f66 (patch)
tree4727163933eae3e273ab6be93f21e4bb89d09763
parent690c9f12b155eb0027455b1dd4735fdf4be80116 (diff)
Refactored axis allocation and release
-rw-r--r--src/GCodes/GCodes.cpp132
-rw-r--r--src/GCodes/GCodes.h4
-rw-r--r--src/GCodes/GCodes3.cpp6
-rw-r--r--src/Movement/RawMove.cpp7
-rw-r--r--src/Movement/RawMove.h82
5 files changed, 140 insertions, 91 deletions
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index acb86014..db68c372 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -1595,20 +1595,19 @@ bool GCodes::LockMovementSystemAndWaitForStandstill(GCodeBuffer& gb, unsigned in
#if SUPPORT_ASYNC_MOVES
// Get the position of all axes by combining positions from the queues
Move& move = reprap.GetMove();
- const AxesBitmap ownedAxes = moveStates[msNumber].axesAndExtrudersOwned;
+ const AxesBitmap ownedAxes = ms.GetAxesAndExtrudersOwned();
// Whenever we release axes, we must update lastKnownMachinePositions for those axes first so that whoever allocated them next gets the correct positions
move.GetPartialMachinePosition(lastKnownMachinePositions, ownedAxes, msNumber);
- moveStates[msNumber].axesAndExtrudersOwned.Clear();
- memcpyf(moveStates[msNumber].coords, lastKnownMachinePositions, MaxAxes);
- move.InverseAxisAndBedTransform(lastKnownMachinePositions, moveStates[msNumber].currentTool);
- UpdateUserPositionFromMachinePosition(gb, moveStates[msNumber]);
+ memcpyf(ms.coords, lastKnownMachinePositions, MaxAxes);
+ move.InverseAxisAndBedTransform(lastKnownMachinePositions, ms.currentTool);
+ UpdateUserPositionFromMachinePosition(gb, ms);
collisionChecker.ResetPositions(lastKnownMachinePositions, ownedAxes);
// Release the axes and extruders that this movement system owns
axesAndExtrudersMoved.ClearBits(ownedAxes);
- ms.ownedAxisLetters.Clear();
+ ms.ReleaseOwnedAxesAndExtruders();
#else
UpdateCurrentUserPosition(gb);
#endif
@@ -1690,8 +1689,10 @@ void GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, MovementState& m
}
ms.hasPositiveExtrusion = false;
ms.moveStartVirtualExtruderPosition = ms.latestVirtualExtruderPosition; // save this before we update it
- AxesBitmap logicalDrivesMoving;
ExtrudersBitmap extrudersMoving;
+#if SUPPORT_ASYNC_MOVES
+ AxesBitmap logicalDrivesMoving;
+#endif
// Check if we are extruding
if (gb.Seen(extrudeLetter)) // DC 2018-08-07: at E3D's request, extrusion is now recognised even on uncoordinated moves
@@ -1763,7 +1764,9 @@ void GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, MovementState& m
? extrusionAmount * extrusionFactors[extruder]
: extrusionAmount;
extrudersMoving.SetBit(extruder);
+#if SUPPORT_ASYNC_MOVES
logicalDrivesMoving.SetBit(ExtruderToLogicalDrive(extruder));
+#endif
}
}
if (!isPrintingMove && ms.usingStandardFeedrate)
@@ -1803,7 +1806,9 @@ void GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, MovementState& m
? extrusionAmount * extrusionFactors[extruder]
: extrusionAmount;
extrudersMoving.SetBit(extruder);
+#if SUPPORT_ASYNC_MOVES
logicalDrivesMoving.SetBit(ExtruderToLogicalDrive(extruder));
+#endif
}
}
}
@@ -1816,7 +1821,7 @@ void GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, MovementState& m
}
#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, logicalDrivesMoving);
+ AllocateAxes(gb, ms, logicalDrivesMoving, ParameterLettersBitmap());
#endif
if (ms.moveType == 1 || ms.moveType == 4)
@@ -1840,13 +1845,46 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeExc
{
MovementState& ms = GetMovementState(gb);
+ // Set up default move parameters
+ ms.movementTool = ms.currentTool;
+ ms.moveType = 0;
+ ms.isCoordinated = isCoordinated;
+ ms.checkEndstops = false;
+ ms.reduceAcceleration = false;
+ ms.movementTool = ms.currentTool;
+ ms.usePressureAdvance = false;
+ axesToSenseLength.Clear();
+
+ // Check to see if the move is a 'homing' move that endstops are checked on and for which X and Y axis mapping is not applied
+ {
+ uint32_t moveType;
+ bool dummy;
+ if (gb.TryGetLimitedUIValue('H', moveType, dummy, 5))
+ {
+ if (!LockCurrentMovementSystemAndWaitForStandstill(gb))
+ {
+ return false;
+ }
+ ms.moveType = moveType;
+ ms.movementTool = nullptr;
+ }
+ }
+
+
#if SUPPORT_ASYNC_MOVES
// We need to check for moving unowned axes right at the start in case we need to fetch axis positions before processing the command
ParameterLettersBitmap axisLettersMentioned = gb.AllParameters() & allAxisLetters;
- axisLettersMentioned.ClearBits(ms.ownedAxisLetters);
- if (axisLettersMentioned.IsNonEmpty())
+ if (ms.moveType == 0)
{
- AllocateAxisLetters(gb, ms, axisLettersMentioned);
+ axisLettersMentioned.ClearBits(ms.GetOwnedAxisLetters());
+ if (axisLettersMentioned.IsNonEmpty())
+ {
+ AllocateAxisLetters(gb, ms, axisLettersMentioned);
+ }
+ }
+ else
+ {
+ AllocateAxesDirectFromLetters(gb, ms, axisLettersMentioned);
}
#endif
@@ -1862,31 +1900,6 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeExc
ms.initialUserC1 = ms.currentUserPosition[(selectedPlane == 0) ? Y_AXIS : Z_AXIS];
}
- // Set up default move parameters
- ms.isCoordinated = isCoordinated;
- ms.checkEndstops = false;
- ms.reduceAcceleration = false;
- ms.movementTool = ms.currentTool;
- ms.moveType = 0;
- ms.usePressureAdvance = false;
- axesToSenseLength.Clear();
-
- // Check to see if the move is a 'homing' move that endstops are checked on.
- // We handle H1 parameters affecting extrusion elsewhere.
- if (gb.Seen('H'))
- {
- const int ival = gb.GetIValue();
- if (ival >= 1 && ival <= 4)
- {
- if (!LockCurrentMovementSystemAndWaitForStandstill(gb))
- {
- return false;
- }
- ms.moveType = ival;
- ms.movementTool = nullptr;
- }
- }
-
// Check for 'R' parameter to move relative to a restore point
const RestorePoint * rp = nullptr;
if (ms.moveType == 0 && gb.Seen('R'))
@@ -2044,9 +2057,6 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeExc
}
}
-#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, realAxesMoving);
-#endif
if (!doingManualBedProbe && CheckEnoughAxesHomed(axesMentioned))
{
gb.ThrowGCodeException("G0/G1: insufficient axes homed");
@@ -2054,9 +2064,6 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeExc
}
else
{
-#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, axesMentioned);
-#endif
switch (ms.moveType)
{
case 3:
@@ -2267,7 +2274,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
ParameterLettersBitmap axisLettersMentioned = gb.AllParameters() & allAxisLetters;
axisLettersMentioned.SetBit(ParameterLetterToBitNumber('X') + axis0); // add in the implicit axes
axisLettersMentioned.SetBit(ParameterLetterToBitNumber('X') + axis1);
- axisLettersMentioned.ClearBits(ms.ownedAxisLetters);
+ axisLettersMentioned.ClearBits(ms.GetOwnedAxisLetters());
if (axisLettersMentioned.IsNonEmpty())
{
AllocateAxisLetters(gb, ms, axisLettersMentioned);
@@ -2467,10 +2474,6 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
gb.ThrowGCodeException("G2/G3: insufficient axes homed");
}
-#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, realAxesMoving);
-#endif
-
// Compute the initial and final angles. Do this before we possible rotate the coordinates of the arc centre.
float finalTheta = atan2(ms.currentUserPosition[axis1] - userArcCentre[1], ms.currentUserPosition[axis0] - userArcCentre[0]);
ms.arcRadius = fastSqrtf(iParam * iParam + jParam * jParam);
@@ -3924,7 +3927,7 @@ GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract) THROWS(GCodeE
ms.feedRate = currentTool->GetRetractSpeed() * currentTool->DriveCount();
ms.canPauseAfter = false; // don't pause after a retraction because that could cause too much retraction
#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, drivesMoving);
+ AllocateAxes(gb, ms, drivesMoving, ParameterLettersBitmap());
#endif
NewSingleSegmentMoveAvailable(ms);
}
@@ -3942,7 +3945,7 @@ GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract) THROWS(GCodeE
ms.canPauseAfter = false; // don't pause in the middle of a command
ms.linearAxesMentioned = true;
#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, AxesBitmap::MakeFromBits(Z_AXIS));
+ AllocateAxes(gb, ms, AxesBitmap::MakeFromBits(Z_AXIS), ParameterLettersBitmap('Z'));
#endif
NewSingleSegmentMoveAvailable(ms);
gb.SetState(GCodeState::doingFirmwareUnRetraction);
@@ -3962,7 +3965,7 @@ GCodeResult GCodes::RetractFilament(GCodeBuffer& gb, bool retract) THROWS(GCodeE
ms.feedRate = currentTool->GetUnRetractSpeed() * currentTool->DriveCount();
ms.canPauseAfter = true;
#if SUPPORT_ASYNC_MOVES
- AllocateAxes(gb, ms, drivesMoving);
+ AllocateAxes(gb, ms, drivesMoving, ParameterLettersBitmap());
#endif
NewSingleSegmentMoveAvailable(ms);
}
@@ -4936,17 +4939,17 @@ const MovementState& GCodes::GetCurrentMovementState(const ObjectExplorationCont
// Allocate additional axes and/or extruders to a movement state returning true if successful, false if another movement state owns it already
// This relies on cooperative scheduling between different GCodeBuffer objects
-void GCodes::AllocateAxes(const GCodeBuffer& gb, MovementState& ms, AxesBitmap axes) THROWS(GCodeException)
+void GCodes::AllocateAxes(const GCodeBuffer& gb, MovementState& ms, AxesBitmap axes, ParameterLettersBitmap axLetters) THROWS(GCodeException)
{
- if ((axes & axesAndExtrudersMoved & ~ms.axesAndExtrudersOwned).IsNonEmpty())
+ if ((axes & axesAndExtrudersMoved & ~ms.GetAxesAndExtrudersOwned()).IsNonEmpty())
{
gb.ThrowGCodeException("Axis is already used by a different motion system");
}
axesAndExtrudersMoved |= axes;
- ms.axesAndExtrudersOwned |= axes;
+ ms.AllocateAxes(axes, axLetters);
}
-// Allocate additional axes by letter. The axis letters are as in the GCode, before we account for coordinate rotation and axis mapping.
+// Allocate additional axes by letter when we are doing a standard move. The axis letters are as in the GCode, before we account for coordinate rotation and axis mapping.
// We must clear out owned axis letters on a tool change, or when coordinate rotation is changed from zero to nonzero
void GCodes::AllocateAxisLetters(const GCodeBuffer& gb, MovementState& ms, ParameterLettersBitmap axLetters) THROWS(GCodeException)
{
@@ -4986,9 +4989,24 @@ void GCodes::AllocateAxisLetters(const GCodeBuffer& gb, MovementState& ms, Param
}
}
}
- newAxes &= ~ms.axesAndExtrudersOwned;
- AllocateAxes(gb, ms, newAxes);
- ms.ownedAxisLetters |= axLetters;
+ AllocateAxes(gb, ms, newAxes, axLetters);
+ UpdateAllCoordinates(gb);
+}
+
+// Allocate axes by letter when we are doing a special move. Do not update the map of owned axes letters.
+void GCodes::AllocateAxesDirectFromLetters(const GCodeBuffer& gb, MovementState& ms, ParameterLettersBitmap axLetters) THROWS(GCodeException)
+{
+ AxesBitmap newAxes;
+ for (size_t axis = 0; axis < numVisibleAxes; ++axis)
+ {
+ const char c = axisLetters[axis];
+ const unsigned int axisLetterBitNumber = ParameterLetterToBitNumber(c);
+ if (axLetters.IsBitSet(axisLetterBitNumber))
+ {
+ newAxes.SetBit(axis);
+ }
+ }
+ AllocateAxes(gb, ms, newAxes, ParameterLettersBitmap()); // don't own the letters!
UpdateAllCoordinates(gb);
}
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index 29df59ca..084fe9e1 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -554,9 +554,11 @@ private:
GCodeResult CollisionAvoidance(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M597
GCodeResult SyncMovementSystems(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M598
GCodeResult ExecuteM400(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // Handle M400
- void AllocateAxes(const GCodeBuffer& gb, MovementState& ms, AxesBitmap axes) THROWS(GCodeException); // allocate axes to a movement state
+ void AllocateAxes(const GCodeBuffer& gb, MovementState& ms, AxesBitmap axes, ParameterLettersBitmap axLetters) THROWS(GCodeException); // allocate axes to a movement state
void AllocateAxisLetters(const GCodeBuffer& gb, MovementState& ms, ParameterLettersBitmap axLetters) THROWS(GCodeException);
// allocate axes by letter
+ void AllocateAxesDirectFromLetters(const GCodeBuffer& gb, MovementState& ms, ParameterLettersBitmap axLetters) THROWS(GCodeException);
+ // allocate axes by letter for a special move
bool DoSync(GCodeBuffer& gb) noexcept; // sync with the other stream returning true if done, false if we need to wait for it
bool SyncWith(GCodeBuffer& thisGb, const GCodeBuffer& otherGb) noexcept; // synchronise motion systems
void UpdateAllCoordinates(const GCodeBuffer& gb) noexcept;
diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp
index f65dedb5..04aadd9f 100644
--- a/src/GCodes/GCodes3.cpp
+++ b/src/GCodes/GCodes3.cpp
@@ -76,7 +76,7 @@ GCodeResult GCodes::SetPositions(GCodeBuffer& gb, const StringRef& reply) THROWS
#if SUPPORT_ASYNC_MOVES
// Check for setting unowned axes before processing the command
ParameterLettersBitmap axisLettersMentioned = gb.AllParameters() & allAxisLetters;
- axisLettersMentioned.ClearBits(ms.ownedAxisLetters);
+ axisLettersMentioned.ClearBits(ms.GetOwnedAxisLetters());
if (axisLettersMentioned.IsNonEmpty())
{
AllocateAxisLetters(gb, ms, axisLettersMentioned);
@@ -1569,8 +1569,8 @@ GCodeResult GCodes::HandleG68(GCodeBuffer& gb, const StringRef& reply) THROWS(GC
// We have just started doing coordinate rotation, so if we own axis letter X we need to own Y and vice versa
// Simplest is just to say we don't own either in the axis letters bitmap
MovementState& ms = GetMovementState(gb);
- ms.ownedAxisLetters.ClearBit('X' - 'A');
- ms.ownedAxisLetters.ClearBit('Y' - 'A');
+ ms.ReleaseAxisLetter('X');
+ ms.ReleaseAxisLetter('Y');
}
#endif
UpdateCurrentUserPosition(gb);
diff --git a/src/Movement/RawMove.cpp b/src/Movement/RawMove.cpp
index 0df696f8..91162dd7 100644
--- a/src/Movement/RawMove.cpp
+++ b/src/Movement/RawMove.cpp
@@ -226,6 +226,13 @@ void MovementState::InitObjectCancellation() noexcept
currentObjectCancelled = printingJustResumed = false;
}
+// When releasing axes we must also release the corresponding axis letters, because they serve as a cache
+void MovementState::ReleaseOwnedAxesAndExtruders() noexcept
+{
+ axesAndExtrudersOwned.Clear();
+ ownedAxisLetters.Clear();
+}
+
#if SUPPORT_ASYNC_MOVES
void AsyncMove::SetDefaults() noexcept
diff --git a/src/Movement/RawMove.h b/src/Movement/RawMove.h
index 99b11d1b..e3f72656 100644
--- a/src/Movement/RawMove.h
+++ b/src/Movement/RawMove.h
@@ -76,11 +76,46 @@ constexpr size_t ResumeObjectRestorePointNumber = NumVisibleRestorePoints + 1;
// Details of a move that are needed only by GCodes
// CAUTION: segmentsLeft should ONLY be changed from 0 to not 0 by calling NewMoveAvailable()!
-struct MovementState : public RawMove
+class MovementState : public RawMove
{
+public:
+ AxesBitmap GetAxesAndExtrudersOwned() const noexcept { return axesAndExtrudersOwned; } // Get the axes and extruders that this movement system owns
+ ParameterLettersBitmap GetOwnedAxisLetters() const noexcept { return ownedAxisLetters; } // Get the letters denoting axes that this movement system owns
+ void AllocateAxes(AxesBitmap axes, ParameterLettersBitmap axisLetters) noexcept;
+ void ReleaseOwnedAxesAndExtruders() noexcept;
+ void ReleaseAxisLetter(char letter) noexcept; // stop claiming that we own an axis letter (if we do) but don't release the associated axis
+
+ float GetProportionDone() const noexcept; // get the proportion of this whole move that has been completed, based on segmentsLeft and totalSegments
+ void Reset() noexcept;
+ void ChangeExtrusionFactor(unsigned int extruder, float multiplier) noexcept; // change the extrusion factor of an extruder
+ const RestorePoint& GetRestorePoint(size_t n) const pre(n < NumTotalRestorePoints) { return restorePoints[n]; }
+ void ClearMove() noexcept;
+ void SavePosition(unsigned int restorePointNumber, size_t numAxes, float p_feedRate, FilePosition p_filePos) noexcept
+ pre(restorePointNumber < NumTotalRestorePoints);
+
+ // Tool management
+ void SelectTool(int toolNumber, bool simulating) noexcept;
+ ReadLockedPointer<Tool> GetLockedCurrentTool() const noexcept;
+ ReadLockedPointer<Tool> GetLockedCurrentOrDefaultTool() const noexcept;
+ int GetCurrentToolNumber() const noexcept;
+ void SetPreviousToolNumber() noexcept;
+ AxesBitmap GetCurrentXAxes() const noexcept; // Get the current axes used as X axes
+ AxesBitmap GetCurrentYAxes() const noexcept; // Get the current axes used as Y axes
+ AxesBitmap GetCurrentAxisMapping(unsigned int axis) const noexcept;
+ float GetCurrentToolOffset(size_t axis) const noexcept; // Get an axis offset of the current tool
+
+ // Object cancellation support
+ void InitObjectCancellation() noexcept;
+ bool IsCurrentObjectCancelled() const noexcept { return currentObjectCancelled; }
+ bool IsFirstMoveSincePrintingResumed() const noexcept { return printingJustResumed; }
+ void DoneMoveSincePrintingResumed() noexcept { printingJustResumed = false; }
+ void StopPrinting(GCodeBuffer& gb) noexcept;
+ void ResumePrinting(GCodeBuffer& gb) noexcept;
+
+ void Diagnostics(MessageType mtype, unsigned int moveSystemNumber) noexcept;
+
+ // These variables are currently all public, but we ought to make most of them private
Tool *currentTool; // the current tool of this movement system
- AxesBitmap axesAndExtrudersOwned; // axes and extruders that this movement system has moved since the last sync
- ParameterLettersBitmap ownedAxisLetters; // letters denoting axes that this movement system owns
// The current user position now holds the requested user position after applying workplace coordinate offsets.
// So we must subtract the workplace coordinate offsets when we want to display them.
@@ -142,35 +177,22 @@ struct MovementState : public RawMove
bool currentObjectCancelled; // true if the current object should not be printed
bool printingJustResumed; // true if we have just restarted printing
- float GetProportionDone() const noexcept; // get the proportion of this whole move that has been completed, based on segmentsLeft and totalSegments
- void Reset() noexcept;
- void ChangeExtrusionFactor(unsigned int extruder, float multiplier) noexcept; // change the extrusion factor of an extruder
- const RestorePoint& GetRestorePoint(size_t n) const pre(n < NumTotalRestorePoints) { return restorePoints[n]; }
- void ClearMove() noexcept;
- void SavePosition(unsigned int restorePointNumber, size_t numAxes, float p_feedRate, FilePosition p_filePos) noexcept
- pre(restorePointNumber < NumTotalRestorePoints);
-
- // Tool management
- void SelectTool(int toolNumber, bool simulating) noexcept;
- ReadLockedPointer<Tool> GetLockedCurrentTool() const noexcept;
- ReadLockedPointer<Tool> GetLockedCurrentOrDefaultTool() const noexcept;
- int GetCurrentToolNumber() const noexcept;
- void SetPreviousToolNumber() noexcept;
- AxesBitmap GetCurrentXAxes() const noexcept; // Get the current axes used as X axes
- AxesBitmap GetCurrentYAxes() const noexcept; // Get the current axes used as Y axes
- AxesBitmap GetCurrentAxisMapping(unsigned int axis) const noexcept;
- float GetCurrentToolOffset(size_t axis) const noexcept; // Get an axis offset of the current tool
+private:
+ AxesBitmap axesAndExtrudersOwned; // axes and extruders that this movement system has moved since the last sync
+ ParameterLettersBitmap ownedAxisLetters; // letters denoting axes that this movement system owns
+};
- // Object cancellation support
- void InitObjectCancellation() noexcept;
- bool IsCurrentObjectCancelled() const noexcept { return currentObjectCancelled; }
- bool IsFirstMoveSincePrintingResumed() const noexcept { return printingJustResumed; }
- void DoneMoveSincePrintingResumed() noexcept { printingJustResumed = false; }
- void StopPrinting(GCodeBuffer& gb) noexcept;
- void ResumePrinting(GCodeBuffer& gb) noexcept;
+inline void MovementState::AllocateAxes(AxesBitmap axes, ParameterLettersBitmap axisLetters) noexcept
+{
+ axesAndExtrudersOwned |= axes;
+ ownedAxisLetters |= axisLetters;
+}
- void Diagnostics(MessageType mtype, unsigned int moveSystemNumber) noexcept;
-};
+// Stop claiming that we own an axis letter (if we do) but don't release the associated axis
+inline void MovementState::ReleaseAxisLetter(char letter) noexcept
+{
+ ownedAxisLetters.ClearBit(ParameterLetterToBitNumber(letter));
+}
#if SUPPORT_ASYNC_MOVES