diff options
-rw-r--r-- | src/Display/Menu.cpp | 9 | ||||
-rw-r--r-- | src/Display/MenuItem.cpp | 2 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer/StringParser.cpp | 185 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer/StringParser.h | 1 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 4 | ||||
-rw-r--r-- | src/GCodes/GCodes.h | 2 | ||||
-rw-r--r-- | src/GCodes/GCodes2.cpp | 2 | ||||
-rw-r--r-- | src/GCodes/GCodes3.cpp | 14 | ||||
-rw-r--r-- | src/Heating/Heat.cpp | 104 | ||||
-rw-r--r-- | src/Networking/ESP8266WiFi/WiFiInterface.cpp | 5 | ||||
-rw-r--r-- | src/ObjectModel/ObjectModel.h | 54 |
11 files changed, 263 insertions, 119 deletions
diff --git a/src/Display/Menu.cpp b/src/Display/Menu.cpp index 76098d06..05954849 100644 --- a/src/Display/Menu.cpp +++ b/src/Display/Menu.cpp @@ -540,13 +540,12 @@ void Menu::EncoderActionEnterItemHelper() if (highlightedItem->Select(command.GetRef())) { char *pcCurrentCommand = command.GetRef().Pointer(); - int nNextCommandIndex = StringContains(pcCurrentCommand, "|"); - while (-1 != nNextCommandIndex) + char *pcNextCommand; + while ((pcNextCommand = strchr(pcCurrentCommand, '|')) != nullptr) { - *(pcCurrentCommand + nNextCommandIndex) = '\0'; + *pcNextCommand = '\0'; EncoderAction_ExecuteHelper(pcCurrentCommand); - pcCurrentCommand += nNextCommandIndex + 1; - nNextCommandIndex = StringContains(pcCurrentCommand, "|"); + pcCurrentCommand = pcNextCommand + 1; } EncoderAction_ExecuteHelper(pcCurrentCommand); } diff --git a/src/Display/MenuItem.cpp b/src/Display/MenuItem.cpp index 3fa0d3a0..0f807d13 100644 --- a/src/Display/MenuItem.cpp +++ b/src/Display/MenuItem.cpp @@ -1056,7 +1056,7 @@ bool FilesMenuItem::Select(const StringRef& cmd) // TODO: do this on the way in and it might be less work... // On the other hand, this only occurs when an item is selected so it's O(1) vs. O(n) - nReplacementIndex = StringContains(cmd.c_str(), "menu"); + nReplacementIndex = cmd.Contains("menu"); if (nReplacementIndex != -1) { cmd.Truncate(nReplacementIndex); diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp index bceefe53..8f8329fe 100644 --- a/src/GCodes/GCodeBuffer/StringParser.cpp +++ b/src/GCodes/GCodeBuffer/StringParser.cpp @@ -1809,6 +1809,24 @@ void StringParser::BalanceTypes(ExpressionValue& val1, ExpressionValue& val2) } } +void StringParser::EnsureNumeric(ExpressionValue& val) +{ + switch (val.type) + { + case TYPE_OF(uint32_t): + val.type = TYPE_OF(int32_t); + val.iVal = val.uVal; + break; + + case TYPE_OF(int32_t): + case TYPE_OF(float): + break; + + default: + throw ConstructParseException("expected numeric operand"); + } +} + void StringParser::ConvertToFloat(ExpressionValue& val) { switch (val.type) @@ -1943,18 +1961,9 @@ ExpressionValue StringParser::ParseNumber() ExpressionValue StringParser::ParseIdentifier() { unsigned int start = readPointer; - unsigned int numBrackets = 0; char c; - while (isalpha((c = gb.buffer[readPointer])) || isdigit(c) || c == '_' || c == '.' || c == '(' || (c == ')' && numBrackets != 0)) + while (isalpha((c = gb.buffer[readPointer])) || isdigit(c) || c == '_' || c == '.') { - if (c == '(') - { - ++numBrackets; - } - else if (c == ')') - { - --numBrackets; - } ++readPointer; } String<MaxVariableNameLength> varName; @@ -1963,14 +1972,158 @@ ExpressionValue StringParser::ParseIdentifier() throw ConstructParseException("variable name too long");; } - //TODO consider supporting standard CNC functions here, also 'true', 'false', 'pi' - const ExpressionValue val = reprap.GetObjectValue(*this, varName.c_str()); - if (c != '}') + // Check for the names of constants + if (varName.Equals("true")) { - throw ConstructParseException("expected '}'"); + return ExpressionValue(true); } - ++readPointer; - return val; + + if (varName.Equals("false")) + { + return ExpressionValue(false); + } + + if (varName.Equals("pi")) + { + return ExpressionValue(Pi); + } + + // Check whether it is a function call + SkipWhiteSpace(); + if (gb.buffer[readPointer] == '(') + { + // It's a function call + ExpressionValue rslt = ParseExpression(0); + if (varName.Equals("abs")) + { + switch (rslt.type) + { + case TYPE_OF(int32_t): + rslt.iVal = labs(rslt.iVal); + break; + + case TYPE_OF(float): + rslt.fVal = fabsf(rslt.fVal); + break; + + default: + throw ConstructParseException("expected numeric operand"); + } + } + else if (varName.Equals("sin")) + { + ConvertToFloat(rslt); + rslt.fVal = sinf(rslt.fVal); + } + else if (varName.Equals("cos")) + { + ConvertToFloat(rslt); + rslt.fVal = cosf(rslt.fVal); + } + else if (varName.Equals("tan")) + { + ConvertToFloat(rslt); + rslt.fVal = tanf(rslt.fVal); + } + else if (varName.Equals("asin")) + { + ConvertToFloat(rslt); + rslt.fVal = asinf(rslt.fVal); + } + else if (varName.Equals("acos")) + { + ConvertToFloat(rslt); + rslt.fVal = acosf(rslt.fVal); + } + else if (varName.Equals("atan")) + { + ConvertToFloat(rslt); + rslt.fVal = atanf(rslt.fVal); + } + else if (varName.Equals("atan2")) + { + ConvertToFloat(rslt); + SkipWhiteSpace(); + if (gb.buffer[readPointer] != ',') + { + throw ConstructParseException("expected ','"); + } + ++readPointer; + SkipWhiteSpace(); + ExpressionValue nextOperand = ParseExpression(0); + ConvertToFloat(nextOperand); + rslt.fVal = atan2f(rslt.fVal, nextOperand.fVal); + } + else if (varName.Equals("sqrt")) + { + ConvertToFloat(rslt); + rslt.fVal = sqrtf(rslt.fVal); + } + else if (varName.Equals("isnan")) + { + ConvertToFloat(rslt); + rslt.type = TYPE_OF(bool); + rslt.bVal = (isnan(rslt.fVal) != 0); + } + else if (varName.Equals("max")) + { + for (;;) + { + SkipWhiteSpace(); + if (gb.buffer[readPointer] != ',') + { + break; + } + ++readPointer; + SkipWhiteSpace(); + ExpressionValue nextOperand = ParseExpression(0); + BalanceNumericTypes(rslt, nextOperand); + if (rslt.type == TYPE_OF(float)) + { + rslt.fVal = max<float>(rslt.fVal, nextOperand.fVal); + } + else + { + rslt.iVal = max<int32_t>(rslt.iVal, nextOperand.iVal); + } + } + } + else if (varName.Equals("min")) + { + for (;;) + { + SkipWhiteSpace(); + if (gb.buffer[readPointer] != ',') + { + break; + } + ++readPointer; + SkipWhiteSpace(); + ExpressionValue nextOperand = ParseExpression(0); + BalanceNumericTypes(rslt, nextOperand); + if (rslt.type == TYPE_OF(float)) + { + rslt.fVal = min<float>(rslt.fVal, nextOperand.fVal); + } + else + { + rslt.iVal = min<int32_t>(rslt.iVal, nextOperand.iVal); + } + } + } + else + { + throw ConstructParseException("unknown function"); + } + SkipWhiteSpace(); + if (gb.buffer[readPointer] != ')') + { + throw ConstructParseException("expected ')'"); + } + return rslt; + } + + return reprap.GetObjectValue(*this, varName.c_str()); } ParseException StringParser::ConstructParseException(const char *str) const diff --git a/src/GCodes/GCodeBuffer/StringParser.h b/src/GCodes/GCodeBuffer/StringParser.h index c4c39687..7eadbe1a 100644 --- a/src/GCodes/GCodeBuffer/StringParser.h +++ b/src/GCodes/GCodeBuffer/StringParser.h @@ -122,6 +122,7 @@ private: void BalanceTypes(ExpressionValue& val1, ExpressionValue& val2) THROWS_PARSE_ERROR; void ConvertToFloat(ExpressionValue& val) THROWS_PARSE_ERROR; void ConvertToBool(ExpressionValue& val) THROWS_PARSE_ERROR; + void EnsureNumeric(ExpressionValue& val) THROWS_PARSE_ERROR; void SkipWhiteSpace(); diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 40180f0f..8f9fba89 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -3487,13 +3487,13 @@ GCodeResult GCodes::LoadFilament(GCodeBuffer& gb, const StringRef& reply) String<FilamentNameLength> filamentName; gb.GetQuotedString(filamentName.GetRef()); - if (StringContains(filamentName.c_str(), ",") >= 0) + if (filamentName.Contains(',') >= 0) { reply.copy("Filament names must not contain commas"); return GCodeResult::error; } - if (StringEqualsIgnoreCase(filamentName.c_str(), tool->GetFilament()->GetName())) + if (filamentName.EqualsIgnoreCase(tool->GetFilament()->GetName())) { // Filament already loaded - nothing to do return GCodeResult::ok; diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index 997ffd9e..aadb2da5 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -259,7 +259,7 @@ private: GCodeResult StraightProbe(GCodeBuffer& gb, const StringRef& reply); // Deal with a G38.x GCodeResult DoDriveMapping(GCodeBuffer& gb, const StringRef& reply); // Deal with a M584 GCodeResult ProbeTool(GCodeBuffer& gb, const StringRef& reply); // Deal with a M585 - GCodeResult FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply, const bool towardsMin = true); // Deal with a M675 + GCodeResult FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply, const bool towardsMin = true) THROWS_PARSE_ERROR; // Deal with a M675 GCodeResult SetDateTime(GCodeBuffer& gb,const StringRef& reply); // Deal with a M905 GCodeResult SavePosition(GCodeBuffer& gb,const StringRef& reply); // Deal with G60 GCodeResult ConfigureDriver(GCodeBuffer& gb,const StringRef& reply); // Deal with M569 diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 95169b7d..8d7073bc 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -4358,7 +4358,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) { String<StringLength20> eraseString; gb.GetQuotedString(eraseString.GetRef()); - if (strcmp(eraseString.c_str(), "ERASE") == 0) + if (eraseString.Equals("ERASE")) { reason = (uint16_t)SoftwareResetReason::erase; } diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp index 43e52665..3ffeef1e 100644 --- a/src/GCodes/GCodes3.cpp +++ b/src/GCodes/GCodes3.cpp @@ -898,17 +898,9 @@ GCodeResult GCodes::FindCenterOfCavity(GCodeBuffer& gb, const StringRef& reply, moveBuffer.coords[axis] = towardsMin ? platform.AxisMinimum(axis) : platform.AxisMaximum(axis); // Deal with feed rate - if (gb.Seen(feedrateLetter)) - { - const float rate = gb.GetDistance(); - gb.MachineState().feedRate = rate * SecondsToMinutes; // don't apply the speed factor to homing and other special moves - } - else - { - reply.copy("No feed rate provided."); - return GCodeResult::badOrMissingParameter; - } - moveBuffer.feedRate = gb.MachineState().feedRate; + gb.MustSee(feedrateLetter); + const float rate = gb.GetDistance(); + moveBuffer.feedRate = gb.MachineState().feedRate = rate * SecondsToMinutes; // don't apply the speed factor to homing and other special moves const bool probeOk = (useProbe) ? platform.GetEndstops().EnableZProbe(probeNumberToUse) diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index 5d436de5..76616120 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -130,71 +130,67 @@ ReadLockedPointer<Heater> Heat::FindHeater(int heater) const noexcept // Process M307 GCodeResult Heat::SetOrReportHeaterModel(GCodeBuffer& gb, const StringRef& reply) { - if (gb.Seen('H')) + gb.MustSee('H'); + const unsigned int heater = gb.GetUIValue(); + const auto h = FindHeater(heater); + if (h.IsNotNull()) { - const unsigned int heater = gb.GetUIValue(); - const auto h = FindHeater(heater); - if (h.IsNotNull()) + const FopDt& model = h->GetModel(); + bool seen = false; + float gain = model.GetGain(), + tc = model.GetTimeConstant(), + td = model.GetDeadTime(), + maxPwm = model.GetMaxPwm(), + voltage = model.GetVoltage(); + int32_t dontUsePid = model.UsePid() ? 0 : 1; + int32_t inversionParameter = 0; + + gb.TryGetFValue('A', gain, seen); + gb.TryGetFValue('C', tc, seen); + gb.TryGetFValue('D', td, seen); + gb.TryGetIValue('B', dontUsePid, seen); + gb.TryGetFValue('S', maxPwm, seen); + gb.TryGetFValue('V', voltage, seen); + gb.TryGetIValue('I', inversionParameter, seen); + + if (seen) { - const FopDt& model = h->GetModel(); - bool seen = false; - float gain = model.GetGain(), - tc = model.GetTimeConstant(), - td = model.GetDeadTime(), - maxPwm = model.GetMaxPwm(), - voltage = model.GetVoltage(); - int32_t dontUsePid = model.UsePid() ? 0 : 1; - int32_t inversionParameter = 0; - - gb.TryGetFValue('A', gain, seen); - gb.TryGetFValue('C', tc, seen); - gb.TryGetFValue('D', td, seen); - gb.TryGetIValue('B', dontUsePid, seen); - gb.TryGetFValue('S', maxPwm, seen); - gb.TryGetFValue('V', voltage, seen); - gb.TryGetIValue('I', inversionParameter, seen); - - if (seen) + const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3); + const GCodeResult rslt = h->SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply); + if (rslt != GCodeResult::ok) { - const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3); - const GCodeResult rslt = h->SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply); - if (rslt != GCodeResult::ok) - { - return rslt; - } + return rslt; } - else if (!model.IsEnabled()) + } + else if (!model.IsEnabled()) + { + reply.printf("Heater %u is disabled", heater); + } + else + { + const char* const mode = (!model.UsePid()) ? "bang-bang" + : (model.ArePidParametersOverridden()) ? "custom PID" + : "PID"; + reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, calibration voltage %.1f, mode %s", heater, + (double)model.GetGain(), (double)model.GetTimeConstant(), (double)model.GetDeadTime(), (double)model.GetMaxPwm(), (double)model.GetVoltage(), mode); + if (model.IsInverted()) { - reply.printf("Heater %u is disabled", heater); + reply.cat(", inverted temperature control"); } - else + if (model.UsePid()) { - const char* const mode = (!model.UsePid()) ? "bang-bang" - : (model.ArePidParametersOverridden()) ? "custom PID" - : "PID"; - reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, calibration voltage %.1f, mode %s", heater, - (double)model.GetGain(), (double)model.GetTimeConstant(), (double)model.GetDeadTime(), (double)model.GetMaxPwm(), (double)model.GetVoltage(), mode); - if (model.IsInverted()) - { - reply.cat(", inverted temperature control"); - } - if (model.UsePid()) - { - // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware - M301PidParameters params = model.GetM301PidParameters(false); - reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD); - params = model.GetM301PidParameters(true); - reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD); - } + // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware + M301PidParameters params = model.GetM301PidParameters(false); + reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD); + params = model.GetM301PidParameters(true); + reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD); } - return GCodeResult::ok; } - - reply.printf("Heater %u not found", heater); - return GCodeResult::error; + return GCodeResult::ok; } - return GCodeResult::badOrMissingParameter; + reply.printf("Heater %u not found", heater); + return GCodeResult::error; } // Process M301 or M304. 'heater' is the default heater number to use. diff --git a/src/Networking/ESP8266WiFi/WiFiInterface.cpp b/src/Networking/ESP8266WiFi/WiFiInterface.cpp index 04ffe795..4e68acc5 100644 --- a/src/Networking/ESP8266WiFi/WiFiInterface.cpp +++ b/src/Networking/ESP8266WiFi/WiFiInterface.cpp @@ -973,8 +973,8 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri return GCodeResult::error; case 588: // Forget WiFi network - if (gb.Seen('S')) { + gb.MustSee('S'); String<SsidLength> ssidText; gb.GetQuotedString(ssidText.GetRef()); if (strcmp(ssidText.c_str(), "*") == 0) @@ -1001,9 +1001,6 @@ GCodeResult WiFiInterface::HandleWiFiCode(int mcode, GCodeBuffer &gb, const Stri return GCodeResult::error; } - reply.copy("Bad or missing parameter"); - return GCodeResult::error; - case 589: // Configure access point if (gb.Seen('S')) { diff --git a/src/ObjectModel/ObjectModel.h b/src/ObjectModel/ObjectModel.h index 702296bc..1d04aadc 100644 --- a/src/ObjectModel/ObjectModel.h +++ b/src/ObjectModel/ObjectModel.h @@ -20,6 +20,31 @@ typedef uint8_t TypeCode; constexpr TypeCode IsArray = 128; // this is or'ed in to a type code to indicate an array constexpr TypeCode NoType = 0; // code for an invalid or unknown type +// Dummy types, used to define type codes +class Bitmap32; +class Enum32; +class Float2; // float printed to 2 decimal places instead of 1 +class Float3; // float printed to 3 decimal places instead of 1 +class ObjectModel; // forward declaration + +// Function template used to get constexpr type IDs +// Each type must return a unique type code in the range 1 to 127 +template<class T> constexpr TypeCode TypeOf(); + +template<> constexpr TypeCode TypeOf<bool> () { return 1; } +template<> constexpr TypeCode TypeOf<uint32_t> () { return 2; } +template<> constexpr TypeCode TypeOf<int32_t>() { return 3; } +template<> constexpr TypeCode TypeOf<float>() { return 4; } +template<> constexpr TypeCode TypeOf<Float2>() { return 5; } +template<> constexpr TypeCode TypeOf<Float3>() { return 6; } +template<> constexpr TypeCode TypeOf<Bitmap32>() { return 7; } +template<> constexpr TypeCode TypeOf<Enum32>() { return 8; } +template<> constexpr TypeCode TypeOf<ObjectModel>() { return 9; } +template<> constexpr TypeCode TypeOf<const char *>() { return 10; } +template<> constexpr TypeCode TypeOf<IPAddress>() { return 11; } + +#define TYPE_OF(_t) (TypeOf<_t>()) + // Forward declarations class ObjectModelTableEntry; class ObjectModel; @@ -37,13 +62,12 @@ struct ExpressionValue const char *sVal; const ObjectModel *omVal; }; -}; -// Dummy types, used to define type codes -class Bitmap32; -class Enum32; -class Float2; // float printed to 2 decimal places instead of 1 -class Float3; // float printed to 3 decimal places instead of 1 + constexpr ExpressionValue(bool b) : type(TYPE_OF(bool)), bVal(b) { } + constexpr ExpressionValue(float f) : type(TYPE_OF(float)), fVal(f) { } + constexpr ExpressionValue(int32_t i) : type(TYPE_OF(int32_t)), iVal(i) { } + ExpressionValue() : type(NoType) { } +}; class ObjectModel { @@ -89,24 +113,6 @@ private: uint32_t *GetBitmapObjectPointer(const char *idString); }; -// Function template used to get constexpr type IDs -// Each type must return a unique type code in the range 1 to 127 -template<class T> constexpr TypeCode TypeOf(); - -template<> constexpr TypeCode TypeOf<bool> () { return 1; } -template<> constexpr TypeCode TypeOf<uint32_t> () { return 2; } -template<> constexpr TypeCode TypeOf<int32_t>() { return 3; } -template<> constexpr TypeCode TypeOf<float>() { return 4; } -template<> constexpr TypeCode TypeOf<Float2>() { return 5; } -template<> constexpr TypeCode TypeOf<Float3>() { return 6; } -template<> constexpr TypeCode TypeOf<Bitmap32>() { return 7; } -template<> constexpr TypeCode TypeOf<Enum32>() { return 8; } -template<> constexpr TypeCode TypeOf<ObjectModel>() { return 9; } -template<> constexpr TypeCode TypeOf<const char *>() { return 10; } -template<> constexpr TypeCode TypeOf<IPAddress>() { return 11; } - -#define TYPE_OF(_t) (TypeOf<_t>()) - // Entry to describe an array class ObjectModelArrayDescriptor { |