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:
authorChristian Hammacher <bmasterc@gmail.com>2021-05-17 15:40:07 +0300
committerChristian Hammacher <bmasterc@gmail.com>2021-05-17 15:40:07 +0300
commit7aaac7e1911ebe52a9260f147425627725d605c5 (patch)
tree4af098e59acf21ac72a3e35602c40f9f41608597
parent0e400be6ad6f07ac2d936312ce8432bf0f5cec1d (diff)
parent262e37f799ef21219923b8373f5cf4ed4aa1227e (diff)
Merge remote-tracking branch 'origin/3.3-dev' into v3-chrishamm
-rw-r--r--src/Accelerometers/Accelerometers.cpp2
-rw-r--r--src/Accelerometers/LIS3DH.cpp6
-rw-r--r--src/Fans/LedStripDriver.cpp2
-rw-r--r--src/FilamentMonitors/LaserFilamentMonitor.cpp4
-rw-r--r--src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp4
-rw-r--r--src/GCodes/GCodeBuffer/BinaryParser.cpp17
-rw-r--r--src/GCodes/GCodeBuffer/BinaryParser.h2
-rw-r--r--src/GCodes/GCodeBuffer/ExpressionParser.cpp364
-rw-r--r--src/GCodes/GCodeBuffer/ExpressionParser.h26
-rw-r--r--src/GCodes/GCodeBuffer/GCodeBuffer.cpp10
-rw-r--r--src/GCodes/GCodeBuffer/GCodeBuffer.h4
-rw-r--r--src/GCodes/GCodeBuffer/StringParser.cpp25
-rw-r--r--src/GCodes/GCodeBuffer/StringParser.h2
-rw-r--r--src/GCodes/GCodeException.h6
-rw-r--r--src/GCodes/GCodes.cpp120
-rw-r--r--src/GCodes/GCodes.h6
-rw-r--r--src/GCodes/GCodes2.cpp158
-rw-r--r--src/GCodes/GCodes3.cpp15
-rw-r--r--src/GCodes/GCodes4.cpp29
-rw-r--r--src/GCodes/RestorePoint.cpp4
-rw-r--r--src/GCodes/RestorePoint.h2
-rw-r--r--src/Heating/Heat.cpp6
-rw-r--r--src/Heating/Heater.cpp10
-rw-r--r--src/Movement/DDA.cpp2
-rw-r--r--src/Movement/DDARing.cpp2
-rw-r--r--src/Movement/DDARing.h2
-rw-r--r--src/Movement/InputShaper.cpp14
-rw-r--r--src/Movement/InputShaper.h6
-rw-r--r--src/Movement/Kinematics/FiveBarScaraKinematics.cpp2
-rw-r--r--src/Movement/Kinematics/HangprinterKinematics.cpp4
-rw-r--r--src/Movement/Kinematics/Kinematics.cpp9
-rw-r--r--src/Movement/Kinematics/Kinematics.h30
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.cpp2
-rw-r--r--src/Movement/Kinematics/PolarKinematics.cpp4
-rw-r--r--src/Movement/Kinematics/RotaryDeltaKinematics.cpp2
-rw-r--r--src/Movement/Kinematics/RoundBedKinematics.cpp4
-rw-r--r--src/Movement/Kinematics/RoundBedKinematics.h2
-rw-r--r--src/Movement/Kinematics/ScaraKinematics.cpp4
-rw-r--r--src/Movement/Kinematics/ZLeadscrewKinematics.cpp6
-rw-r--r--src/Movement/Kinematics/ZLeadscrewKinematics.h4
-rw-r--r--src/Movement/Move.cpp90
-rw-r--r--src/Movement/Move.h15
-rw-r--r--src/Networking/ESP8266WiFi/WiFiInterface.cpp59
-rw-r--r--src/ObjectModel/GlobalVariables.cpp15
-rw-r--r--src/ObjectModel/ObjectModel.cpp51
-rw-r--r--src/ObjectModel/ObjectModel.h7
-rw-r--r--src/ObjectModel/Variable.cpp21
-rw-r--r--src/ObjectModel/Variable.h7
-rw-r--r--src/Platform/Heap.cpp31
-rw-r--r--src/Platform/Heap.h4
-rw-r--r--src/Tools/Tool.cpp45
-rw-r--r--src/Tools/Tool.h4
-rw-r--r--src/Version.h2
53 files changed, 752 insertions, 522 deletions
diff --git a/src/Accelerometers/Accelerometers.cpp b/src/Accelerometers/Accelerometers.cpp
index 5ad0ef69..43128ebd 100644
--- a/src/Accelerometers/Accelerometers.cpp
+++ b/src/Accelerometers/Accelerometers.cpp
@@ -423,7 +423,7 @@ GCodeResult Accelerometers::ConfigureAccelerometer(GCodeBuffer& gb, const String
return GCodeResult::error;
}
- const uint32_t spiFrequency = (gb.Seen('Q')) ? gb.GetUIValue() : DefaultAccelerometerSpiFrequency;
+ const uint32_t spiFrequency = (gb.Seen('Q')) ? gb.GetLimitedUIValue('Q', 500000, 10000001) : DefaultAccelerometerSpiFrequency;
temp = new LIS3DH(SharedSpiDevice::GetMainSharedSpiDevice(), spiFrequency, spiCsPort.GetPin(), irqPort.GetPin());
if (temp->CheckPresent())
{
diff --git a/src/Accelerometers/LIS3DH.cpp b/src/Accelerometers/LIS3DH.cpp
index d4e22502..121ddb1c 100644
--- a/src/Accelerometers/LIS3DH.cpp
+++ b/src/Accelerometers/LIS3DH.cpp
@@ -160,8 +160,10 @@ unsigned int LIS3DH::CollectData(const uint16_t **collectedData, uint16_t &dataR
*collectedData = reinterpret_cast<const uint16_t*>(dataBuffer);
overflowed = (fifoStatus & 0x40) != 0;
- dataRate = (totalNumRead == 0) ? 0
- : (totalNumRead * StepTimer::StepClockRate)/(lastInterruptTime - firstInterruptTime);
+ const uint32_t interval = lastInterruptTime - firstInterruptTime;
+ dataRate = (totalNumRead == 0 || interval == 0)
+ ? 0
+ : (totalNumRead * StepTimer::StepClockRate)/interval;
totalNumRead += numToRead;
}
return numToRead;
diff --git a/src/Fans/LedStripDriver.cpp b/src/Fans/LedStripDriver.cpp
index 4ec1356c..0774ed96 100644
--- a/src/Fans/LedStripDriver.cpp
+++ b/src/Fans/LedStripDriver.cpp
@@ -477,7 +477,7 @@ void LedStripDriver::Init() noexcept
// This function handles M150
// For DotStar LEDs:
// We can handle an unlimited length LED strip, because we can send the data in multiple chunks.
-// So whenever we receive a m150 command, we send the data immediately, in multiple chunks if our DMA buffer is too small to send it as a single chunk.
+// So whenever we receive a M150 command, we send the data immediately, in multiple chunks if our DMA buffer is too small to send it as a single chunk.
// To send multiple chunks, we process the command once per chunk, using numRemaining to keep track of how many more LEDs need to be written to
// For NeoPixel LEDs:
// If there is a gap or more then about 9us in transmission, the string will reset and the next command will be taken as applying to the start of the strip.
diff --git a/src/FilamentMonitors/LaserFilamentMonitor.cpp b/src/FilamentMonitors/LaserFilamentMonitor.cpp
index 206dcae9..44fc2c1d 100644
--- a/src/FilamentMonitors/LaserFilamentMonitor.cpp
+++ b/src/FilamentMonitors/LaserFilamentMonitor.cpp
@@ -157,6 +157,10 @@ GCodeResult LaserFilamentMonitor::Configure(GCodeBuffer& gb, const StringRef& re
else
{
reply.catf("version %u, ", version);
+ if (switchOpenMask != 0)
+ {
+ reply.cat(((sensorValue & switchOpenMask) != 0) ? "no filament, " : "filament present, ");
+ }
if (imageQuality != 0)
{
reply.catf("quality %u, ", imageQuality);
diff --git a/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp b/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp
index 2c5b99f5..f20a43e2 100644
--- a/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp
+++ b/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp
@@ -159,6 +159,10 @@ GCodeResult RotatingMagnetFilamentMonitor::Configure(GCodeBuffer& gb, const Stri
else
{
reply.catf("version %u, ", version);
+ if (switchOpenMask != 0)
+ {
+ reply.cat(((sensorValue & switchOpenMask) != 0) ? "no filament, " : "filament present, ");
+ }
if (version >= 3)
{
reply.catf("mag %u agc %u, ", magnitude, agc);
diff --git a/src/GCodes/GCodeBuffer/BinaryParser.cpp b/src/GCodes/GCodeBuffer/BinaryParser.cpp
index 1e333b9e..f929f462 100644
--- a/src/GCodes/GCodeBuffer/BinaryParser.cpp
+++ b/src/GCodes/GCodeBuffer/BinaryParser.cpp
@@ -754,7 +754,7 @@ void BinaryParser::WriteParameters(const StringRef& s, bool quoteStrings) const
}
}
-void BinaryParser::SetParameters(VariableSet& vs, int codeRunning) noexcept
+void BinaryParser::AddParameters(VariableSet& vs, int codeRunning) noexcept
{
if (bufferLength != 0 && header->numParameters != 0)
{
@@ -773,7 +773,7 @@ void BinaryParser::SetParameters(VariableSet& vs, int codeRunning) noexcept
{
case DataType::String:
{
- StringHandle sh(seenParameterValue, seenParameter->intValue);
+ StringHandle sh(seenParameterValue, param->intValue);
ev.Set(sh);
}
break;
@@ -781,22 +781,22 @@ void BinaryParser::SetParameters(VariableSet& vs, int codeRunning) noexcept
case DataType::Expression:
try
{
- ExpressionParser parser(gb, seenParameterValue, seenParameterValue + seenParameter->intValue, -1);
- ev.Set(parser.ParseFloat());
+ ExpressionParser parser(gb, seenParameterValue, seenParameterValue + param->intValue, -1);
+ ev = parser.Parse();
}
- catch (const GCodeException&) { }
+ catch (const GCodeException&) { } // TODO error handling
break;
case DataType::Float:
- ev.Set(seenParameter->floatValue);
+ ev.Set(param->floatValue);
break;
case DataType::Int:
- ev.Set(seenParameter->intValue);
+ ev.Set(param->intValue);
break;
case DataType::UInt:
- ev.Set((int32_t)seenParameter->uintValue);
+ ev.Set((int32_t)param->uintValue);
break;
default:
@@ -824,7 +824,6 @@ void BinaryParser::SetParameters(VariableSet& vs, int codeRunning) noexcept
}
}
}
- //TODO
}
GCodeException BinaryParser::ConstructParseException(const char *str) const noexcept
diff --git a/src/GCodes/GCodeBuffer/BinaryParser.h b/src/GCodes/GCodeBuffer/BinaryParser.h
index 19615473..18f0c675 100644
--- a/src/GCodes/GCodeBuffer/BinaryParser.h
+++ b/src/GCodes/GCodeBuffer/BinaryParser.h
@@ -59,7 +59,7 @@ public:
void PrintCommand(const StringRef& s) const noexcept;
void AppendFullCommand(const StringRef &s) const noexcept;
- void SetParameters(VariableSet& vs, int codeRunning) noexcept;
+ void AddParameters(VariableSet& vs, int codeRunning) noexcept;
private:
GCodeBuffer& gb;
diff --git a/src/GCodes/GCodeBuffer/ExpressionParser.cpp b/src/GCodes/GCodeBuffer/ExpressionParser.cpp
index 3575fbb7..2a536da1 100644
--- a/src/GCodes/GCodeBuffer/ExpressionParser.cpp
+++ b/src/GCodes/GCodeBuffer/ExpressionParser.cpp
@@ -12,6 +12,7 @@
#include <Platform/Platform.h>
#include <General/NamedEnum.h>
#include <General/NumericConverter.h>
+#include <Hardware/ExceptionHandlers.h>
#include <limits>
@@ -21,6 +22,14 @@
constexpr size_t MaxStringExpressionLength = StringLength100;
+namespace StackUsage
+{
+ // The following values are obtained from file ExpressionParser.su generated by the compiler
+ constexpr uint32_t ParseInternal = 72;
+ constexpr uint32_t ParseIdentifierExpression = 256;
+ constexpr uint32_t GetObjectValue_withTable = 48;
+}
+
// These can't be declared locally inside ParseIdentifierExpression because NamedEnum includes static data
NamedEnum(NamedConstant, unsigned int, _false, iterations, line, _null, pi, _result, _true);
NamedEnum(Function, unsigned int, abs, acos, asin, atan, atan2, cos, degrees, exists, floor, isnan, max, min, mod, radians, random, sin, sqrt, tan);
@@ -33,22 +42,23 @@ ExpressionParser::ExpressionParser(const GCodeBuffer& p_gb, const char *text, co
}
// Evaluate a bracketed expression
-ExpressionValue ExpressionParser::ParseExpectKet(bool evaluate, char closingBracket) THROWS(GCodeException)
+void ExpressionParser::ParseExpectKet(ExpressionValue& rslt, bool evaluate, char closingBracket) THROWS(GCodeException)
{
- auto rslt = Parse(evaluate);
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(rslt, evaluate, 0);
if (CurrentCharacter() != closingBracket)
{
- throw ConstructParseException("expected '%c'", (uint32_t)closingBracket);
+ ThrowParseException("expected '%c'", (uint32_t)closingBracket);
}
AdvancePointer();
- return rslt;
}
-// Evaluate an expression
+// Evaluate an expression. Do not call this one recursively!
ExpressionValue ExpressionParser::Parse(bool evaluate) THROWS(GCodeException)
{
obsoleteField.Clear();
- ExpressionValue result = ParseInternal(evaluate);
+ ExpressionValue result;
+ ParseInternal(result, evaluate, 0);
if (!obsoleteField.IsEmpty())
{
reprap.GetPlatform().MessageF(WarningMessage, "obsolete object model field %s queried\n", obsoleteField.c_str());
@@ -56,27 +66,9 @@ ExpressionValue ExpressionParser::Parse(bool evaluate) THROWS(GCodeException)
return result;
}
-// Evaluate an expression that must either be a numeric or string literal or enclosed in { }
-ExpressionValue ExpressionParser::ParseSimple() THROWS(GCodeException)
-{
- const char c = CurrentCharacter();
- if (c == '{')
- {
- return ParseExpectKet(true, '}');
- }
- if (c == '"')
- {
- return ParseQuotedString();
- }
- if (isDigit(c))
- {
- return ParseNumber();
- }
- throw ConstructParseException("expected simple expression");
-}
-
// Evaluate an expression internally, stopping before any binary operators with priority 'priority' or lower
-ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority) THROWS(GCodeException)
+// This is recursive, so avoid allocating large amounts of data on the stack
+void ExpressionParser::ParseInternal(ExpressionValue& val, bool evaluate, uint8_t priority) THROWS(GCodeException)
{
// Lists of binary operators and their priorities
static constexpr const char *operators = "?^&|!=<>+-*/"; // for multi-character operators <= and >= and != this is the first character
@@ -87,16 +79,16 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
// Start by looking for a unary operator or opening bracket
SkipWhiteSpace();
const char c = CurrentCharacter();
- ExpressionValue val;
switch (c)
{
case '"':
- val = ParseQuotedString();
+ ParseQuotedString(val);
break;
case '-':
AdvancePointer();
- val = ParseInternal(evaluate, UnaryPriority);
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val, evaluate, UnaryPriority);
switch (val.GetType())
{
case TypeCode::Int32:
@@ -108,13 +100,14 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
break;
default:
- throw ConstructParseException("expected numeric value after '-'");
+ ThrowParseException("expected numeric value after '-'");
}
break;
case '+':
AdvancePointer();
- val = ParseInternal(evaluate, UnaryPriority);
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val, evaluate, UnaryPriority);
switch (val.GetType())
{
case TypeCode::Uint32:
@@ -128,7 +121,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
break;
default:
- throw ConstructParseException("expected numeric or enumeration value after '+'");
+ ThrowParseException("expected numeric or enumeration value after '+'");
}
break;
@@ -138,11 +131,13 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
if (isalpha(CurrentCharacter()))
{
// Probably applying # to an object model array, so optimise by asking the OM for just the length
- val = ParseIdentifierExpression(evaluate, true, false);
+ CheckStack(StackUsage::ParseIdentifierExpression);
+ ParseIdentifierExpression(val, evaluate, true, false);
}
else
{
- val = ParseInternal(evaluate, UnaryPriority);
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val, evaluate, UnaryPriority);
if (val.GetType() == TypeCode::CString)
{
val.Set((int32_t)strlen(val.sVal));
@@ -153,24 +148,25 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
}
else
{
- throw ConstructParseException("expected object model value or string after '#");
+ ThrowParseException("expected object model value or string after '#");
}
}
break;
case '{':
AdvancePointer();
- val = ParseExpectKet(evaluate, '}');
+ ParseExpectKet(val, evaluate, '}');
break;
case '(':
AdvancePointer();
- val = ParseExpectKet(evaluate, ')');
+ ParseExpectKet(val, evaluate, ')');
break;
case '!':
AdvancePointer();
- val = ParseInternal(evaluate, UnaryPriority);
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val, evaluate, UnaryPriority);
ConvertToBool(val, evaluate);
val.bVal = !val.bVal;
break;
@@ -178,15 +174,16 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
default:
if (isdigit(c)) // looks like a number
{
- val = ParseNumber();
+ ParseNumber(val);
}
else if (isalpha(c)) // looks like a variable name
{
- val = ParseIdentifierExpression(evaluate, false, false);
+ CheckStack(StackUsage::ParseIdentifierExpression);
+ ParseIdentifierExpression(val, evaluate, false, false);
}
else
{
- throw ConstructParseException("expected an expression");
+ ThrowParseException("expected an expression");
}
break;
}
@@ -198,20 +195,19 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
char opChar = CurrentCharacter();
if (opChar == 0) // don't pass null to strchr
{
-
- return val;
+ return;
}
const char * const q = strchr(operators, opChar);
if (q == nullptr)
{
- return val;
+ return;
}
const size_t index = q - operators;
const uint8_t opPrio = priorities[index];
if (opPrio <= priority)
{
- return val;
+ return;
}
AdvancePointer(); // skip the [first] operator character
@@ -222,7 +218,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
{
if (CurrentCharacter() != '=')
{
- throw ConstructParseException("expected '='");
+ ThrowParseException("expected '='");
}
invert = true;
AdvancePointer();
@@ -247,11 +243,13 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
case '&':
ConvertToBool(val, evaluate);
{
- ExpressionValue val2 = ParseInternal(evaluate && val.bVal, opPrio); // get the next operand
+ ExpressionValue val2;
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val2, evaluate && val.bVal, opPrio); // get the next operand
if (val.bVal)
{
ConvertToBool(val2, evaluate);
- val.bVal = val.bVal && val2.bVal;
+ val.bVal = val2.bVal;
}
}
break;
@@ -259,11 +257,13 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
case '|':
ConvertToBool(val, evaluate);
{
- ExpressionValue val2 = ParseInternal(evaluate && !val.bVal, opPrio); // get the next operand
+ ExpressionValue val2;
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val2, evaluate && !val.bVal, opPrio); // get the next operand
if (!val.bVal)
{
ConvertToBool(val2, evaluate);
- val.bVal = val.bVal || val2.bVal;
+ val.bVal = val2.bVal;
}
}
break;
@@ -271,20 +271,26 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
case '?':
ConvertToBool(val, evaluate);
{
- ExpressionValue val2 = ParseInternal(evaluate && val.bVal, opPrio); // get the second operand
+ const bool b = val.bVal;
+ ExpressionValue val2;
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(((b) ? val : val2), evaluate && b, opPrio); // get the second operand
if (CurrentCharacter() != ':')
{
- throw ConstructParseException("expected ':'");
+ ThrowParseException("expected ':'");
}
AdvancePointer();
- ExpressionValue val3 = ParseInternal(evaluate && !val.bVal, opPrio - 1); // get the third operand, which may be a further conditional expression
- return (val.bVal) ? val2 : val3;
+ // We recently checked the stack for a call to ParseInternal, no need to do it again
+ ParseInternal(((b) ? val2 : val), evaluate && !b, opPrio - 1); // get the third operand, which may be a further conditional expression
+ return;
}
default:
// Handle binary operators that always evaluate both operands
{
- ExpressionValue val2 = ParseInternal(evaluate, opPrio); // get the next operand
+ ExpressionValue val2;
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(val2, evaluate, opPrio); // get the next operand
switch(opChar)
{
case '+':
@@ -300,7 +306,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
}
else
{
- throw ConstructParseException("invalid operand types");
+ ThrowParseException("invalid operand types");
}
}
else
@@ -337,7 +343,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
}
else
{
- throw ConstructParseException("invalid operand types");
+ ThrowParseException("invalid operand types");
}
}
else
@@ -392,7 +398,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
break;
default:
- throw ConstructParseException("expected numeric or Boolean operands to comparison operator");
+ ThrowParseException("expected numeric or Boolean operands to comparison operator");
}
val.SetType(TypeCode::Bool);
if (invert)
@@ -418,7 +424,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
break;
default:
- throw ConstructParseException("expected numeric or Boolean operands to comparison operator");
+ ThrowParseException("expected numeric or Boolean operands to comparison operator");
}
val.SetType(TypeCode::Bool);
if (invert)
@@ -443,7 +449,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
switch (val.GetType())
{
case TypeCode::ObjectModel:
- throw ConstructParseException("cannot compare objects");
+ ThrowParseException("cannot compare objects");
case TypeCode::Int32:
val.bVal = (val.iVal == val2.iVal);
@@ -470,7 +476,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
break;
default:
- throw ConstructParseException("unexpected operand type to equality operator");
+ ThrowParseException("unexpected operand type to equality operator");
}
}
val.SetType(TypeCode::Bool);
@@ -481,14 +487,7 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
break;
case '^':
- {
- String<MaxStringExpressionLength> str;
- val.AppendAsString(str.GetRef());
- val2.AppendAsString(str.GetRef());
- StringHandle sh(str.c_str());
- val.Set(sh);
- val2.Release();
- }
+ StringConcat(val, val2);
break;
}
}
@@ -496,6 +495,17 @@ ExpressionValue ExpressionParser::ParseInternal(bool evaluate, uint8_t priority)
} while (true);
}
+// Concatenate val1 and val2 and assign the result to val1
+// This is written as a separate function because it needs a temporary string buffer, and its caller is recursive. Its declaration must be declared 'noinline'.
+/*static*/ void ExpressionParser::StringConcat(ExpressionValue &val, ExpressionValue &val2) noexcept
+{
+ String<MaxStringExpressionLength> str;
+ val.AppendAsString(str.GetRef());
+ val2.AppendAsString(str.GetRef());
+ StringHandle sh(str.c_str());
+ val.Set(sh);
+}
+
bool ExpressionParser::ParseBoolean() THROWS(GCodeException)
{
ExpressionValue val = Parse();
@@ -521,12 +531,12 @@ int32_t ExpressionParser::ParseInteger() THROWS(GCodeException)
case TypeCode::Uint32:
if (val.uVal > (uint32_t)std::numeric_limits<int32_t>::max())
{
- throw ConstructParseException("unsigned integer too large");
+ ThrowParseException("unsigned integer too large");
}
return (int32_t)val.uVal;
default:
- throw ConstructParseException("expected integer value");
+ ThrowParseException("expected integer value");
}
}
@@ -543,10 +553,10 @@ uint32_t ExpressionParser::ParseUnsigned() THROWS(GCodeException)
{
return (uint32_t)val.iVal;
}
- throw ConstructParseException("value must be non-negative");
+ ThrowParseException("value must be non-negative");
default:
- throw ConstructParseException("expected non-negative integer value");
+ ThrowParseException("expected non-negative integer value");
}
}
@@ -564,7 +574,7 @@ void ExpressionParser::BalanceNumericTypes(ExpressionValue& val1, ExpressionValu
{
if (evaluate)
{
- throw ConstructParseException("expected numeric operands");
+ ThrowParseException("expected numeric operands");
}
val1.Set((int32_t)0);
val2.Set((int32_t)0);
@@ -603,7 +613,7 @@ void ExpressionParser::BalanceTypes(ExpressionValue& val1, ExpressionValue& val2
{
if (evaluate)
{
- throw ConstructParseException("cannot convert operands to same type");
+ ThrowParseException("cannot convert operands to same type");
}
val1.Set((int32_t)0);
val2.Set((int32_t)0);
@@ -626,7 +636,7 @@ void ExpressionParser::EnsureNumeric(ExpressionValue& val, bool evaluate) const
default:
if (evaluate)
{
- throw ConstructParseException("expected numeric operand");
+ ThrowParseException("expected numeric operand");
}
val.Set((int32_t)0);
}
@@ -648,9 +658,9 @@ void ExpressionParser::ConvertToFloat(ExpressionValue& val, bool evaluate) const
default:
if (evaluate)
{
- throw ConstructParseException("expected numeric operand");
+ ThrowParseException("expected numeric operand");
}
- val.Set(0.0f);
+ val.Set(0.0f, 1);
}
}
@@ -660,13 +670,13 @@ void ExpressionParser::ConvertToBool(ExpressionValue& val, bool evaluate) const
{
if (evaluate)
{
- throw ConstructParseException("expected Boolean operand");
+ ThrowParseException("expected Boolean operand");
}
val.Set(false);
}
}
-void ExpressionParser::ConvertToString(ExpressionValue& val, bool evaluate) THROWS(GCodeException)
+void ExpressionParser::ConvertToString(ExpressionValue& val, bool evaluate) noexcept
{
if (!val.IsStringType())
{
@@ -698,28 +708,34 @@ void ExpressionParser::CheckForExtraCharacters() THROWS(GCodeException)
SkipWhiteSpace();
if (CurrentCharacter() != 0)
{
- throw ConstructParseException("Unexpected characters after expression");
+ ThrowParseException("Unexpected characters after expression");
}
}
// Parse a number. The initial character of the string is a decimal digit.
-ExpressionValue ExpressionParser::ParseNumber() noexcept
+void ExpressionParser::ParseNumber(ExpressionValue& rslt) noexcept
{
NumericConverter conv;
- conv.Accumulate(CurrentCharacter(), NumericConverter::AcceptSignedFloat | NumericConverter::AcceptHex, [this]()->char { AdvancePointer(); return CurrentCharacter(); }); // must succeed because CurrentCharacter is a decimal digit
- return (conv.FitsInInt32())
- ? ExpressionValue(conv.GetInt32())
- : ExpressionValue(conv.GetFloat(), constrain<unsigned int>(conv.GetDigitsAfterPoint(), 1, MaxFloatDigitsDisplayedAfterPoint));
+ conv.Accumulate(CurrentCharacter(), NumericConverter::AcceptSignedFloat | NumericConverter::AcceptHex, [this]()->char { AdvancePointer(); return CurrentCharacter(); }); // must succeed because CurrentCharacter is a decimal digit
+
+ if (conv.FitsInInt32())
+ {
+ rslt.Set(conv.GetInt32());
+ }
+ else
+ {
+ rslt.Set(conv.GetFloat(), constrain<unsigned int>(conv.GetDigitsAfterPoint(), 1, MaxFloatDigitsDisplayedAfterPoint));
+ }
}
// Parse an identifier expression
// If 'evaluate' is false then the object model path may not exist, in which case we must ignore error that and parse it all anyway
// This means we can use expressions such as: if {a.b == null || a.b.c == 1}
-ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool applyLengthOperator, bool applyExists) THROWS(GCodeException)
+void ExpressionParser::ParseIdentifierExpression(ExpressionValue& rslt, bool evaluate, bool applyLengthOperator, bool applyExists) THROWS(GCodeException)
{
if (!isalpha(CurrentCharacter()))
{
- throw ConstructParseException("expected an identifier");
+ ThrowParseException("expected an identifier");
}
String<MaxVariableNameLength> id;
@@ -733,14 +749,16 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
AdvancePointer();
if (c == '[')
{
- const ExpressionValue index = Parse(evaluate);
+ ExpressionValue index;
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(index, evaluate, 0);
if (CurrentCharacter() != ']')
{
- throw ConstructParseException("expected ']'");
+ ThrowParseException("expected ']'");
}
if (index.GetType() != TypeCode::Int32)
{
- throw ConstructParseException("expected integer expression");
+ ThrowParseException("expected integer expression");
}
AdvancePointer(); // skip the ']'
context.ProvideIndex(index.iVal);
@@ -748,7 +766,7 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
}
if (id.cat(c))
{
- throw ConstructParseException("variable name too long");;
+ ThrowParseException("variable name too long");;
}
}
@@ -758,56 +776,63 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
{
if (context.WantExists())
{
- throw ConstructParseException(InvalidExistsMessage);
+ ThrowParseException(InvalidExistsMessage);
}
switch (whichConstant.RawValue())
{
case NamedConstant::_true:
- return ExpressionValue(true);
+ rslt.Set(true);
+ return;
case NamedConstant::_false:
- return ExpressionValue(false);
+ rslt.Set(false);
+ return;
case NamedConstant::_null:
- return ExpressionValue(nullptr);
+ rslt.Set(nullptr);
+ return;
case NamedConstant::pi:
- return ExpressionValue(Pi);
+ rslt.Set(Pi);
+ return;
case NamedConstant::iterations:
{
const int32_t v = gb.CurrentFileMachineState().GetIterations();
if (v < 0)
{
- throw ConstructParseException("'iterations' used when not inside a loop");
+ ThrowParseException("'iterations' used when not inside a loop");
}
- return ExpressionValue(v);
+ rslt.Set(v);
}
+ return;
case NamedConstant::_result:
{
- int32_t rslt;
+ int32_t res;
switch (gb.GetLastResult())
{
case GCodeResult::ok:
- rslt = 0;
+ res = 0;
break;
case GCodeResult::warning:
case GCodeResult::warningNotSupported:
- rslt = 1;
+ res = 1;
break;
default:
- rslt = 2;
+ res = 2;
break;
}
- return ExpressionValue(rslt);
+ rslt.Set(res);
}
+ return;
case NamedConstant::line:
- return ExpressionValue((int32_t)gb.GetLineNumber());
+ rslt.Set((int32_t)gb.GetLineNumber());
+ return;
default:
THROW_INTERNAL_ERROR;
@@ -821,24 +846,25 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
// It's a function call
if (context.WantExists())
{
- throw ConstructParseException(InvalidExistsMessage);
+ ThrowParseException(InvalidExistsMessage);
}
const Function func(id.c_str());
if (!func.IsValid())
{
- throw ConstructParseException("unknown function");
+ ThrowParseException("unknown function");
}
AdvancePointer();
- ExpressionValue rslt;
if (func == Function::exists)
{
- rslt = ParseIdentifierExpression(evaluate, false, true);
+ CheckStack(StackUsage::ParseIdentifierExpression);
+ ParseIdentifierExpression(rslt, evaluate, false, true);
}
else
{
- rslt = Parse(evaluate); // evaluate the first operand
+ CheckStack(StackUsage::ParseInternal);
+ ParseInternal(rslt, evaluate, 0); // evaluate the first operand
switch (func.RawValue())
{
@@ -856,7 +882,7 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
default:
if (evaluate)
{
- throw ConstructParseException("expected numeric operand");
+ ThrowParseException("expected numeric operand");
}
rslt.Set((int32_t)0);
}
@@ -904,11 +930,13 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
SkipWhiteSpace();
if (CurrentCharacter() != ',')
{
- throw ConstructParseException("expected ','");
+ ThrowParseException("expected ','");
}
AdvancePointer();
SkipWhiteSpace();
- ExpressionValue nextOperand = Parse(evaluate);
+ ExpressionValue nextOperand;
+ // We recently checked the stack for a call to ParseInternal, no need to do it again
+ ParseInternal(nextOperand, evaluate, 0);
ConvertToFloat(nextOperand, evaluate);
rslt.fVal = atan2f(rslt.fVal, nextOperand.fVal);
rslt.param = MaxFloatDigitsDisplayedAfterPoint;
@@ -960,11 +988,13 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
SkipWhiteSpace();
if (CurrentCharacter() != ',')
{
- throw ConstructParseException("expected ','");
+ ThrowParseException("expected ','");
}
AdvancePointer();
SkipWhiteSpace();
- ExpressionValue nextOperand = Parse(evaluate);
+ ExpressionValue nextOperand;
+ // We recently checked the stack for a call to ParseInternal, no need to do it again
+ ParseInternal(nextOperand, evaluate, 0);
BalanceNumericTypes(rslt, nextOperand, evaluate);
if (rslt.GetType() == TypeCode::Float)
{
@@ -991,7 +1021,9 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
}
AdvancePointer();
SkipWhiteSpace();
- ExpressionValue nextOperand = Parse(evaluate);
+ ExpressionValue nextOperand;
+ // We recently checked the stack for a call to ParseInternal, no need to do it again
+ ParseInternal(nextOperand, evaluate, 0);
BalanceNumericTypes(rslt, nextOperand, evaluate);
if (rslt.GetType() == TypeCode::Float)
{
@@ -1015,7 +1047,9 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
}
AdvancePointer();
SkipWhiteSpace();
- ExpressionValue nextOperand = Parse(evaluate);
+ ExpressionValue nextOperand;
+ // We recently checked the stack for a call to ParseInternal, no need to do it again
+ ParseInternal(nextOperand, evaluate, 0);
BalanceNumericTypes(rslt, nextOperand, evaluate);
if (rslt.GetType() == TypeCode::Float)
{
@@ -1031,11 +1065,20 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
case Function::random:
{
- const uint32_t limit = (rslt.GetType() == TypeCode::Uint32) ? rslt.uVal
- : (rslt.GetType() == TypeCode::Int32 && rslt.iVal > 0) ? rslt.iVal
- : throw ConstructParseException("expected positive integer");
- rslt.SetType(TypeCode::Int32);
- rslt.iVal = random(limit);
+ uint32_t limit;
+ if (rslt.GetType() == TypeCode::Uint32)
+ {
+ limit = rslt.uVal;
+ }
+ else if (rslt.GetType() == TypeCode::Int32 && rslt.iVal > 0)
+ {
+ limit = rslt.iVal;
+ }
+ else
+ {
+ ThrowParseException("expected positive integer");
+ }
+ rslt.Set((int32_t)random(limit));
}
break;
@@ -1047,10 +1090,10 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
SkipWhiteSpace();
if (CurrentCharacter() != ')')
{
- throw ConstructParseException("expected ')'");
+ ThrowParseException("expected ')'");
}
AdvancePointer();
- return rslt;
+ return;
}
// If we are not evaluating then the object expression doesn't have to exist, so don't retrieve it because that might throw an error
@@ -1059,58 +1102,65 @@ ExpressionValue ExpressionParser::ParseIdentifierExpression(bool evaluate, bool
// Check for a parameter, local or global variable
if (StringStartsWith(id.c_str(), "param."))
{
- return GetVariableValue(&gb.GetVariables(), id.c_str() + strlen("param."), true, context.WantExists());
+ GetVariableValue(rslt, &gb.GetVariables(), id.c_str() + strlen("param."), true, applyExists);
+ return;
}
if (StringStartsWith(id.c_str(), "global."))
{
auto vars = reprap.GetGlobalVariablesForReading();
- return GetVariableValue(vars.Ptr(), id.c_str() + strlen("global."), false, context.WantExists());
+ GetVariableValue(rslt, vars.Ptr(), id.c_str() + strlen("global."), false, applyExists);
+ return;
}
if (StringStartsWith(id.c_str(), "var."))
{
- return GetVariableValue(&gb.GetVariables(), id.c_str() + strlen("var."), false, context.WantExists());
+ GetVariableValue(rslt, &gb.GetVariables(), id.c_str() + strlen("var."), false, applyExists);
+ return;
}
// "exists(var)", "exists(param)" and "exists(global)" should return true.
// "exists(global)" will anyway because "global" is a root key in the object model. Handle the other two here.
- if (context.WantExists() && (strcmp(id.c_str(), "param") == 0 || strcmp(id.c_str(), "var") == 0))
+ if (applyExists && (strcmp(id.c_str(), "param") == 0 || strcmp(id.c_str(), "var") == 0))
{
- return ExpressionValue(true);
+ rslt.Set(true);
+ return;
}
// Else assume an object model value
- ExpressionValue value = reprap.GetObjectValue(context, nullptr, id.c_str());
+ CheckStack(StackUsage::GetObjectValue_withTable);
+ rslt = reprap.GetObjectValue(context, nullptr, id.c_str(), 0);
if (context.ObsoleteFieldQueried() && obsoleteField.IsEmpty())
{
obsoleteField.copy(id.c_str());
}
- return value;
+ return;
}
- return ExpressionValue(nullptr);
+ rslt.Set(nullptr);
}
// Get the value of a variable
-ExpressionValue ExpressionParser::GetVariableValue(const VariableSet *vars, const char *name, bool parameter, bool wantExists) THROWS(GCodeException)
+void ExpressionParser::GetVariableValue(ExpressionValue& rslt, const VariableSet *vars, const char *name, bool parameter, bool wantExists) THROWS(GCodeException)
{
const Variable* var = vars->Lookup(name);
if (wantExists)
{
- return ExpressionValue(var != nullptr);
+ rslt.Set(var != nullptr);
+ return;
}
if (var != nullptr && (!parameter || var->GetScope() < 0))
{
- return var->GetValue();
+ rslt = var->GetValue();
+ return;
}
- throw ConstructParseException((parameter) ? "unknown parameter '%s'" : "unknown variable '%s'", name);
+ ThrowParseException((parameter) ? "unknown parameter '%s'" : "unknown variable '%s'", name);
}
// Parse a quoted string, given that the current character is double-quote
// This is almost a copy of InternalGetQuotedString in class StringParser
-ExpressionValue ExpressionParser::ParseQuotedString() THROWS(GCodeException)
+void ExpressionParser::ParseQuotedString(ExpressionValue& rslt) THROWS(GCodeException)
{
String<MaxStringExpressionLength> str;
AdvancePointer();
@@ -1120,14 +1170,15 @@ ExpressionValue ExpressionParser::ParseQuotedString() THROWS(GCodeException)
AdvancePointer();
if (c < ' ')
{
- throw ConstructParseException("control character in string");
+ ThrowParseException("control character in string");
}
if (c == '"')
{
if (CurrentCharacter() != c)
{
StringHandle sh(str.c_str());
- return ExpressionValue(sh);
+ rslt.Set(sh);
+ return;
}
AdvancePointer();
}
@@ -1147,7 +1198,7 @@ ExpressionValue ExpressionParser::ParseQuotedString() THROWS(GCodeException)
}
if (str.cat(c))
{
- throw ConstructParseException("string too long");
+ ThrowParseException("string too long");
}
}
}
@@ -1163,19 +1214,40 @@ int ExpressionParser::GetColumn() const noexcept
return (column < 0) ? column : (currentp - startp) + column;
}
-GCodeException ExpressionParser::ConstructParseException(const char *str) const noexcept
+void ExpressionParser::ThrowParseException(const char *str) const THROWS(GCodeException)
{
- return GCodeException(gb.GetLineNumber(), GetColumn(), str);
+ throw GCodeException(gb.GetLineNumber(), GetColumn(), str);
}
-GCodeException ExpressionParser::ConstructParseException(const char *str, const char *param) const noexcept
+void ExpressionParser::ThrowParseException(const char *str, const char *param) const THROWS(GCodeException)
{
- return GCodeException(gb.GetLineNumber(), GetColumn(), str, param);
+ throw GCodeException(gb.GetLineNumber(), GetColumn(), str, param);
}
-GCodeException ExpressionParser::ConstructParseException(const char *str, uint32_t param) const noexcept
+void ExpressionParser::ThrowParseException(const char *str, uint32_t param) const THROWS(GCodeException)
{
- return GCodeException(gb.GetLineNumber(), GetColumn(), str, param);
+ throw GCodeException(gb.GetLineNumber(), GetColumn(), str, param);
+}
+
+// Call this before making a recursive call, or before calling a function that needs a lot of stack from a recursive function
+void ExpressionParser::CheckStack(uint32_t calledFunctionStackUsage) const THROWS(GCodeException)
+{
+ register const char * stackPtr asm ("sp");
+ const char *stackLimit = (const char*)TaskBase::GetCallerTaskHandle() + sizeof(TaskBase);
+ //debugPrintf("Margin: %u\n", stackPtr - stackLimit);
+ if (stackLimit + calledFunctionStackUsage + (StackUsage::Throw + StackUsage::Margin) <= stackPtr)
+ {
+ return;
+ }
+
+ // The stack is in danger of overflowing. Throw an exception if we have enough stack to do so (ideally, this should always be the case)
+ if (stackLimit + StackUsage::Throw <= stackPtr)
+ {
+ throw GCodeException(gb.GetLineNumber(), GetColumn(), "Expression nesting too deep");
+ }
+
+ // Not enough stack left to throw an exception
+ SoftwareReset(SoftwareResetReason::stackOverflow, (const uint32_t *)stackPtr);
}
// End
diff --git a/src/GCodes/GCodeBuffer/ExpressionParser.h b/src/GCodes/GCodeBuffer/ExpressionParser.h
index a4e6cb06..778a5b22 100644
--- a/src/GCodes/GCodeBuffer/ExpressionParser.h
+++ b/src/GCodes/GCodeBuffer/ExpressionParser.h
@@ -20,7 +20,6 @@ public:
ExpressionParser(const GCodeBuffer& p_gb, const char *text, const char *textLimit, int p_column = -1) noexcept;
ExpressionValue Parse(bool evaluate = true) THROWS(GCodeException);
- ExpressionValue ParseSimple() THROWS(GCodeException);
bool ParseBoolean() THROWS(GCodeException);
float ParseFloat() THROWS(GCodeException);
int32_t ParseInteger() THROWS(GCodeException);
@@ -31,22 +30,27 @@ public:
const char *GetEndptr() const noexcept { return currentp; }
private:
- GCodeException ConstructParseException(const char *str) const noexcept;
- GCodeException ConstructParseException(const char *str, const char *param) const noexcept;
- GCodeException ConstructParseException(const char *str, uint32_t param) const noexcept;
+ [[noreturn]] void __attribute__((noinline)) ThrowParseException(const char *str) const THROWS(GCodeException);
+ [[noreturn]] void __attribute__((noinline)) ThrowParseException(const char *str, const char *param) const THROWS(GCodeException);
+ [[noreturn]] void __attribute__((noinline)) ThrowParseException(const char *str, uint32_t param) const THROWS(GCodeException);
- ExpressionValue ParseInternal(bool evaluate = true, uint8_t priority = 0) THROWS(GCodeException);
- ExpressionValue ParseExpectKet(bool evaluate, char expectedKet) THROWS(GCodeException);
- ExpressionValue ParseNumber() noexcept
+ void ParseInternal(ExpressionValue& val, bool evaluate, uint8_t priority) THROWS(GCodeException);
+ void ParseExpectKet(ExpressionValue& rslt, bool evaluate, char expectedKet) THROWS(GCodeException);
+ void __attribute__((noinline)) ParseNumber(ExpressionValue& rslt) noexcept
pre(readPointer >= 0; isdigit(gb.buffer[readPointer]));
- ExpressionValue ParseIdentifierExpression(bool evaluate, bool applyLengthOperator, bool applyExists) THROWS(GCodeException)
+ void __attribute__((noinline)) ParseIdentifierExpression(ExpressionValue& rslt, bool evaluate, bool applyLengthOperator, bool applyExists) THROWS(GCodeException)
pre(readPointer >= 0; isalpha(gb.buffer[readPointer]));
- ExpressionValue ParseQuotedString() THROWS(GCodeException);
- ExpressionValue GetVariableValue(const VariableSet *vars, const char *name, bool parameter, bool wantExists) THROWS(GCodeException);
+ void __attribute__((noinline)) ParseQuotedString(ExpressionValue& rslt) THROWS(GCodeException);
+ void GetVariableValue(ExpressionValue& rslt, const VariableSet *vars, const char *name, bool parameter, bool wantExists) THROWS(GCodeException);
void ConvertToFloat(ExpressionValue& val, bool evaluate) const THROWS(GCodeException);
void ConvertToBool(ExpressionValue& val, bool evaluate) const THROWS(GCodeException);
- void ConvertToString(ExpressionValue& val, bool evaluate) THROWS(GCodeException);
+ void ConvertToString(ExpressionValue& val, bool evaluate) noexcept;
+
+ void CheckStack(uint32_t calledFunctionStackUsage) const THROWS(GCodeException);
+
+ // The following must be declared 'noinline' because it allocates a large buffer on the stack and its caller is recursive
+ static void __attribute__((noinline)) StringConcat(ExpressionValue &val, ExpressionValue &val2) noexcept;
void BalanceNumericTypes(ExpressionValue& val1, ExpressionValue& val2, bool evaluate) const THROWS(GCodeException);
void BalanceTypes(ExpressionValue& val1, ExpressionValue& val2, bool evaluate) THROWS(GCodeException);
diff --git a/src/GCodes/GCodeBuffer/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer/GCodeBuffer.cpp
index 01466213..fea25c00 100644
--- a/src/GCodes/GCodeBuffer/GCodeBuffer.cpp
+++ b/src/GCodes/GCodeBuffer/GCodeBuffer.cpp
@@ -626,18 +626,18 @@ bool GCodeBuffer::TryGetFloatArray(char c, size_t numVals, float vals[], const S
// Try to get a quoted string after parameter letter.
// If we found it then set 'seen' true and return true, else leave 'seen' alone and return false
-bool GCodeBuffer::TryGetQuotedString(char c, const StringRef& str, bool& seen) THROWS(GCodeException)
+bool GCodeBuffer::TryGetQuotedString(char c, const StringRef& str, bool& seen, bool allowEmpty) THROWS(GCodeException)
{
if (Seen(c))
{
seen = true;
- GetQuotedString(str);
+ GetQuotedString(str, allowEmpty);
return true;
}
return false;
}
-// Try to get a string, which may be quoted, after parameter letter.
+// Try to get a non-empty string, which may be quoted, after parameter letter.
// If we found it then set 'seen' true and return true, else leave 'seen' alone and return false
bool GCodeBuffer::TryGetPossiblyQuotedString(char c, const StringRef& str, bool& seen) THROWS(GCodeException)
{
@@ -1027,9 +1027,9 @@ void GCodeBuffer::AppendFullCommand(const StringRef &s) const noexcept
PARSER_OPERATION(AppendFullCommand(s));
}
-void GCodeBuffer::SetParameters(int codeRunning) noexcept
+void GCodeBuffer::AddParameters(VariableSet& vars, int codeRunning) noexcept
{
- PARSER_OPERATION(SetParameters(GetVariables(), codeRunning));
+ PARSER_OPERATION(AddParameters(vars, codeRunning));
}
VariableSet& GCodeBuffer::GetVariables() const noexcept
diff --git a/src/GCodes/GCodeBuffer/GCodeBuffer.h b/src/GCodes/GCodeBuffer/GCodeBuffer.h
index 05f6c284..fb1f7a3f 100644
--- a/src/GCodes/GCodeBuffer/GCodeBuffer.h
+++ b/src/GCodes/GCodeBuffer/GCodeBuffer.h
@@ -110,7 +110,7 @@ public:
bool TryGetBValue(char c, bool& val, bool& seen) THROWS(GCodeException);
bool TryGetFloatArray(char c, size_t numVals, float vals[], const StringRef& reply, bool& seen, bool doPad = false) THROWS(GCodeException);
bool TryGetUIArray(char c, size_t numVals, uint32_t vals[], const StringRef& reply, bool& seen, bool doPad = false) THROWS(GCodeException);
- bool TryGetQuotedString(char c, const StringRef& str, bool& seen) THROWS(GCodeException);
+ bool TryGetQuotedString(char c, const StringRef& str, bool& seen, bool allowEmpty = false) THROWS(GCodeException);
bool TryGetPossiblyQuotedString(char c, const StringRef& str, bool& seen) THROWS(GCodeException);
bool IsIdle() const noexcept;
@@ -226,7 +226,7 @@ public:
void MotionStopped() noexcept { motionCommanded = false; }
bool WasMotionCommanded() const noexcept { return motionCommanded; }
- void SetParameters(int codeRunning) noexcept;
+ void AddParameters(VariableSet& vars, int codeRunning) noexcept;
VariableSet& GetVariables() const noexcept;
Mutex mutex;
diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp
index 7db2e7b2..abf822e5 100644
--- a/src/GCodes/GCodeBuffer/StringParser.cpp
+++ b/src/GCodes/GCodeBuffer/StringParser.cpp
@@ -413,7 +413,9 @@ bool StringParser::ProcessConditionalGCode(const StringRef& reply, BlockType ski
}
}
- if (i >= 2 && i < 9 && (gb.buffer[i] == 0 || gb.buffer[i] == ' ' || gb.buffer[i] == '\t' || gb.buffer[i] == '{')) // if the command word is properly terminated
+ if (i >= 2 && i < 9
+ && (gb.buffer[i] == 0 || gb.buffer[i] == ' ' || gb.buffer[i] == '\t' || gb.buffer[i] == '{' || gb.buffer[i] == '"' || gb.buffer[i] == '(') // if the command word is properly terminated
+ )
{
readPointer = i;
const char * const command = gb.buffer;
@@ -894,10 +896,8 @@ void StringParser::DecodeCommand() noexcept
}
else if ( hasCommandNumber
&& commandLetter == 'G'
- && commandNumber <= 3
- && ( strchr(reprap.GetGCodes().GetAxisLetters(), cl) != nullptr
- || ((cl == 'I' || cl == 'J') && commandNumber >= 2)
- )
+ && commandNumber <= 1
+ && strchr(reprap.GetGCodes().GetAxisLetters(), cl) != nullptr
&& ( reprap.GetGCodes().GetMachineType() == MachineType::cnc // Fanuc style CNC
|| reprap.GetGCodes().GetMachineType() == MachineType::laser // LaserWeb style
)
@@ -1757,7 +1757,7 @@ void StringParser::SkipWhiteSpace() noexcept
}
}
-void StringParser::SetParameters(VariableSet& vs, int codeRunning) noexcept
+void StringParser::AddParameters(VariableSet& vs, int codeRunning) noexcept
{
parametersPresent.Iterate([this, &vs, codeRunning](unsigned int bit, unsigned int count)
{
@@ -1765,13 +1765,18 @@ void StringParser::SetParameters(VariableSet& vs, int codeRunning) noexcept
if ((letter != 'P' || codeRunning != 98) && Seen(letter))
{
ExpressionParser parser(gb, &gb.buffer[readPointer], &gb.buffer[commandEnd]);
+ ExpressionValue ev;
try
{
- ExpressionValue ev = parser.Parse();
- char paramName[2] = { letter, 0 };
- vs.Insert(new Variable(paramName, ev, -1));
+ ev = parser.Parse();
+ }
+ catch (const GCodeException&)
+ {
+ //TODO can we report the error anywhere?
+ ev.Set(nullptr);
}
- catch (const GCodeException&) { }
+ char paramName[2] = { letter, 0 };
+ vs.Insert(new Variable(paramName, ev, -1));
}
}
);
diff --git a/src/GCodes/GCodeBuffer/StringParser.h b/src/GCodes/GCodeBuffer/StringParser.h
index 5e744a1f..a4b828fe 100644
--- a/src/GCodes/GCodeBuffer/StringParser.h
+++ b/src/GCodes/GCodeBuffer/StringParser.h
@@ -81,7 +81,7 @@ public:
void PrintCommand(const StringRef& s) const noexcept;
void AppendFullCommand(const StringRef &s) const noexcept;
- void SetParameters(VariableSet& vs, int codeRunning) noexcept;
+ void AddParameters(VariableSet& vs, int codeRunning) noexcept;
GCodeException ConstructParseException(const char *str) const noexcept;
GCodeException ConstructParseException(const char *str, const char *param) const noexcept;
diff --git a/src/GCodes/GCodeException.h b/src/GCodes/GCodeException.h
index 4fb56d85..67f8f6ad 100644
--- a/src/GCodes/GCodeException.h
+++ b/src/GCodes/GCodeException.h
@@ -10,6 +10,12 @@
#include <RepRapFirmware.h>
+namespace StackUsage
+{
+ constexpr uint32_t Throw = 1050; // how much stack we need to throw an exception
+ constexpr uint32_t Margin = 300; // the margin we allow for calls to non-recursive functions that can throw
+}
+
class GCodeException
{
public:
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index 3fa82655..805aa675 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -36,6 +36,7 @@
#include <Platform/Tasks.h>
#include <Tools/Tool.h>
#include <Endstops/ZProbe.h>
+#include <ObjectModel/Variable.h>
#if SUPPORT_LED_STRIPS
# include <Fans/LedStripDriver.h>
@@ -179,7 +180,7 @@ void GCodes::Init() noexcept
{
f = 0.0;
}
- lastDefaultFanSpeed = pausedDefaultFanSpeed = 0.0;
+ lastDefaultFanSpeed = 0.0;
lastAuxStatusReportType = -1; // no status reports requested yet
@@ -998,6 +999,7 @@ void GCodes::DoPause(GCodeBuffer& gb, PauseReason reason, const char *msg, uint1
SaveFanSpeeds();
pauseRestorePoint.toolNumber = reprap.GetCurrentToolNumber();
+ pauseRestorePoint.fanSpeed = lastDefaultFanSpeed;
#if HAS_MASS_STORAGE
if (simulationMode == 0)
@@ -1180,6 +1182,7 @@ bool GCodes::DoEmergencyPause() noexcept
SaveFanSpeeds();
pauseRestorePoint.toolNumber = reprap.GetCurrentToolNumber();
+ pauseRestorePoint.fanSpeed = lastDefaultFanSpeed;
pauseState = PauseState::paused;
return true;
@@ -1295,8 +1298,8 @@ bool GCodes::ReHomeOnStall(DriversBitmap stalledDrivers) noexcept
return false; // can't handle it yet
}
- // Evaluate which machine axes have stalled.
- AxesBitmap machineAxesNotHomed;
+ // Evaluate which machine axes have stalled and create parameters for them
+ VariableSet vars;
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
const AxisDriversConfig& cfg = platform.GetAxisDriversConfig(axis);
@@ -1305,18 +1308,16 @@ bool GCodes::ReHomeOnStall(DriversBitmap stalledDrivers) noexcept
//TODO handle remote stalled drivers
if (cfg.driverNumbers[i].IsLocal() && stalledDrivers.IsBitSet(cfg.driverNumbers[i].localDriver))
{
- machineAxesNotHomed.SetBit(axis);
+ char str[2] = { axisLetters[axis], 0 };
+ vars.Insert(new Variable(str, ExpressionValue((int32_t)1), -1)); // create a parameter with value 1 for the axis
break;
}
}
}
- // Now pass the machine axes to the rehome.g file
- // TODO
-
autoPauseGCode->SetState(GCodeState::resuming1); // set up to resume after rehoming
pauseState = PauseState::resuming;
- DoFileMacro(*autoPauseGCode, REHOME_G, true, AsyncSystemMacroCode); // run the SD card rehome-and-resume script
+ DoFileMacro(*autoPauseGCode, REHOME_G, true, AsyncSystemMacroCode, vars); // run the SD card rehome-and-resume script
return true;
}
@@ -2101,15 +2102,20 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
}
// Apply segmentation if necessary. To speed up simulation on SCARA printers, we don't apply kinematics segmentation when simulating.
- // Note for when we use RTOS: as soon as we set segmentsLeft nonzero, the Move process will assume that the move is ready to take, so this must be the last thing we do.
+ // As soon as we set segmentsLeft nonzero, the Move process will assume that the move is ready to take, so this must be the last thing we do.
const Kinematics& kin = reprap.GetMove().GetKinematics();
- if (kin.UseSegmentation() && simulationMode != 1 && (moveBuffer.hasPositiveExtrusion || moveBuffer.isCoordinated || !kin.UseRawG0()))
+ const SegmentationType st = kin.GetSegmentationType();
+ if (st.useSegmentation && simulationMode != 1 && (moveBuffer.hasPositiveExtrusion || moveBuffer.isCoordinated || st.useG0Segmentation))
{
- // This kinematics approximates linear motion by means of segmentation.
- // We assume that the segments will be smaller than the mesh spacing.
- const float xyLength = fastSqrtf(fsquare(currentUserPosition[X_AXIS] - initialUserPosition[X_AXIS]) + fsquare(currentUserPosition[Y_AXIS] - initialUserPosition[Y_AXIS]));
- const float moveTime = xyLength/moveBuffer.feedRate; // this is a best-case time, often the move will take longer
- moveBuffer.totalSegments = (unsigned int)max<long>(1, lrintf(min<float>(xyLength * kin.GetReciprocalMinSegmentLength(), moveTime * kin.GetSegmentsPerSecond())));
+ // This kinematics approximates linear motion by means of segmentation
+ float moveLengthSquared = fsquare(currentUserPosition[X_AXIS] - initialUserPosition[X_AXIS]) + fsquare(currentUserPosition[Y_AXIS] - initialUserPosition[Y_AXIS]);
+ if (st.useZSegmentation)
+ {
+ moveLengthSquared += fsquare(currentUserPosition[Z_AXIS] - initialUserPosition[Z_AXIS]);
+ }
+ const float moveLength = fastSqrtf(moveLengthSquared);
+ const float moveTime = moveLength/moveBuffer.feedRate; // this is a best-case time, often the move will take longer
+ moveBuffer.totalSegments = (unsigned int)max<long>(1, lrintf(min<float>(moveLength * kin.GetReciprocalMinSegmentLength(), moveTime * kin.GetSegmentsPerSecond())));
}
else
{
@@ -2148,9 +2154,10 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated, const char *& e
// If an error occurs, return true with 'err' assigned
bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
{
+ // The plans are XY, ZX and YZ depending on the G17/G18/G19 setting. We must use ZX instead of XZ to get the correct arc direction.
const unsigned int selectedPlane = gb.LatestMachineState().selectedPlane;
- const unsigned int axis0 = (selectedPlane == 2) ? Y_AXIS : X_AXIS;
- const unsigned int axis1 = (selectedPlane == 0) ? Y_AXIS : Z_AXIS;
+ const unsigned int axis0 = (unsigned int[]){ X_AXIS, Z_AXIS, Y_AXIS }[selectedPlane];
+ const unsigned int axis1 = (axis0 + 1) % 3;
if (moveFractionToSkip > 0.0)
{
@@ -2257,7 +2264,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
}
else
{
- if (gb.Seen('I'))
+ if (gb.Seen((char)('I' + axis0)))
{
iParam = gb.GetDistance();
}
@@ -2266,7 +2273,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
iParam = 0.0;
}
- if (gb.Seen('J'))
+ if (gb.Seen((char)('I' + axis1)))
{
jParam = gb.GetDistance();
}
@@ -2275,9 +2282,9 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise, const char *& err)
jParam = 0.0;
}
- if (iParam == 0.0 && jParam == 0.0) // at least one of IJ must be specified and nonzero
+ if (iParam == 0.0 && jParam == 0.0) // at least one of IJK must be specified and nonzero
{
- err = "G2/G3: no I or J or R parameter";
+ err = "G2/G3: no I J K or R parameter";
return true;
}
}
@@ -2721,6 +2728,17 @@ void GCodes::EmergencyStop() noexcept
#endif
}
+// Simplified version of DoFileMacro, see below
+bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, int codeRunning) noexcept
+{
+ VariableSet vars;
+ if (codeRunning >= 0)
+ {
+ gb.AddParameters(vars, codeRunning);
+ }
+ return DoFileMacro(gb, fileName, reportMissing, codeRunning, vars);
+}
+
// Run a file macro. Prior to calling this, 'state' must be set to the state we want to enter when the macro has been completed.
// Return true if the file was found or it wasn't and we were asked to report that fact.
// 'codeRunning' is the G or M command we are running, or 0 for a tool change file. In particular:
@@ -2728,7 +2746,7 @@ void GCodes::EmergencyStop() noexcept
// 502 = running M502
// 98 = running a macro explicitly via M98
// otherwise it is either the G- or M-code being executed, or ToolChangeMacroCode for a tool change file, or SystemMacroCode for another system file
-bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, int codeRunning) noexcept
+bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, int codeRunning, VariableSet& initialVariables) noexcept
{
#if HAS_LINUX_INTERFACE
if (reprap.UsingLinuxInterface())
@@ -2752,10 +2770,7 @@ bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissi
gb.AbortFile(false, true);
return true;
}
- if (codeRunning >= 0)
- {
- gb.SetParameters(codeRunning);
- }
+ gb.GetVariables().AssignFrom(initialVariables);
gb.StartNewFile();
if (gb.IsMacroEmpty())
{
@@ -2782,10 +2797,7 @@ bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissi
f->Close();
return true;
}
- if (codeRunning >= 0)
- {
- gb.SetParameters(codeRunning);
- }
+ gb.GetVariables().AssignFrom(initialVariables);
gb.LatestMachineState().fileState.Set(f);
gb.StartNewFile();
gb.GetFileInput()->Reset(gb.LatestMachineState().fileState);
@@ -3225,7 +3237,7 @@ void GCodes::StartPrinting(bool fromStart) noexcept
buildObjects.Init();
reprap.GetMove().ResetMoveCounters();
- if (fromStart) // if not resurrecting a print
+ if (fromStart) // if not resurrecting a print
{
fileGCode->LatestMachineState().volumetricExtrusion = false; // default to non-volumetric extrusion
virtualExtruderPosition = 0.0;
@@ -3260,8 +3272,8 @@ void GCodes::StartPrinting(bool fromStart) noexcept
reprap.GetPrintMonitor().GetPrintingFilename());
if (fromStart)
{
- // Get the fileGCode to execute the start macro so that any M82/M83 codes will be executed in the correct context
- DoFileMacro(*fileGCode, START_G, false, AsyncSystemMacroCode);
+ fileGCode->LatestMachineState().selectedPlane = 0; // default G2 and G3 moves to XY plane
+ DoFileMacro(*fileGCode, START_G, false, AsyncSystemMacroCode); // get fileGCode to execute the start macro so that any M82/M83 codes will be executed in the correct context
}
}
@@ -3308,23 +3320,23 @@ GCodeResult GCodes::DoDwell(GCodeBuffer& gb) THROWS(GCodeException)
return (gb.DoDwellTime((uint32_t)dwell)) ? GCodeResult::ok : GCodeResult::notFinished;
}
-// Set offset, working and standby temperatures for a tool. I.e. handle a G10.
+// Set offset, working and standby temperatures for a tool. i.e. handle a G10 or M568.
GCodeResult GCodes::SetOrReportOffsets(GCodeBuffer &gb, const StringRef& reply, int code) THROWS(GCodeException)
{
- int32_t toolNumber = 0;
+ uint32_t toolNumber = 0;
bool seenP = false;
- gb.TryGetIValue('P', toolNumber, seenP);
+ gb.TryGetUIValue('P', toolNumber, seenP);
ReadLockedPointer<Tool> const tool = (seenP) ? reprap.GetTool(toolNumber) : reprap.GetLockedCurrentTool();
if (tool.IsNull())
{
if (seenP)
{
- reply.printf("Attempt to set/report offsets and temperatures for non-existent tool: %" PRIi32, toolNumber);
+ reply.printf("Tool %" PRIu32 " not found", toolNumber);
}
else
{
- reply.printf("Attempt to set/report offsets and temperatures for no selected tool");
+ reply.printf("No tool selected");
}
return GCodeResult::error;
}
@@ -3384,23 +3396,44 @@ GCodeResult GCodes::SetOrReportOffsets(GCodeBuffer &gb, const StringRef& reply,
}
}
- bool settingSpindleRpm = false;
- if (code == 568) // Only M568 can set spindle RPM
+ bool settingOther = false;
+ if (code == 568) // Only M568 can set spindle RPM and change tool heater states
{
+ // Deal with spindle RPM
if (tool->GetSpindleNumber() > -1)
{
if (gb.Seen('F'))
{
- settingSpindleRpm = true;
+ settingOther = true;
if (simulationMode == 0)
{
tool->SetSpindleRpm(gb.GetUIValue());
}
}
}
+
+ // Deal with tool heater states
+ uint32_t newHeaterState;
+ if (gb.TryGetLimitedUIValue('A', newHeaterState, settingOther, 3))
+ {
+ switch (newHeaterState)
+ {
+ case 0: // turn heaters off
+ tool->HeatersToOff();
+ break;
+
+ case 1: // turn heaters to standby, except any that are used by a different active tool
+ tool->HeatersToStandby();
+ break;
+
+ case 2: // set heaters to their active temperatures, except any that are used by a different active tool
+ tool->HeatersToActive();
+ break;
+ }
+ }
}
- if (!settingOffset && !settingTemps && !settingSpindleRpm)
+ if (!settingOffset && !settingTemps && !settingOther)
{
// Print offsets and temperatures
reply.printf("Tool %d offsets:", tool->Number());
@@ -3626,13 +3659,13 @@ bool GCodes::IsMappedFan(unsigned int fanNumber) noexcept
}
// Save the speeds of all fans
+// The speed of the default printing fan (i.e. S parameter of the last M106 command with no P parameter) is no longer included because we save that in a restore point.
void GCodes::SaveFanSpeeds() noexcept
{
for (size_t i = 0; i < MaxFans; ++i)
{
pausedFanSpeeds[i] = reprap.GetFansManager().GetFanValue(i);
}
- pausedDefaultFanSpeed = lastDefaultFanSpeed;
}
// Handle sending a reply back to the appropriate interface(s) and update lastResult
@@ -4203,6 +4236,7 @@ void GCodes::SavePosition(RestorePoint& rp, const GCodeBuffer& gb) const noexcep
rp.virtualExtruderPosition = virtualExtruderPosition;
rp.filePos = gb.GetFilePosition();
rp.toolNumber = reprap.GetCurrentToolNumber();
+ rp.fanSpeed = lastDefaultFanSpeed;
#if SUPPORT_LASER || SUPPORT_IOBITS
rp.laserPwmOrIoBits = moveBuffer.laserPwmOrIoBits;
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index dd714ab0..933aa4b7 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -338,7 +338,8 @@ private:
void StartPrinting(bool fromStart) noexcept; // Start printing the file already selected
void StopPrint(StopPrintReason reason) noexcept; // Stop the current print
- bool DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept; // Get G Codes from a file and print them
+ bool DoFilePrint(GCodeBuffer& gb, const StringRef& reply) noexcept; // Get G Codes from a file and print them
+ bool DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, int codeRunning, VariableSet& initialVariables) noexcept;
bool DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, int codeRunning) noexcept;
// Run a GCode macro file, optionally report error if not found
void FileMacroCyclesReturn(GCodeBuffer& gb) noexcept; // End a macro
@@ -610,8 +611,7 @@ private:
AxesBitmap axesVirtuallyHomed; // same as axesHomed except all bits are set when simulating
float pausedFanSpeeds[MaxFans]; // Fan speeds when the print was paused or a tool change started
- float lastDefaultFanSpeed; // Last speed given in a M106 command with on fan number
- float pausedDefaultFanSpeed; // The speed of the default print cooling fan when the print was paused or a tool change started
+ float lastDefaultFanSpeed; // Last speed given in a M106 command with no fan number
float speedFactor; // speed factor as a fraction (normally 1.0)
float extrusionFactors[MaxExtruders]; // extrusion factors (normally 1.0)
float volumetricExtrusionFactors[MaxExtruders]; // Volumetric extrusion factors
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index 6a295391..ec036535 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -144,7 +144,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
case 0: // Rapid move
case 1: // Ordinary move
- if (moveBuffer.segmentsLeft != 0) // do this check first to avoid locking movement unnecessarily
+ if (moveBuffer.segmentsLeft != 0) // do this check first to avoid locking movement unnecessarily
{
return false;
}
@@ -161,7 +161,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
if (err != nullptr)
{
gb.SetState(GCodeState::abortWhenMovementFinished); // empty the queue before ending simulation, and force the user position to be restored
- gb.LatestMachineState().SetError(err); // must do this *after* calling SetState
+ gb.LatestMachineState().SetError(err); // must do this *after* calling SetState
}
}
break;
@@ -537,98 +537,68 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
break;
case 3: // Spin spindle clockwise
+ case 4: // Spin spindle counter clockwise
+ if (machineType == MachineType::cnc)
{
- if (machineType == MachineType::cnc)
+ // Determine what spindle number we are using
+ Tool * const currentTool = reprap.GetCurrentTool();
+ uint32_t slot;
+ if (gb.Seen('P'))
{
- Tool * const currentTool = reprap.GetCurrentTool();
- if (gb.Seen('S'))
- {
- if (currentTool != nullptr && currentTool->GetSpindleNumber() > -1)
- {
- currentTool->SetSpindleRpm(gb.GetUIValue());
- platform.AccessSpindle(currentTool->GetSpindleNumber()).SetState(SpindleState::forward);
- }
- else
- {
- const uint32_t slot = gb.GetLimitedUIValue('P', MaxSpindles); // Direct spindle speed can only be set when slot is provided as well
- Spindle& spindle = platform.AccessSpindle(slot);
- spindle.SetConfiguredRpm(gb.GetIValue(), false);
- spindle.SetState(SpindleState::forward);
- }
- }
- else if (currentTool != nullptr && currentTool->GetSpindleNumber() > -1)
- {
- platform.AccessSpindle(currentTool->GetSpindleNumber()).SetState(SpindleState::forward);
- }
- else
- {
- reply.copy("No spindle selected via P and no active tool with spindle");
- result = GCodeResult::warning;
- }
+ slot = gb.GetLimitedUIValue('P', MaxSpindles);
}
- else if (gb.Seen('S'))
+ else if (currentTool != nullptr && currentTool->GetSpindleNumber() >= 0)
{
- switch (machineType)
- {
-#if SUPPORT_LASER
- case MachineType::laser:
- if (moveBuffer.segmentsLeft != 0)
- {
- return false; // don't modify moves that haven't gone yet
- }
- moveBuffer.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
- break;
-#endif
-
- default:
-#if SUPPORT_ROLAND
- if (reprap.GetRoland()->Active())
- {
- result = reprap.GetRoland()->ProcessSpindle(gb.GetFValue());
- }
- else
-#endif
- {
- result = GCodeResult::notSupportedInCurrentMode;
- }
- break;
- }
+ slot = currentTool->GetSpindleNumber();
}
else
{
- result = GCodeResult::notSupportedInCurrentMode;
+ reply.copy("No P parameter and no active tool with spindle");
+ result = GCodeResult::error;
+ break;
}
- }
- break;
- case 4: // Spin spindle counter clockwise
- if (machineType == MachineType::cnc)
- {
- Tool * const currentTool = reprap.GetCurrentTool();
+ Spindle& spindle = platform.AccessSpindle(slot);
if (gb.Seen('S'))
{
- if (currentTool != nullptr && currentTool->GetSpindleNumber() > -1)
+ const uint32_t rpm = gb.GetUIValue();
+ if (currentTool != nullptr && currentTool->GetSpindleNumber() == (int)slot)
{
- currentTool->SetSpindleRpm(gb.GetUIValue());
- platform.AccessSpindle(currentTool->GetSpindleNumber()).SetState(SpindleState::reverse);
+ currentTool->SetSpindleRpm(rpm);
}
else
{
- const uint32_t slot = gb.GetLimitedUIValue('P', MaxSpindles); // Direct spindle speed can only be set when slot is provided as well
- Spindle& spindle = platform.AccessSpindle(slot);
- spindle.SetConfiguredRpm(gb.GetIValue(), false);
- spindle.SetState(SpindleState::reverse);
+ spindle.SetConfiguredRpm(rpm, false);
}
}
- else if (currentTool != nullptr && currentTool->GetSpindleNumber() > -1)
- {
- // At this point slot = currentTool->GetSpindleNumber()
- platform.AccessSpindle(currentTool->GetSpindleNumber()).SetState(SpindleState::reverse);
- }
- else
+ spindle.SetState((code == 4) ? SpindleState::reverse : SpindleState::forward);
+ }
+ else if (code == 3 && gb.Seen('S'))
+ {
+ switch (machineType)
{
- reply.copy("No spindle selected via P and no active tool with spindle");
- result = GCodeResult::warning;
+#if SUPPORT_LASER
+ case MachineType::laser:
+ if (moveBuffer.segmentsLeft != 0)
+ {
+ return false; // don't modify moves that haven't gone yet
+ }
+ moveBuffer.laserPwmOrIoBits.laserPwm = ConvertLaserPwm(gb.GetFValue());
+ break;
+#endif
+
+ default:
+#if SUPPORT_ROLAND
+ if (reprap.GetRoland()->Active())
+ {
+ result = reprap.GetRoland()->ProcessSpindle(gb.GetFValue());
+ }
+ else
+#endif
+ {
+ result = GCodeResult::notSupportedInCurrentMode;
+ }
+ break;
}
}
else
@@ -642,29 +612,28 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
{
case MachineType::cnc:
{
+ // Determine what spindle number we are using
+ Tool * const currentTool = reprap.GetCurrentTool();
+ uint32_t slot;
if (gb.Seen('P'))
{
- // Turn off specific spindle
- const uint32_t slot = gb.GetLimitedUIValue('P', MaxSpindles);
- platform.AccessSpindle(slot).SetState(SpindleState::stopped);
+ slot = gb.GetLimitedUIValue('P', MaxSpindles);
+ }
+ else if (currentTool != nullptr && currentTool->GetSpindleNumber() >= 0)
+ {
+ slot = currentTool->GetSpindleNumber();
}
else
{
- Tool * const currentTool = reprap.GetCurrentTool();
- if (currentTool != nullptr && currentTool->GetSpindleNumber() > -1) // Turn off spindle of current tool
+ // Turn off every spindle if no 'P' parameter is present and the current tool does not have a spindle
+ for (size_t i = 0; i < MaxSpindles; i++)
{
- platform.AccessSpindle(currentTool->GetSpindleNumber()).SetState(SpindleState::stopped);
- }
- else
- {
- // Turn off every spindle if no 'P' parameter is present and the current tool
- // does not have a spindle
- for (size_t i = 0; i < MaxSpindles; i++)
- {
- platform.AccessSpindle(i).SetState(SpindleState::stopped);
- }
+ platform.AccessSpindle(i).SetState(SpindleState::stopped);
}
+ break;
}
+
+ platform.AccessSpindle(slot).SetState(SpindleState::stopped);
}
break;
@@ -1471,7 +1440,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
}
else
{
- SetMappedFanSpeed(pausedDefaultFanSpeed);
+ const size_t restorePointNumber = gb.GetLimitedUIValue('R', NumRestorePoints);
+ SetMappedFanSpeed(numberedRestorePoints[restorePointNumber].fanSpeed);
}
}
}
@@ -2858,8 +2828,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeEx
String<StringLength100> key;
String<StringLength20> flags;
bool dummy;
- gb.TryGetQuotedString('K', key.GetRef(), dummy);
- gb.TryGetQuotedString('F', flags.GetRef(), dummy);
+ gb.TryGetQuotedString('K', key.GetRef(), dummy, true);
+ gb.TryGetQuotedString('F', flags.GetRef(), dummy, true);
if (&gb == auxGCode)
{
lastAuxStatusReportType = ObjectModelAuxStatusReportType;
diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp
index 0db31705..3ff14db0 100644
--- a/src/GCodes/GCodes3.cpp
+++ b/src/GCodes/GCodes3.cpp
@@ -44,16 +44,11 @@
// Deal with G60
GCodeResult GCodes::SavePosition(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
- const uint32_t sParam = (gb.Seen('S')) ? gb.GetUIValue() : 0;
- if (sParam < ARRAY_SIZE(numberedRestorePoints))
- {
- SavePosition(numberedRestorePoints[sParam], gb);
- numberedRestorePoints[sParam].toolNumber = reprap.GetCurrentToolNumber();
- return GCodeResult::ok;
- }
-
- reply.copy("Bad restore point number");
- return GCodeResult::error;
+ uint32_t sParam = 0;
+ bool dummySeen;
+ gb.TryGetLimitedUIValue('S', sParam, dummySeen, NumRestorePoints);
+ SavePosition(numberedRestorePoints[sParam], gb);
+ return GCodeResult::ok;
}
// This handles G92. Return true if completed, false if it needs to be called again.
diff --git a/src/GCodes/GCodes4.cpp b/src/GCodes/GCodes4.cpp
index c3d2bbbb..244a3a7d 100644
--- a/src/GCodes/GCodes4.cpp
+++ b/src/GCodes/GCodes4.cpp
@@ -310,8 +310,9 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::toolChange0: // run tfree for the old tool (if any)
case GCodeState::m109ToolChange0: // run tfree for the old tool (if any)
doingToolChange = true;
- SaveFanSpeeds();
SavePosition(toolChangeRestorePoint, gb);
+ toolChangeRestorePoint.toolNumber = reprap.GetCurrentToolNumber();
+ toolChangeRestorePoint.fanSpeed = lastDefaultFanSpeed;
reprap.SetPreviousToolNumber();
gb.AdvanceState();
@@ -529,10 +530,8 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
case GCodeState::resuming3:
if (LockMovementAndWaitForStandstill(gb))
{
- for (size_t i = 0; i < MaxFans; ++i)
- {
- reprap.GetFansManager().SetFanValue(i, pausedFanSpeeds[i]);
- }
+ // We no longer restore the paused fan speeds automatically on resuming, because that messes up the print cooling fan speed if a tool change has been done
+ // They can be restored manually in resume.g if required
virtualExtruderPosition = pauseRestorePoint.virtualExtruderPosition; // reset the extruder position in case we are receiving absolute extruder moves
moveBuffer.virtualExtruderPosition = pauseRestorePoint.virtualExtruderPosition;
fileGCode->LatestMachineState().feedRate = pauseRestorePoint.feedRate;
@@ -554,8 +553,12 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
{
bool updating = false;
String<MaxFilenameLength> filenameString;
- bool dummy;
- gb.TryGetQuotedString('P', filenameString.GetRef(), dummy);
+ try
+ {
+ bool dummy;
+ gb.TryGetQuotedString('P', filenameString.GetRef(), dummy);
+ }
+ catch (const GCodeException&) { }
for (unsigned int module = 1; module < NumFirmwareUpdateModules; ++module)
{
if (firmwareUpdateModuleMap.IsBitSet(module))
@@ -594,10 +597,14 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept
// Update main firmware
firmwareUpdateModuleMap.Clear();
String<MaxFilenameLength> filenameString;
- bool dummy;
- gb.TryGetQuotedString('P', filenameString.GetRef(), dummy);
- reprap.UpdateFirmware(filenameString.GetRef());
- // The above call does not return unless an error occurred
+ try
+ {
+ bool dummy;
+ gb.TryGetQuotedString('P', filenameString.GetRef(), dummy);
+ reprap.UpdateFirmware(filenameString.GetRef());
+ // The above call does not return unless an error occurred
+ }
+ catch (const GCodeException&) { }
}
isFlashing = false;
gb.SetState(GCodeState::normal);
diff --git a/src/GCodes/RestorePoint.cpp b/src/GCodes/RestorePoint.cpp
index 7ba04a31..edbfbf71 100644
--- a/src/GCodes/RestorePoint.cpp
+++ b/src/GCodes/RestorePoint.cpp
@@ -33,6 +33,7 @@ constexpr ObjectModelTableEntry RestorePoint::objectModelTable[] =
// 0. LaserFilamentMonitor members
{ "coords", OBJECT_MODEL_FUNC_NOSELF(&coordinatesArrayDescriptor), ObjectModelEntryFlags::none },
{ "extruderPos", OBJECT_MODEL_FUNC(self->virtualExtruderPosition, 1), ObjectModelEntryFlags::none },
+ { "fanPwm", OBJECT_MODEL_FUNC(self->fanSpeed, 2), ObjectModelEntryFlags::none },
{ "feedRate", OBJECT_MODEL_FUNC(self->feedRate, 1), ObjectModelEntryFlags::none },
#if SUPPORT_IOBITS
{ "ioBits", OBJECT_MODEL_FUNC_IF(reprap.GetGCodes().GetMachineType() != MachineType::laser,
@@ -45,7 +46,7 @@ constexpr ObjectModelTableEntry RestorePoint::objectModelTable[] =
{ "toolNumber", OBJECT_MODEL_FUNC((int32_t)self->toolNumber), ObjectModelEntryFlags::none },
};
-constexpr uint8_t RestorePoint::objectModelTableDescriptor[] = { 1, 4 + SUPPORT_LASER + SUPPORT_IOBITS };
+constexpr uint8_t RestorePoint::objectModelTableDescriptor[] = { 1, 5 + SUPPORT_LASER + SUPPORT_IOBITS };
DEFINE_GET_OBJECT_MODEL_TABLE(RestorePoint)
@@ -69,6 +70,7 @@ void RestorePoint::Init() noexcept
proportionDone = 0.0;
initialUserC0 = initialUserC1 = 0.0;
toolNumber = -1;
+ fanSpeed = 0.0;
#if SUPPORT_LASER || SUPPORT_IOBITS
laserPwmOrIoBits.Clear();
diff --git a/src/GCodes/RestorePoint.h b/src/GCodes/RestorePoint.h
index ee2a6742..5b581691 100644
--- a/src/GCodes/RestorePoint.h
+++ b/src/GCodes/RestorePoint.h
@@ -21,6 +21,7 @@ public:
FilePosition filePos; // The file position that this move was read from
float initialUserC0, initialUserC1; // If we paused during an arc move and proportionDone is nonzero, the X and Y user coordinates at the start of the move
int toolNumber; // The tool number that was active
+ float fanSpeed; // the last fan speed that was set by M106 with no P parameter
#if SUPPORT_LASER || SUPPORT_IOBITS
LaserPwmOrIoBits laserPwmOrIoBits; // The output port bits setting for this move, or the laser power
@@ -32,7 +33,6 @@ public:
protected:
DECLARE_OBJECT_MODEL
OBJECT_MODEL_ARRAY(coordinates)
- OBJECT_MODEL_ARRAY(spindleSpeeds)
};
#endif /* SRC_GCODES_RESTOREPOINT_H_ */
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index e71c1f19..ad7f22d4 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -932,7 +932,7 @@ GCodeResult Heat::ConfigureSensor(GCodeBuffer& gb, const StringRef& reply) THROW
if (gb.Seen('P'))
{
- String<StringLength50> portName; // StringLength20 is too short for "thermocouple-max31856"
+ String<StringLength50> portName;
gb.GetReducedString(portName.GetRef());
#if SUPPORT_CAN_EXPANSION
boardAddress = IoPort::RemoveBoardAddress(portName.GetRef());
@@ -958,13 +958,13 @@ GCodeResult Heat::ConfigureSensor(GCodeBuffer& gb, const StringRef& reply) THROW
DeleteSensor(sensorNum);
- String<StringLength20> typeName;
+ String<StringLength50> typeName; // StringLength20 is too short for "thermocouple-max31856"
gb.GetReducedString(typeName.GetRef());
#if SUPPORT_CAN_EXPANSION
if (boardAddress == CanId::NoAddress)
{
- boardAddress = CanInterface::GetCanAddress(); // no port name was given, so default to local
+ boardAddress = CanInterface::GetCanAddress(); // no port name was given, so default to local
}
TemperatureSensor * const newSensor = TemperatureSensor::Create(sensorNum, boardAddress, typeName.c_str(), reply);
#else
diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp
index 670e0b98..fd3742bd 100644
--- a/src/Heating/Heater.cpp
+++ b/src/Heating/Heater.cpp
@@ -440,11 +440,13 @@ GCodeResult Heater::SetFaultDetectionParameters(float pMaxTempExcursion, float p
GCodeResult Heater::ConfigureMonitor(GCodeBuffer &gb, const StringRef &reply) THROWS(GCodeException)
{
// Get any parameters that have been provided
- const bool seenP = gb.Seen('P');
- const size_t index = (seenP) ? gb.GetLimitedUIValue('P', MaxMonitorsPerHeater) : 0;
+ uint32_t index = 0;
+ bool seenP = false;
+ gb.TryGetLimitedUIValue('P', index, seenP, MaxMonitorsPerHeater);
- const bool seenSensor = gb.Seen('T');
- const int monitoringSensor = (seenSensor) ? gb.GetLimitedUIValue('T', MaxSensors) : GetSensorNumber();
+ bool seenSensor = false;
+ uint32_t monitoringSensor = GetSensorNumber();
+ gb.TryGetLimitedUIValue('T', monitoringSensor, seenSensor, MaxSensors);
const bool seenAction = gb.Seen('A');
const HeaterMonitorAction action = (seenAction)
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index e4e21d02..0f358034 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -1261,7 +1261,7 @@ void DDA::Prepare(uint8_t simMode, float extrusionPending[]) noexcept
{
flags.wasAccelOnlyMove = IsAccelerationMove(); // save this for the next move to look at
- if (flags.xyMoving && reprap.GetMove().GetShaper().GetType() == InputShaperType::DAA)
+ if (flags.xyMoving && reprap.GetMove().GetShaper().GetType() == InputShaperType::daa)
{
AdjustAcceleration();
}
diff --git a/src/Movement/DDARing.cpp b/src/Movement/DDARing.cpp
index 0b7e10e2..cb765e53 100644
--- a/src/Movement/DDARing.cpp
+++ b/src/Movement/DDARing.cpp
@@ -113,7 +113,7 @@ void DDARing::Exit() noexcept
}
}
-GCodeResult DDARing::ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) noexcept
+GCodeResult DDARing::ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
bool seen = false;
uint32_t numDdasWanted = 0, numDMsWanted = 0;
diff --git a/src/Movement/DDARing.h b/src/Movement/DDARing.h
index be3623d0..359baeb6 100644
--- a/src/Movement/DDARing.h
+++ b/src/Movement/DDARing.h
@@ -84,7 +84,7 @@ public:
bool SetWaitingToEmpty() noexcept;
- GCodeResult ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) noexcept;
+ GCodeResult ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException);
#if SUPPORT_REMOTE_COMMANDS
void AddMoveFromRemote(const CanMessageMovementLinear& msg) noexcept; // add a move from the ATE to the movement queue
diff --git a/src/Movement/InputShaper.cpp b/src/Movement/InputShaper.cpp
index 4caf84da..cc4d5f9a 100644
--- a/src/Movement/InputShaper.cpp
+++ b/src/Movement/InputShaper.cpp
@@ -65,13 +65,21 @@ GCodeResult InputShaper::Configure(GCodeBuffer& gb, const StringRef& reply) THRO
if (gb.Seen('P'))
{
+ String<StringLength20> shaperName;
+ gb.GetReducedString(shaperName.GetRef());
+ const InputShaperType newType(shaperName.c_str());
+ if (!newType.IsValid())
+ {
+ reply.printf("Unsupported input shaper type '%s'", shaperName.c_str());
+ return GCodeResult::error;
+ }
seen = true;
- type = (InputShaperType)gb.GetLimitedUIValue('P', InputShaperType::NumValues);
+ type = newType;
}
else if (seen && type == InputShaperType::none)
{
// For backwards compatibility, if we have set input shaping parameters but not defined shaping type, default to DAA for now. Change this when we support better types of input shaping.
- type = InputShaperType::DAA;
+ type = InputShaperType::daa;
}
if (seen)
@@ -80,7 +88,7 @@ GCodeResult InputShaper::Configure(GCodeBuffer& gb, const StringRef& reply) THRO
}
else if (type != InputShaperType::none)
{
- reply.printf("%s input shaping at %.1fHz damping factor %.2f, min. acceleration %.1f",
+ reply.printf("Input shaping '%s' at %.1fHz damping factor %.2f, min. acceleration %.1f",
type.ToString(), (double)GetFrequency(), (double)GetFloatDamping(), (double)minimumAcceleration);
}
else
diff --git a/src/Movement/InputShaper.h b/src/Movement/InputShaper.h
index 4ed73304..068c0a24 100644
--- a/src/Movement/InputShaper.h
+++ b/src/Movement/InputShaper.h
@@ -12,12 +12,10 @@
#include <General/NamedEnum.h>
#include <ObjectModel/ObjectModel.h>
+// These names must be in alphabetical order and lowercase
NamedEnum(InputShaperType, uint8_t,
+ daa,
none,
- ZVD,
- ZVDD,
- EI2,
- DAA
);
class InputShaper INHERIT_OBJECT_MODEL
diff --git a/src/Movement/Kinematics/FiveBarScaraKinematics.cpp b/src/Movement/Kinematics/FiveBarScaraKinematics.cpp
index 8fd10f2d..83c9b3ae 100644
--- a/src/Movement/Kinematics/FiveBarScaraKinematics.cpp
+++ b/src/Movement/Kinematics/FiveBarScaraKinematics.cpp
@@ -42,7 +42,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(FiveBarScaraKinematics, ZLeadscrewKine
#endif
FiveBarScaraKinematics::FiveBarScaraKinematics() noexcept
- : ZLeadscrewKinematics(KinematicsType::scara, true, true)
+ : ZLeadscrewKinematics(KinematicsType::scara, SegmentationType(true, false, false))
{
Recalc();
}
diff --git a/src/Movement/Kinematics/HangprinterKinematics.cpp b/src/Movement/Kinematics/HangprinterKinematics.cpp
index cf8e8d90..3d5c65aa 100644
--- a/src/Movement/Kinematics/HangprinterKinematics.cpp
+++ b/src/Movement/Kinematics/HangprinterKinematics.cpp
@@ -60,7 +60,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE(HangprinterKinematics)
// Constructor
HangprinterKinematics::HangprinterKinematics() noexcept
- : RoundBedKinematics(KinematicsType::scara, true, true)
+ : RoundBedKinematics(KinematicsType::hangprinter, SegmentationType(true, true, true))
{
Init();
}
@@ -226,7 +226,7 @@ bool HangprinterKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
(double)anchors[C_AXIS][X_AXIS], (double)anchors[C_AXIS][Y_AXIS], (double)anchors[C_AXIS][Z_AXIS],
(double)anchors[D_AXIS][X_AXIS], (double)anchors[D_AXIS][Y_AXIS], (double)anchors[D_AXIS][Z_AXIS],
(double)printRadius,
- (int)segmentsPerSecond, (double)minSegmentLength
+ (int)GetSegmentsPerSecond(), (double)GetMinSegmentLength()
);
}
}
diff --git a/src/Movement/Kinematics/Kinematics.cpp b/src/Movement/Kinematics/Kinematics.cpp
index 1a0c7dc1..850308b5 100644
--- a/src/Movement/Kinematics/Kinematics.cpp
+++ b/src/Movement/Kinematics/Kinematics.cpp
@@ -23,8 +23,9 @@
const char * const Kinematics::HomeAllFileName = "homeall.g";
// Constructor. Pass segsPerSecond <= 0.0 to get non-segmented kinematics.
-Kinematics::Kinematics(KinematicsType t, bool doUseSegmentation, bool doUseRawG0) noexcept
- : segmentsPerSecond(DefaultSegmentsPerSecond), minSegmentLength(DefaultMinSegmentLength), useSegmentation(doUseSegmentation), useRawG0(doUseRawG0), type(t)
+Kinematics::Kinematics(KinematicsType t, SegmentationType segType) noexcept
+ : segmentsPerSecond(DefaultSegmentsPerSecond), minSegmentLength(DefaultMinSegmentLength), reciprocalMinSegmentLength(1.0/DefaultMinSegmentLength),
+ segmentationType(segType), type(t)
{
}
@@ -55,8 +56,8 @@ bool Kinematics::TryConfigureSegmentation(GCodeBuffer& gb) noexcept
gb.TryGetFValue('T', minSegmentLength, seen);
if (seen)
{
- useSegmentation = minSegmentLength > 0.0 && segmentsPerSecond > 0.0;
- if (useSegmentation)
+ segmentationType.useSegmentation = minSegmentLength > 0.0 && segmentsPerSecond > 0.0;
+ if (segmentationType.useSegmentation)
{
reciprocalMinSegmentLength = 1.0 / minSegmentLength;
}
diff --git a/src/Movement/Kinematics/Kinematics.h b/src/Movement/Kinematics/Kinematics.h
index 13967567..addd6098 100644
--- a/src/Movement/Kinematics/Kinematics.h
+++ b/src/Movement/Kinematics/Kinematics.h
@@ -63,6 +63,19 @@ enum class LimitPositionResult : uint8_t
adjustedAndIntermediateUnreachable // we adjusted the final position to make it reachable, but intermediate positions are still urreachable
};
+struct SegmentationType
+{
+ uint8_t useSegmentation : 1,
+ useZSegmentation : 1,
+ useG0Segmentation : 1,
+ zero : 5;
+
+ constexpr SegmentationType(bool useSeg, bool useZSeg, bool useG0Seg) noexcept
+ : useSegmentation(useSeg), useZSegmentation(useZSeg), useG0Segmentation(useG0Seg), zero(0)
+ {
+ }
+};
+
class Kinematics INHERIT_OBJECT_MODEL
{
public:
@@ -204,8 +217,7 @@ public:
// Functions that return information held in this base class
KinematicsType GetKinematicsType() const noexcept { return type; }
- bool UseSegmentation() const noexcept { return useSegmentation; }
- bool UseRawG0() const noexcept { return useRawG0; }
+ SegmentationType GetSegmentationType() const noexcept { return segmentationType; }
float GetSegmentsPerSecond() const noexcept pre(UseSegmentation()) { return segmentsPerSecond; }
float GetMinSegmentLength() const noexcept pre(UseSegmentation()) { return minSegmentLength; }
float GetReciprocalMinSegmentLength() const noexcept pre(UseSegmentation()) { return reciprocalMinSegmentLength; }
@@ -213,8 +225,7 @@ public:
protected:
DECLARE_OBJECT_MODEL_VIRTUAL
- // Constructor. Pass segsPerSecond <= 0.0 to get non-segmented motion.
- Kinematics(KinematicsType t, bool doUseSegmentation, bool doUseRawG0) noexcept;
+ Kinematics(KinematicsType t, SegmentationType segType) noexcept;
// Apply the M208 limits to the Cartesian position that the user wants to move to for all axes from the specified one upwards
// Return true if any coordinates were changed
@@ -229,10 +240,6 @@ protected:
static void PrintVector(const char *s, const float *v, size_t numElems) noexcept;
static void PrintVector(const char *s, const double *v, size_t numElems) noexcept;
- float segmentsPerSecond; // if we are using segmentation, the target number of segments/second
- float minSegmentLength; // if we are using segmentation, the minimum segment size
- float reciprocalMinSegmentLength; // if we are using segmentation, the reciprocal of minimum segment size
-
static const char * const HomeAllFileName;
private:
@@ -240,8 +247,11 @@ private:
static constexpr float DefaultSegmentsPerSecond = 100.0;
static constexpr float DefaultMinSegmentLength = 0.2;
- bool useSegmentation; // true if we have to approximate linear movement using segmentation
- bool useRawG0; // true if we normally use segmentation but we do not need to segment travel moves
+ float segmentsPerSecond; // if we are using segmentation, the target number of segments/second
+ float minSegmentLength; // if we are using segmentation, the minimum segment size
+ float reciprocalMinSegmentLength; // if we are using segmentation, the reciprocal of minimum segment size
+
+ SegmentationType segmentationType; // the type of segmentation we are using
KinematicsType type;
};
diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.cpp b/src/Movement/Kinematics/LinearDeltaKinematics.cpp
index f18c804b..7328294c 100644
--- a/src/Movement/Kinematics/LinearDeltaKinematics.cpp
+++ b/src/Movement/Kinematics/LinearDeltaKinematics.cpp
@@ -55,7 +55,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE(LinearDeltaKinematics)
#endif
-LinearDeltaKinematics::LinearDeltaKinematics() noexcept : RoundBedKinematics(KinematicsType::linearDelta, false, true), numTowers(UsualNumTowers)
+LinearDeltaKinematics::LinearDeltaKinematics() noexcept : RoundBedKinematics(KinematicsType::linearDelta, SegmentationType(false, false, false)), numTowers(UsualNumTowers)
{
Init();
}
diff --git a/src/Movement/Kinematics/PolarKinematics.cpp b/src/Movement/Kinematics/PolarKinematics.cpp
index 6716baa1..79935f35 100644
--- a/src/Movement/Kinematics/PolarKinematics.cpp
+++ b/src/Movement/Kinematics/PolarKinematics.cpp
@@ -37,7 +37,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE(PolarKinematics)
// Constructor
PolarKinematics::PolarKinematics() noexcept
- : Kinematics(KinematicsType::polar, true, true),
+ : Kinematics(KinematicsType::polar, SegmentationType(true, false, false)),
minRadius(0.0), maxRadius(DefaultMaxRadius), homedRadius(0.0),
maxTurntableSpeed(DefaultMaxTurntableSpeed), maxTurntableAcceleration(DefaultMaxTurntableAcceleration)
{
@@ -98,7 +98,7 @@ bool PolarKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const Strin
{
reply.printf("Kinematics is Polar with radius %.1f to %.1fmm, homed radius %.1fmm, segments/sec %d, min. segment length %.2f",
(double)minRadius, (double)maxRadius, (double)homedRadius,
- (int)segmentsPerSecond, (double)minSegmentLength);
+ (int)GetSegmentsPerSecond(), (double)GetMinSegmentLength());
}
return seen;
}
diff --git a/src/Movement/Kinematics/RotaryDeltaKinematics.cpp b/src/Movement/Kinematics/RotaryDeltaKinematics.cpp
index 0477e53e..53d15d47 100644
--- a/src/Movement/Kinematics/RotaryDeltaKinematics.cpp
+++ b/src/Movement/Kinematics/RotaryDeltaKinematics.cpp
@@ -38,7 +38,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE(RotaryDeltaKinematics)
#endif
// Constructor
-RotaryDeltaKinematics::RotaryDeltaKinematics() noexcept : RoundBedKinematics(KinematicsType::rotaryDelta, true, false)
+RotaryDeltaKinematics::RotaryDeltaKinematics() noexcept : RoundBedKinematics(KinematicsType::rotaryDelta, SegmentationType(true, true, true))
{
Init();
}
diff --git a/src/Movement/Kinematics/RoundBedKinematics.cpp b/src/Movement/Kinematics/RoundBedKinematics.cpp
index 82aad6b9..c35d68e3 100644
--- a/src/Movement/Kinematics/RoundBedKinematics.cpp
+++ b/src/Movement/Kinematics/RoundBedKinematics.cpp
@@ -9,8 +9,8 @@
#include <Platform/RepRap.h>
#include <Platform/Platform.h>
-RoundBedKinematics::RoundBedKinematics(KinematicsType t, bool doUseSegmentation, bool doUseRawG0) noexcept
-: Kinematics(t, doUseSegmentation, doUseRawG0), printRadiusSquared(0.0)
+RoundBedKinematics::RoundBedKinematics(KinematicsType t, SegmentationType segType) noexcept
+ : Kinematics(t, segType), printRadiusSquared(0.0)
{
}
diff --git a/src/Movement/Kinematics/RoundBedKinematics.h b/src/Movement/Kinematics/RoundBedKinematics.h
index b69d56a1..29db1b7e 100644
--- a/src/Movement/Kinematics/RoundBedKinematics.h
+++ b/src/Movement/Kinematics/RoundBedKinematics.h
@@ -19,7 +19,7 @@ public:
void LimitSpeedAndAcceleration(DDA& dda, const float *normalisedDirectionVector, size_t numVisibleAxes, bool continuousRotationShortcut) const noexcept override;
AxesBitmap GetLinearAxes() const noexcept override;
protected:
- RoundBedKinematics(KinematicsType t, bool doUseSegmentation, bool doUseRawG0) noexcept;
+ RoundBedKinematics(KinematicsType t, SegmentationType segType) noexcept;
float printRadiusSquared;
};
diff --git a/src/Movement/Kinematics/ScaraKinematics.cpp b/src/Movement/Kinematics/ScaraKinematics.cpp
index 63d62347..29b9650d 100644
--- a/src/Movement/Kinematics/ScaraKinematics.cpp
+++ b/src/Movement/Kinematics/ScaraKinematics.cpp
@@ -37,7 +37,7 @@ DEFINE_GET_OBJECT_MODEL_TABLE_WITH_PARENT(ScaraKinematics, ZLeadscrewKinematics)
#endif
ScaraKinematics::ScaraKinematics() noexcept
- : ZLeadscrewKinematics(KinematicsType::scara, true, true),
+ : ZLeadscrewKinematics(KinematicsType::scara, SegmentationType(true, false, false)),
proximalArmLength(DefaultProximalArmLength), distalArmLength(DefaultDistalArmLength), xOffset(0.0), yOffset(0.0)
{
thetaLimits[0] = DefaultMinTheta;
@@ -229,7 +229,7 @@ bool ScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const Strin
(double)distalArmLength, (double)psiLimits[0], (double)psiLimits[1], (supportsContinuousRotation[0]) ? " (continuous)" : "",
(double)crosstalk[0], (double)crosstalk[1], (double)crosstalk[2],
(double)xOffset, (double)yOffset,
- (int)segmentsPerSecond, (double)minSegmentLength);
+ (int)GetSegmentsPerSecond(), (double)GetMinSegmentLength());
}
return seen;
}
diff --git a/src/Movement/Kinematics/ZLeadscrewKinematics.cpp b/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
index 2880c999..61293b5c 100644
--- a/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
+++ b/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
@@ -69,12 +69,12 @@ DEFINE_GET_OBJECT_MODEL_TABLE(ZLeadscrewKinematics)
#endif
ZLeadscrewKinematics::ZLeadscrewKinematics(KinematicsType k) noexcept
- : Kinematics(k, false, true), numLeadscrews(0), correctionFactor(1.0), maxCorrection(1.0), screwPitch(M3ScrewPitch)
+ : Kinematics(k, SegmentationType(false, false, false)), numLeadscrews(0), correctionFactor(1.0), maxCorrection(1.0), screwPitch(M3ScrewPitch)
{
}
-ZLeadscrewKinematics::ZLeadscrewKinematics(KinematicsType k, bool doUseSegmentation, bool doUseRawG0) noexcept
- : Kinematics(k, doUseSegmentation, doUseRawG0), numLeadscrews(0), correctionFactor(1.0), maxCorrection(1.0), screwPitch(M3ScrewPitch)
+ZLeadscrewKinematics::ZLeadscrewKinematics(KinematicsType k, SegmentationType segType) noexcept
+ : Kinematics(k, segType), numLeadscrews(0), correctionFactor(1.0), maxCorrection(1.0), screwPitch(M3ScrewPitch)
{
}
diff --git a/src/Movement/Kinematics/ZLeadscrewKinematics.h b/src/Movement/Kinematics/ZLeadscrewKinematics.h
index 6f096bcc..5c8d0834 100644
--- a/src/Movement/Kinematics/ZLeadscrewKinematics.h
+++ b/src/Movement/Kinematics/ZLeadscrewKinematics.h
@@ -15,7 +15,7 @@ class ZLeadscrewKinematics : public Kinematics
{
public:
ZLeadscrewKinematics(KinematicsType k) noexcept;
- ZLeadscrewKinematics(KinematicsType t, bool doUseSegmentation, bool doUseRawG0) noexcept;
+ ZLeadscrewKinematics(KinematicsType t, SegmentationType segType) noexcept;
bool Configure(unsigned int mCode, GCodeBuffer& gb, const StringRef& reply, bool& error) THROWS(GCodeException) override;
bool SupportsAutoCalibration() const noexcept override;
@@ -33,7 +33,7 @@ protected:
private:
void AppendCorrections(const floatc_t corrections[], const StringRef& reply) const noexcept;
- static const unsigned int MaxLeadscrews = 8; // some Folgertech FT5 printers have 8 bed adjusting screws
+ static const unsigned int MaxLeadscrews = 4; // the maximum we support for auto bed levelling
unsigned int numLeadscrews;
float leadscrewX[MaxLeadscrews], leadscrewY[MaxLeadscrews];
diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp
index ee139290..2feadeb5 100644
--- a/src/Movement/Move.cpp
+++ b/src/Movement/Move.cpp
@@ -624,6 +624,37 @@ void Move::InverseAxisTransform(float xyzPoint[MaxAxes], const Tool *tool) const
}
}
+// Compute the height correction needed at a point, ignoring taper
+float Move::ComputeHeightCorrection(float xyzPoint[MaxAxes], const Tool *tool) const noexcept
+{
+ float zCorrection = 0.0;
+ unsigned int numCorrections = 0;
+ const GridDefinition& grid = GetGrid();
+ const AxesBitmap axis1Axes = Tool::GetAxisMapping(tool, grid.GetAxisNumber(1));
+
+ // Transform the Z coordinate based on the average correction for each axis used as an X or Y axis.
+ Tool::GetAxisMapping(tool, grid.GetAxisNumber(0))
+ .Iterate([this, xyzPoint, tool, axis1Axes, &zCorrection, &numCorrections](unsigned int axis0Axis, unsigned int)
+ {
+ const float axis0Coord = xyzPoint[axis0Axis] + Tool::GetOffset(tool, axis0Axis);
+ axis1Axes.Iterate([this, xyzPoint, tool, axis0Coord, &zCorrection, &numCorrections](unsigned int axis1Axis, unsigned int)
+ {
+ const float axis1Coord = xyzPoint[axis1Axis] + Tool::GetOffset(tool, axis1Axis);
+ zCorrection += heightMap.GetInterpolatedHeightError(axis0Coord, axis1Coord);
+ ++numCorrections;
+ }
+ );
+ }
+ );
+
+ if (numCorrections > 1)
+ {
+ zCorrection /= numCorrections; // take an average
+ }
+
+ return zCorrection + zShift;
+}
+
// Do the bed transform AFTER the axis transform
void Move::BedTransform(float xyzPoint[MaxAxes], const Tool *tool) const noexcept
{
@@ -632,32 +663,7 @@ void Move::BedTransform(float xyzPoint[MaxAxes], const Tool *tool) const noexcep
const float toolHeight = xyzPoint[Z_AXIS] + Tool::GetOffset(tool, Z_AXIS);
if (!useTaper || toolHeight < taperHeight)
{
- float zCorrection = 0.0;
- unsigned int numCorrections = 0;
- const GridDefinition& grid = GetGrid();
- const AxesBitmap axis1Axes = Tool::GetAxisMapping(tool, grid.GetAxisNumber(1));
-
- // Transform the Z coordinate based on the average correction for each axis used as an X or Y axis.
- Tool::GetAxisMapping(tool, grid.GetAxisNumber(0))
- .Iterate([this, xyzPoint, tool, axis1Axes, &zCorrection, &numCorrections](unsigned int axis0Axis, unsigned int)
- {
- const float axis0Coord = xyzPoint[axis0Axis] + Tool::GetOffset(tool, axis0Axis);
- axis1Axes.Iterate([this, xyzPoint, tool, axis0Coord, &zCorrection, &numCorrections](unsigned int axis1Axis, unsigned int)
- {
- const float axis1Coord = xyzPoint[axis1Axis] + Tool::GetOffset(tool, axis1Axis);
- zCorrection += heightMap.GetInterpolatedHeightError(axis0Coord, axis1Coord);
- ++numCorrections;
- }
- );
- }
- );
-
- if (numCorrections > 1)
- {
- zCorrection /= numCorrections; // take an average
- }
-
- zCorrection += zShift;
+ const float zCorrection = ComputeHeightCorrection(xyzPoint, tool);
xyzPoint[Z_AXIS] += (useTaper && zCorrection < taperHeight) ? (taperHeight - toolHeight) * recipTaperHeight * zCorrection : zCorrection;
}
}
@@ -668,33 +674,7 @@ void Move::InverseBedTransform(float xyzPoint[MaxAxes], const Tool *tool) const
{
if (usingMesh)
{
- float zCorrection = 0.0;
- unsigned int numCorrections = 0;
- const GridDefinition& grid = GetGrid();
- const AxesBitmap axis1Axes = Tool::GetAxisMapping(tool, grid.GetAxisNumber(1));
-
- // Transform the Z coordinate based on the average correction for each axis used as an X or Y axis.
- Tool::GetAxisMapping(tool, grid.GetAxisNumber(0))
- .Iterate([this, xyzPoint, tool, axis1Axes, &zCorrection, &numCorrections](unsigned int axis0Axis, unsigned int)
- {
- const float axis0Coord = xyzPoint[axis0Axis] + Tool::GetOffset(tool, axis0Axis);
- axis1Axes.Iterate([this, xyzPoint, tool, axis0Coord, &zCorrection, &numCorrections](unsigned int axis1Axis, unsigned int)
- {
- const float axis1Coord = xyzPoint[axis1Axis] + Tool::GetOffset(tool, axis1Axis);
- zCorrection += heightMap.GetInterpolatedHeightError(axis0Coord, axis1Coord);
- ++numCorrections;
- }
- );
- }
- );
-
- if (numCorrections > 1)
- {
- zCorrection /= numCorrections; // take an average
- }
-
- zCorrection += zShift;
-
+ const float zCorrection = ComputeHeightCorrection(xyzPoint, tool);
if (!useTaper || zCorrection >= taperHeight) // need check on zCorrection to avoid possible divide by zero
{
xyzPoint[Z_AXIS] -= zCorrection;
@@ -989,7 +969,7 @@ bool Move::WriteResumeSettings(FileStore *f) const noexcept
#endif
// Process M204
-GCodeResult Move::ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) noexcept
+GCodeResult Move::ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException)
{
bool seen = false;
if (gb.Seen('S'))
@@ -1020,7 +1000,7 @@ GCodeResult Move::ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply)
}
// Process M595
-GCodeResult Move::ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) noexcept
+GCodeResult Move::ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException)
{
const size_t ringNumber = (gb.Seen('Q')) ? gb.GetLimitedUIValue('Q', ARRAY_SIZE(rings)) : 0;
return rings[ringNumber].ConfigureMovementQueue(gb, reply);
diff --git a/src/Movement/Move.h b/src/Movement/Move.h
index a4559968..5cfcf29e 100644
--- a/src/Movement/Move.h
+++ b/src/Movement/Move.h
@@ -93,8 +93,8 @@ public:
float PushBabyStepping(size_t axis, float amount) noexcept; // Try to push some babystepping through the lookahead queue
- GCodeResult ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) noexcept; // process M204
- GCodeResult ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) noexcept; // process M595
+ GCodeResult ConfigureAccelerations(GCodeBuffer&gb, const StringRef& reply) THROWS(GCodeException); // process M204
+ GCodeResult ConfigureMovementQueue(GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); // process M595
float GetMaxPrintingAcceleration() const noexcept { return maxPrintingAcceleration; }
float GetMaxTravelAcceleration() const noexcept { return maxTravelAcceleration; }
@@ -120,7 +120,7 @@ public:
bool IsRawMotorMove(uint8_t moveType) const noexcept; // Return true if this is a raw motor move
float IdleTimeout() const noexcept; // Returns the idle timeout in seconds
- void SetIdleTimeout(float timeout) noexcept; // Set the idle timeout in seconds
+ void SetIdleTimeout(float timeout) noexcept; // Set the idle timeout in seconds
void Simulate(uint8_t simMode) noexcept; // Enter or leave simulation mode
float GetSimulationTime() const noexcept { return mainDDARing.GetSimulationTime(); } // Get the accumulated simulation time
@@ -214,10 +214,11 @@ private:
timing // no moves being executed or in queue, motors are at full current
};
- void BedTransform(float move[MaxAxes], const Tool *tool) const noexcept; // Take a position and apply the bed compensations
- void InverseBedTransform(float move[MaxAxes],const Tool *tool) const noexcept; // Go from a bed-transformed point back to user coordinates
- void AxisTransform(float move[MaxAxes], const Tool *tool) const noexcept; // Take a position and apply the axis-angle compensations
- void InverseAxisTransform(float move[MaxAxes], const Tool *tool) const noexcept; // Go from an axis transformed point back to user coordinates
+ void BedTransform(float xyzPoint[MaxAxes], const Tool *tool) const noexcept; // Take a position and apply the bed compensations
+ void InverseBedTransform(float xyzPoint[MaxAxes], const Tool *tool) const noexcept; // Go from a bed-transformed point back to user coordinates
+ void AxisTransform(float xyzPoint[MaxAxes], const Tool *tool) const noexcept; // Take a position and apply the axis-angle compensations
+ void InverseAxisTransform(float xyzPoint[MaxAxes], const Tool *tool) const noexcept; // Go from an axis transformed point back to user coordinates
+ float ComputeHeightCorrection(float xyzPoint[MaxAxes], const Tool *tool) const noexcept; // Compute the height correction needed at a point, ignoring taper
const char *GetCompensationTypeString() const noexcept;
diff --git a/src/Networking/ESP8266WiFi/WiFiInterface.cpp b/src/Networking/ESP8266WiFi/WiFiInterface.cpp
index 6f46ccc8..8be20e71 100644
--- a/src/Networking/ESP8266WiFi/WiFiInterface.cpp
+++ b/src/Networking/ESP8266WiFi/WiFiInterface.cpp
@@ -1149,35 +1149,7 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri
}
case 589: // Configure access point
- if (gb.Seen('T'))
- {
- // Special code to set max transmitter power, 0 to 20.5dBm
- const float powerTimes4 = gb.GetFValue() * 4;
- if (powerTimes4 < 0.0 || powerTimes4 > 82.0)
- {
- reply.copy("Power setting out of range");
- }
- else
- {
- const int32_t rslt = SendCommand(NetworkCommand::networkSetTxPower, 0, (uint8_t)powerTimes4, 0, nullptr, 0, nullptr, 0);
- if (rslt == ResponseEmpty)
- {
- return GCodeResult::ok;
- }
- reply.printf("Failed to set maximum transmit power: %s", TranslateWiFiResponse(rslt));
- }
- }
- else if (gb.Seen('C'))
- {
- const uint32_t clockVal = gb.GetUIValue();
- const int32_t rslt = SendCommand(NetworkCommand::networkSetClockControl, 0, 0, clockVal, nullptr, 0, nullptr, 0);
- if (rslt == ResponseEmpty)
- {
- return GCodeResult::ok;
- }
- reply.printf("Failed to set clock: %s", TranslateWiFiResponse(rslt));
- }
- else if (gb.Seen('S'))
+ if (gb.Seen('S'))
{
// Configure access point parameters
WirelessConfigurationData config;
@@ -1217,6 +1189,35 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri
reply.printf("Failed to configure access point parameters: %s", TranslateWiFiResponse(rslt));
}
+ else if (gb.Seen('T'))
+ {
+ // Special code to set max transmitter power, 0 to 20.5dBm
+ const float powerTimes4 = gb.GetFValue() * 4;
+ if (powerTimes4 < 0.0 || powerTimes4 > 82.0)
+ {
+ reply.copy("Power setting out of range");
+ }
+ else
+ {
+ const int32_t rslt = SendCommand(NetworkCommand::networkSetTxPower, 0, (uint8_t)powerTimes4, 0, nullptr, 0, nullptr, 0);
+ if (rslt == ResponseEmpty)
+ {
+ return GCodeResult::ok;
+ }
+ reply.printf("Failed to set maximum transmit power: %s", TranslateWiFiResponse(rslt));
+ }
+ }
+ else if (gb.Seen('L'))
+ {
+ // Special code to configure SPI clock speed
+ const uint32_t clockVal = gb.GetUIValue();
+ const int32_t rslt = SendCommand(NetworkCommand::networkSetClockControl, 0, 0, clockVal, nullptr, 0, nullptr, 0);
+ if (rslt == ResponseEmpty)
+ {
+ return GCodeResult::ok;
+ }
+ reply.printf("Failed to set clock: %s", TranslateWiFiResponse(rslt));
+ }
else
{
// Report access point parameters
diff --git a/src/ObjectModel/GlobalVariables.cpp b/src/ObjectModel/GlobalVariables.cpp
index 0d4cd7bf..0e532135 100644
--- a/src/ObjectModel/GlobalVariables.cpp
+++ b/src/ObjectModel/GlobalVariables.cpp
@@ -20,16 +20,15 @@ void GlobalVariables::ReportAsJson(OutputBuffer *buf, ObjectExplorationContext&
buf->cat('{');
if (context.IncreaseDepth())
{
- bool needComma = false;
{
ReadLocker locker(lock); // make sure that no other task modifies the list while we are traversing it
-
- for (const Variable *v = vars.GetRoot(); v != nullptr; v = v->GetNext())
- {
- buf->catf((needComma) ? ",\"%s\":" : "\"%s\":", v->GetName().Ptr());
- ReportItemAsJsonFull(buf, context, classDescriptor, v->GetValue(), filter);
- needComma = true;
- }
+ vars.IterateWhile([this, buf, &context, classDescriptor, filter](unsigned int index, const Variable& v) noexcept -> bool
+ {
+ buf->catf((index != 0) ? ",\"%s\":" : "\"%s\":", v.GetName().Ptr());
+ ReportItemAsJsonFull(buf, context, classDescriptor, v.GetValue(), filter);
+ return true;
+ }
+ );
}
context.DecreaseDepth();
}
diff --git a/src/ObjectModel/ObjectModel.cpp b/src/ObjectModel/ObjectModel.cpp
index 14f458d0..6a1dfd51 100644
--- a/src/ObjectModel/ObjectModel.cpp
+++ b/src/ObjectModel/ObjectModel.cpp
@@ -15,6 +15,13 @@
#include <cstring>
#include <General/SafeStrtod.h>
#include <General/IP4String.h>
+#include <Hardware/ExceptionHandlers.h>
+
+namespace StackUsage
+{
+ constexpr uint32_t GetObjectValue_noTable = 56;
+ constexpr uint32_t GetObjectValue_withTable = 48;
+}
ExpressionValue::ExpressionValue(const MacAddress& mac) noexcept : type((uint32_t)TypeCode::MacAddress), param(mac.HighWord()), uVal(mac.LowWord())
{
@@ -179,7 +186,7 @@ void ExpressionValue::Release() noexcept
#if SUPPORT_CAN_EXPANSION
-// Given that this is a CanExpansionBoardDetails value, extract the part requested according to the parameter
+// Given that this is a CanExpansionBoardDetails value, extract the part requested according to the parameter and append it to the string
// sVal is a string of the form shortName|version
void ExpressionValue::ExtractRequestedPart(const StringRef& rslt) const noexcept
{
@@ -192,22 +199,15 @@ void ExpressionValue::ExtractRequestedPart(const StringRef& rslt) const noexcept
switch((ExpansionDetail)param)
{
case ExpansionDetail::shortName:
- rslt.copy(sVal, indexOfDivider);
+ rslt.catn(sVal, indexOfDivider);
break;
case ExpansionDetail::firmwareVersion:
- if (p == nullptr)
- {
- rslt.Clear();
- }
- else
- {
- rslt.copy(sVal + indexOfDivider + 1);
- }
+ rslt.cat((p == nullptr) ? "unknown" : sVal + indexOfDivider + 1);
break;
case ExpansionDetail::firmwareFileName:
- rslt.copy("Duet3Firmware_");
+ rslt.cat("Duet3Firmware_");
rslt.catn(sVal, indexOfDivider);
rslt.cat(".bin");
break;
@@ -361,6 +361,26 @@ GCodeException ObjectExplorationContext::ConstructParseException(const char *msg
return GCodeException(line, column, msg, sparam);
}
+// Call this before making a recursive call, or before calling a function that needs a lot of stack from a recursive function
+void ObjectExplorationContext::CheckStack(uint32_t calledFunctionStackUsage) const THROWS(GCodeException)
+{
+ register const char * stackPtr asm ("sp");
+ const char *stackLimit = (const char*)TaskBase::GetCallerTaskHandle() + sizeof(TaskBase);
+ if (stackLimit + calledFunctionStackUsage + (StackUsage::Throw + StackUsage::Margin) < stackPtr)
+ {
+ return;
+ }
+
+ // The stack is in danger of overflowing. Throw an exception if we have enough stack to do so (ideally, this should always be the case)
+ if (stackLimit + StackUsage::Throw <= stackPtr)
+ {
+ throw GCodeException(line, column, "Expression nesting too deep");
+ }
+
+ // Not enough stack left to throw an exception
+ SoftwareReset(SoftwareResetReason::stackOverflow, (const uint32_t *)stackPtr);
+}
+
// Report this object
void ObjectModel::ReportAsJson(OutputBuffer* buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor,
uint8_t tableNumber, const char* filter) const THROWS(GCodeException)
@@ -850,7 +870,8 @@ int ObjectModelTableEntry::IdCompare(const char *id) const noexcept
}
// Get the value of an object
-ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, const char *idString, uint8_t tableNumber) const
+ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, const char *idString, uint8_t tableNumber) const THROWS(GCodeException)
+decrease(strlen(idString)) // recursion variant
{
if (classDescriptor == nullptr)
{
@@ -868,6 +889,7 @@ ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, c
}
idString = GetNextElement(idString);
const ExpressionValue val = e->func(this, context);
+ context.CheckStack(StackUsage::GetObjectValue_noTable);
return GetObjectValue(context, classDescriptor, val, idString);
}
if (tableNumber != 0)
@@ -885,7 +907,8 @@ ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, c
throw context.ConstructParseException("unknown value '%s'", idString);
}
-ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ExpressionValue& val, const char *idString) const
+ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor, const ExpressionValue& val, const char *idString) const THROWS(GCodeException)
+decrease(strlen(idString)) // recursion variant
{
if (*idString == 0 && context.WantExists() && val.GetType() != TypeCode::None)
{
@@ -923,6 +946,7 @@ ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, c
}
const ExpressionValue arrayElement = val.omadVal->GetElement(this, context);
+ context.CheckStack(StackUsage::GetObjectValue_noTable);
return GetObjectValue(context, classDescriptor, arrayElement, idString + 1);
}
@@ -932,6 +956,7 @@ ExpressionValue ObjectModel::GetObjectValue(ObjectExplorationContext& context, c
case 0:
return val;
case '.':
+ context.CheckStack(StackUsage::GetObjectValue_withTable);
return val.omVal->GetObjectValue(context, (val.omVal == this) ? classDescriptor : nullptr, idString + 1, val.param);
case '^':
throw context.ConstructParseException("object is not an array");
diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h
index 24f810d3..6a2858e0 100644
--- a/src/ObjectModel/ObjectModel.h
+++ b/src/ObjectModel/ObjectModel.h
@@ -147,9 +147,11 @@ struct ExpressionValue
void Set(bool b) noexcept { Release(); type = (uint32_t)TypeCode::Bool; bVal = b; }
void Set(char c) noexcept { Release(); type = (uint32_t)TypeCode::Char; cVal = c; }
void Set(int32_t i) noexcept { Release(); type = (uint32_t)TypeCode::Int32; iVal = i; }
- void Set(float f) noexcept { Release(); type = (uint32_t)TypeCode::Float; fVal = f; param = 1; }
+ void Set(float f) noexcept { Release(); type = (uint32_t)TypeCode::Float; fVal = f; param = MaxFloatDigitsDisplayedAfterPoint; }
+ void Set(float f, uint32_t digits) noexcept { Release(); type = (uint32_t)TypeCode::Float; fVal = f; param = digits; }
void Set(const char *s) noexcept { Release(); type = (uint32_t)TypeCode::CString; sVal = s; }
void Set(StringHandle sh) noexcept { Release(); type = (uint32_t)TypeCode::HeapString; shVal = sh; }
+ void Set(nullptr_t dummy) noexcept { Release(); type = (uint32_t)TypeCode::None; }
// Store a 56-bit value
void Set56BitValue(uint64_t v) { Release(); param = (uint32_t)(v >> 32) & 0x00FFFFFF; uVal = (uint32_t)v; }
@@ -219,6 +221,7 @@ public:
GCodeException ConstructParseException(const char *msg) const noexcept;
GCodeException ConstructParseException(const char *msg, const char *sparam) const noexcept;
+ void CheckStack(uint32_t calledFunctionStackUsage) const THROWS(GCodeException);
private:
static constexpr size_t MaxIndices = 4; // max depth of array nesting
@@ -266,7 +269,7 @@ public:
void ReportAsJson(OutputBuffer *buf, const char *filter, const char *reportFlags, bool wantArrayLength) const THROWS(GCodeException);
// Get the value of an object via the table
- ExpressionValue GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, const char *idString, uint8_t tableNumber = 0) const THROWS(GCodeException);
+ ExpressionValue GetObjectValue(ObjectExplorationContext& context, const ObjectModelClassDescriptor * null classDescriptor, const char *idString, uint8_t tableNumber) const THROWS(GCodeException);
// Function to report a value or object as JSON. This does not need to handle 'var' or 'global' because those are checked for before this is called.
void ReportItemAsJson(OutputBuffer *buf, ObjectExplorationContext& context, const ObjectModelClassDescriptor *classDescriptor,
diff --git a/src/ObjectModel/Variable.cpp b/src/ObjectModel/Variable.cpp
index 85c143f8..65e6b55f 100644
--- a/src/ObjectModel/Variable.cpp
+++ b/src/ObjectModel/Variable.cpp
@@ -118,4 +118,25 @@ VariableSet::~VariableSet()
Clear();
}
+// Delete any existing variables and instead use the variables from the argument, taking ownership of them
+void VariableSet::AssignFrom(VariableSet& other) noexcept
+{
+ Clear();
+ root = other.root;
+ other.root = nullptr;
+}
+
+void VariableSet::IterateWhile(stdext::inplace_function<bool(unsigned int, const Variable&) /*noexcept*/ > func) const noexcept
+{
+ unsigned int num = 0;
+ for (const Variable *v = root; v != nullptr; v = v->GetNext())
+ {
+ if (!func(num, *v))
+ {
+ break;
+ }
+ ++num;
+ }
+}
+
// End
diff --git a/src/ObjectModel/Variable.h b/src/ObjectModel/Variable.h
index c40cff59..28717688 100644
--- a/src/ObjectModel/Variable.h
+++ b/src/ObjectModel/Variable.h
@@ -45,15 +45,20 @@ class VariableSet
public:
VariableSet() noexcept : root(nullptr) { }
~VariableSet();
+ VariableSet(const VariableSet&) = delete;
+ VariableSet& operator=(const VariableSet& other) = delete;
+
+ void AssignFrom(VariableSet& other) noexcept;
Variable *Lookup(const char *str) noexcept;
const Variable *Lookup(const char *str) const noexcept;
- const Variable *GetRoot() const noexcept { return root; }
void Insert(Variable *toInsert) noexcept;
void EndScope(uint8_t blockNesting) noexcept;
void Delete(const char *str) noexcept;
void Clear() noexcept;
+ void IterateWhile(stdext::inplace_function<bool(unsigned int index, const Variable& v) /*noexcept*/ > func) const noexcept;
+
private:
Variable *root;
};
diff --git a/src/Platform/Heap.cpp b/src/Platform/Heap.cpp
index 1f7f389a..5f34a6f6 100644
--- a/src/Platform/Heap.cpp
+++ b/src/Platform/Heap.cpp
@@ -353,6 +353,29 @@ StringHandle::StringHandle(const char *s, size_t len) noexcept
}
}
+#if 0 // This constructor is currently unused, but may be useful in future
+// Build a handle by concatenating two strings
+StringHandle::StringHandle(const char *s1, const char *s2) noexcept
+{
+ const size_t len = strlen(s1) + strlen(s2);
+ if (len == 0)
+ {
+ slotPtr = nullptr;
+ }
+ else
+ {
+ WriteLocker locker(heapLock); // prevent other tasks modifying the heap
+ IndexSlot * const slot = AllocateHandle();
+ StorageSpace * const space = AllocateSpace(len + 1);
+ SafeStrncpy(space->data, s1, space->length);
+ SafeStrncat(space->data, s2, space->length);
+ slot->storage = space;
+ slot->refCount = 1;
+ slotPtr = slot;
+ }
+}
+#endif
+
void StringHandle::Assign(const char *s) noexcept
{
Delete();
@@ -366,11 +389,9 @@ void StringHandle::Assign(const char *s) noexcept
void StringHandle::InternalAssign(const char *s, size_t len) noexcept
{
- IndexSlot * const slot = AllocateHandle(); // allocate a handle
+ IndexSlot * const slot = AllocateHandle();
StorageSpace * const space = AllocateSpace(len + 1);
- const size_t lengthToCopy = min<size_t>(len, space->length - 1); // truncate the string if it won't fit
- memcpy(space->data, s, lengthToCopy);
- space->data[lengthToCopy] = 0;
+ SafeStrncpy(space->data, s, space->length);
slot->storage = space;
slot->refCount = 1;
slotPtr = slot;
@@ -380,7 +401,7 @@ void StringHandle::Delete() noexcept
{
if (slotPtr != nullptr)
{
- ReadLocker locker(heapLock); // prevent other tasks modifying the heap
+ ReadLocker locker(heapLock); // prevent other tasks modifying the heap
InternalDelete();
}
}
diff --git a/src/Platform/Heap.h b/src/Platform/Heap.h
index 5fdca862..4d6009f5 100644
--- a/src/Platform/Heap.h
+++ b/src/Platform/Heap.h
@@ -26,6 +26,10 @@ public:
StringHandle(const char *s) noexcept;
StringHandle(const char *s, size_t len) noexcept;
+#if 0 // unused
+ StringHandle(const char *s1, const char *s2) noexcept;
+#endif
+
ReadLockedPointer<const char> Get() const noexcept;
size_t GetLength() const noexcept;
void Delete() noexcept;
diff --git a/src/Tools/Tool.cpp b/src/Tools/Tool.cpp
index 3a39e39c..6806207a 100644
--- a/src/Tools/Tool.cpp
+++ b/src/Tools/Tool.cpp
@@ -442,7 +442,7 @@ void Tool::Activate() noexcept
state = ToolState::active;
}
-void Tool::Standby() noexcept
+void Tool::HeatersToStandby() const noexcept
{
const Tool * const currentTool = reprap.GetCurrentTool();
for (size_t heater = 0; heater < heaterCount; heater++)
@@ -463,6 +463,48 @@ void Tool::Standby() noexcept
}
}
}
+}
+
+void Tool::HeatersToActive() const noexcept
+{
+ const Tool * const currentTool = reprap.GetCurrentTool();
+ for (size_t heater = 0; heater < heaterCount; heater++)
+ {
+ // Don't switch a heater to active if the active tool is using it and is different from this tool
+ if (currentTool == this || currentTool == nullptr || !currentTool->UsesHeater(heater))
+ {
+ try
+ {
+ reprap.GetHeat().SetActiveTemperature(heaters[heater], activeTemperatures[heater]);
+ String<1> dummy;
+ (void)reprap.GetHeat().Activate(heaters[heater], dummy.GetRef());
+ }
+ catch (const GCodeException& exc)
+ {
+ String<StringLength100> message;
+ exc.GetMessage(message.GetRef(), nullptr);
+ reprap.GetPlatform().Message(ErrorMessage, message.c_str());
+ }
+ }
+ }
+}
+
+void Tool::HeatersToOff() const noexcept
+{
+ const Tool * const currentTool = reprap.GetCurrentTool();
+ for (size_t heater = 0; heater < heaterCount; heater++)
+ {
+ // Don't switch a heater to standby if the active tool is using it and is different from this tool
+ if (currentTool == this || currentTool == nullptr || !currentTool->UsesHeater(heater))
+ {
+ reprap.GetHeat().SwitchOff(heaters[heater]);
+ }
+ }
+}
+
+void Tool::Standby() noexcept
+{
+ HeatersToStandby();
// NIST Standard M6 says "When the tool change is complete: * The spindle will be stopped. [...]"
// We don't have M6 but Tn already does tool change so we need
@@ -472,6 +514,7 @@ void Tool::Standby() noexcept
Spindle& spindle = reprap.GetPlatform().AccessSpindle(spindleNumber);
spindle.SetState(SpindleState::stopped);
}
+
state = ToolState::standby;
}
diff --git a/src/Tools/Tool.h b/src/Tools/Tool.h
index 377ce0bf..6c8f01a0 100644
--- a/src/Tools/Tool.h
+++ b/src/Tools/Tool.h
@@ -116,6 +116,10 @@ public:
void SetFansPwm(float f) const noexcept;
+ void HeatersToOff() const noexcept;
+ void HeatersToActive() const noexcept;
+ void HeatersToStandby() const noexcept;
+
protected:
DECLARE_OBJECT_MODEL
OBJECT_MODEL_ARRAY(activeTemps)
diff --git a/src/Version.h b/src/Version.h
index c1a1566a..0cd87871 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -9,7 +9,7 @@
#define SRC_VERSION_H_
#ifndef VERSION
-# define MAIN_VERSION "3.3RC1"
+# define MAIN_VERSION "3.3RC2+1"
# ifdef USE_CAN0
# define VERSION_SUFFIX " (CAN0)"
# else