diff options
author | David Crocker <dcrocker@eschertech.com> | 2020-02-16 17:10:43 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2020-02-16 17:10:43 +0300 |
commit | 315f4f47613fe09e613b6c639a7ddc37af3516d2 (patch) | |
tree | 45133ce063bf3bb2c40f522471f0074038168149 | |
parent | 9885ed7cc55cb19fde1be14c8adfd11dfa7f86cf (diff) |
Replaced HeaterProtection instances by HeaterMonitors
47 files changed, 763 insertions, 771 deletions
diff --git a/src/CAN/CanInterface.cpp b/src/CAN/CanInterface.cpp index 9ad20b35..14b02942 100644 --- a/src/CAN/CanInterface.cpp +++ b/src/CAN/CanInterface.cpp @@ -719,7 +719,6 @@ GCodeResult CanInterface::SendRequestAndGetStandardReply(CanMessageBuffer *buf, } else { -// debugPrintf("Discarded msg src=%u RID=%u exp %u\n", buf->id.Src(), buf->msg.standardReply.requestId, rid); reply.lcatf("Discarded msg src=%u typ=%u RID=%u exp %u", buf->id.Src(), (unsigned int)buf->id.MsgType(), (unsigned int)buf->msg.standardReply.requestId, rid); } } diff --git a/src/Configuration.h b/src/Configuration.h index d379c873..79286ec2 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -86,8 +86,9 @@ constexpr float HOT_ENOUGH_TO_RETRACT = 90.0; // Celsius constexpr unsigned int MaxBadTemperatureCount = 2000/HeatSampleIntervalMillis; // Number of bad temperature samples permitted before a heater fault is reported (2 seconds) constexpr float BadLowTemperature = -10.0; // Celsius -constexpr float DefaultHotEndTemperatureLimit = 290.0; // Celsius - E3D say to tighten the hot end at 285C +constexpr float DefaultHotEndTemperatureLimit = 285.0; // Celsius - E3D say to tighten the hot end at 285C constexpr float DefaultBedTemperatureLimit = 125.0; // Celsius +constexpr float DefaultAllowedOverTemperature = 5.0; constexpr float DefaultHotEndFanTemperature = 45.0; // Temperature at which a thermostatic hot end fan comes on constexpr float ThermostatHysteresis = 1.0; // How much hysteresis we use to prevent noise turning fans on/off too often constexpr float BadErrorTemperature = 2000.0; // Must exceed any reasonable temperature limit including DEFAULT_TEMPERATURE_LIMIT diff --git a/src/DuetM/Pins_DuetM.h b/src/DuetM/Pins_DuetM.h index c737ab62..5800705a 100644 --- a/src/DuetM/Pins_DuetM.h +++ b/src/DuetM/Pins_DuetM.h @@ -65,7 +65,7 @@ constexpr size_t MaxSmartDrivers = 7; // The maximum number of smart drivers constexpr size_t MaxSensors = 32; constexpr size_t MaxHeaters = 4; // The maximum number of heaters in the machine -constexpr size_t MaxExtraHeaterProtections = 4; // The number of extra heater protection instances +constexpr size_t MaxMonitorsPerHeater = 3; // The maximum number of monitors per heater constexpr size_t MaxBedHeaters = 2; constexpr size_t MaxChamberHeaters = 2; diff --git a/src/DuetNG/Pins_DuetNG.h b/src/DuetNG/Pins_DuetNG.h index 44387da9..0095530a 100644 --- a/src/DuetNG/Pins_DuetNG.h +++ b/src/DuetNG/Pins_DuetNG.h @@ -67,7 +67,7 @@ constexpr size_t MaxSmartDrivers = 10; // The maximum number of smart drivers constexpr size_t MaxSensors = 32; constexpr size_t MaxHeaters = 10; // The maximum number of heaters in the machine -constexpr size_t MaxExtraHeaterProtections = 8; // The number of extra heater protection instances +constexpr size_t MaxMonitorsPerHeater = 3; // The maximum number of monitors per heater constexpr size_t MaxBedHeaters = 4; constexpr size_t MaxChamberHeaters = 4; diff --git a/src/Endstops/Endstop.h b/src/Endstops/Endstop.h index e0eb3a9e..8fa272a0 100644 --- a/src/Endstops/Endstop.h +++ b/src/Endstops/Endstop.h @@ -22,6 +22,7 @@ class EndstopOrZProbe INHERIT_OBJECT_MODEL { public: EndstopOrZProbe() noexcept : next(nullptr) {} + EndstopOrZProbe(const EndstopOrZProbe&) = delete; virtual ~EndstopOrZProbe() noexcept {} virtual EndStopHit Stopped() const noexcept = 0; diff --git a/src/Endstops/EndstopsManager.cpp b/src/Endstops/EndstopsManager.cpp index b618c25f..052dbd3e 100644 --- a/src/Endstops/EndstopsManager.cpp +++ b/src/Endstops/EndstopsManager.cpp @@ -21,6 +21,7 @@ #include "GCodes/GCodes.h" #include "Movement/Move.h" #include <OutputMemory.h> +#include <Heating/Heat.h> #if SUPPORT_CAN_EXPANSION # include "CanMessageBuffer.h" @@ -38,6 +39,13 @@ ReadWriteLock EndstopsManager::zProbesLock; // Macro to build a standard lambda function that includes the necessary type conversions #define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(EndstopsManager, __VA_ARGS__) +constexpr ObjectModelArrayDescriptor EndstopsManager::sensorsArrayDescriptor = +{ + &Heat::sensorsLock, + [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Heat*)self)->GetNumSensorsToReport(); }, + [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->FindSensor(context.GetLastIndex()).Ptr()); } +}; + constexpr ObjectModelArrayDescriptor EndstopsManager::endstopsArrayDescriptor = { &endstopsLock, @@ -55,7 +63,7 @@ constexpr ObjectModelArrayDescriptor EndstopsManager::filamentMonitorsArrayDescr constexpr ObjectModelArrayDescriptor EndstopsManager::inputsArrayDescriptor = { - &endstopsLock, + nullptr, [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return reprap.GetPlatform().GetNumInputsToReport(); }, [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(&reprap.GetPlatform().GetGpInPort(context.GetLastIndex())); } @@ -73,13 +81,14 @@ constexpr ObjectModelTableEntry EndstopsManager::objectModelTable[] = { // Within each group, these entries must be in alphabetical order // 0. sensors members + { "analog", OBJECT_MODEL_FUNC_NOSELF(&sensorsArrayDescriptor), ObjectModelEntryFlags::live }, { "endstops", OBJECT_MODEL_FUNC_NOSELF(&endstopsArrayDescriptor), ObjectModelEntryFlags::live }, { "filamentMonitors", OBJECT_MODEL_FUNC_NOSELF(&filamentMonitorsArrayDescriptor), ObjectModelEntryFlags::live }, { "inputs", OBJECT_MODEL_FUNC_NOSELF(&inputsArrayDescriptor), ObjectModelEntryFlags::live }, { "probes", OBJECT_MODEL_FUNC_NOSELF(&probesArrayDescriptor), ObjectModelEntryFlags::live }, }; -constexpr uint8_t EndstopsManager::objectModelTableDescriptor[] = { 1, 4 }; +constexpr uint8_t EndstopsManager::objectModelTableDescriptor[] = { 1, 5 }; DEFINE_GET_OBJECT_MODEL_TABLE(EndstopsManager) diff --git a/src/Endstops/EndstopsManager.h b/src/Endstops/EndstopsManager.h index a7ce08d1..bbd453bc 100644 --- a/src/Endstops/EndstopsManager.h +++ b/src/Endstops/EndstopsManager.h @@ -26,6 +26,7 @@ class EndstopsManager INHERIT_OBJECT_MODEL { public: EndstopsManager() noexcept; + EndstopsManager(const EndstopsManager&) = delete; void Init() noexcept; @@ -78,6 +79,7 @@ public: protected: DECLARE_OBJECT_MODEL + OBJECT_MODEL_ARRAY(sensors) OBJECT_MODEL_ARRAY(endstops) OBJECT_MODEL_ARRAY(filamentMonitors) OBJECT_MODEL_ARRAY(inputs) diff --git a/src/Endstops/ZProbe.h b/src/Endstops/ZProbe.h index 13bb807b..bbc129bc 100644 --- a/src/Endstops/ZProbe.h +++ b/src/Endstops/ZProbe.h @@ -16,7 +16,7 @@ class ZProbe : public EndstopOrZProbe public: ZProbe(unsigned int num, ZProbeType p_type) noexcept; - virtual void SetIREmitter(bool on) const noexcept = 0; + virtual void SetIREmitter(bool on) const noexcept = 0; // Caution, this is called from within the tick ISR virtual uint16_t GetRawReading() const noexcept = 0; virtual void SetProbing(bool isProbing) const noexcept = 0; virtual GCodeResult AppendPinNames(const StringRef& str) const noexcept = 0; diff --git a/src/Fans/Fan.h b/src/Fans/Fan.h index 6f2e3ce2..c0de5e3c 100644 --- a/src/Fans/Fan.h +++ b/src/Fans/Fan.h @@ -23,6 +23,7 @@ class Fan INHERIT_OBJECT_MODEL { public: Fan(unsigned int fanNum) noexcept; + Fan(const Fan&) = delete; virtual ~Fan() noexcept { } virtual bool Check() noexcept = 0; // update the fan PWM returning true if it is a thermostatic fan that is on diff --git a/src/Fans/FansManager.h b/src/Fans/FansManager.h index 32ef8034..b38986db 100644 --- a/src/Fans/FansManager.h +++ b/src/Fans/FansManager.h @@ -25,6 +25,8 @@ class FansManager { public: FansManager() noexcept; + FansManager(const FansManager&) = delete; + void Init() noexcept; bool CheckFans() noexcept; GCodeResult ConfigureFanPort(uint32_t fanNum, GCodeBuffer& gb, const StringRef& reply); diff --git a/src/FilamentMonitors/FilamentMonitor.h b/src/FilamentMonitors/FilamentMonitor.h index 4bffb40a..a08acef8 100644 --- a/src/FilamentMonitors/FilamentMonitor.h +++ b/src/FilamentMonitors/FilamentMonitor.h @@ -27,6 +27,8 @@ enum class FilamentSensorStatus : uint8_t class FilamentMonitor INHERIT_OBJECT_MODEL { public: + FilamentMonitor(const FilamentMonitor&) = delete; + // Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters virtual bool Configure(GCodeBuffer& gb, const StringRef& reply, bool& seen) = 0; diff --git a/src/GCodes/GCodeBuffer/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer/GCodeBuffer.cpp index 5b42ae8c..5e12c7db 100644 --- a/src/GCodes/GCodeBuffer/GCodeBuffer.cpp +++ b/src/GCodes/GCodeBuffer/GCodeBuffer.cpp @@ -229,6 +229,22 @@ int32_t GCodeBuffer::GetIValue() return isBinaryBuffer ? binaryParser.GetIValue() : stringParser.GetIValue(); } +// Get an integer with limit checking +int32_t GCodeBuffer::GetLimitedIValue(char c, int32_t minValue, int32_t maxValue) THROWS(GCodeException) +{ + MustSee(c); + const int32_t ret = GetIValue(); + if (ret < minValue) + { + throw GCodeException(machineState->lineNumber, -1, "parameter '%c' too low", (uint32_t)c); + } + if (ret > maxValue) + { + throw GCodeException(machineState->lineNumber, -1, "parameter '%c' too high", (uint32_t)c); + } + return ret; +} + // Get an unsigned integer value uint32_t GCodeBuffer::GetUIValue() { @@ -236,15 +252,15 @@ uint32_t GCodeBuffer::GetUIValue() } // Get an unsigned integer value, throw if >= limit -uint32_t GCodeBuffer::GetLimitedUIValue(char c, uint32_t limit) THROWS(GCodeException) +uint32_t GCodeBuffer::GetLimitedUIValue(char c, uint32_t maxValuePlusOne) THROWS(GCodeException) { MustSee(c); const uint32_t ret = GetUIValue(); - if (ret < limit) + if (ret < maxValuePlusOne) { return ret; } - throw GCodeException(machineState->lineNumber, -1, "parameter '%c' too large", (uint32_t)c); + throw GCodeException(machineState->lineNumber, -1, "parameter '%c' too high", (uint32_t)c); } // Get an IP address quad after a key letter diff --git a/src/GCodes/GCodeBuffer/GCodeBuffer.h b/src/GCodes/GCodeBuffer/GCodeBuffer.h index f25d1a73..45cb429f 100644 --- a/src/GCodes/GCodeBuffer/GCodeBuffer.h +++ b/src/GCodes/GCodeBuffer/GCodeBuffer.h @@ -71,8 +71,9 @@ public: float GetFValue() THROWS(GCodeException) __attribute__((hot)); // Get a float after a key letter float GetDistance() THROWS(GCodeException); // Get a distance or coordinate and convert it from inches to mm if necessary int32_t GetIValue() THROWS(GCodeException) __attribute__((hot)); // Get an integer after a key letter + int32_t GetLimitedIValue(char c, int32_t minValue, int32_t maxValue) THROWS(GCodeException); // Get an integer after a key letter uint32_t GetUIValue() THROWS(GCodeException); // Get an unsigned integer value - uint32_t GetLimitedUIValue(char c, uint32_t limit) THROWS(GCodeException); // Get an unsigned integer value, throw if >= limit + uint32_t GetLimitedUIValue(char c, uint32_t maxValuePlusOne) THROWS(GCodeException); // Get an unsigned integer value, throw if >= limit void GetIPAddress(IPAddress& returnedIp) THROWS(GCodeException); // Get an IP address quad after a key letter void GetMacAddress(MacAddress& mac) THROWS(GCodeException); // Get a MAC address sextet after a key letter PwmFrequency GetPwmFrequency() THROWS(GCodeException); // Get a PWM frequency diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp index 3b9b98cc..c8316bff 100644 --- a/src/GCodes/GCodeBuffer/StringParser.cpp +++ b/src/GCodes/GCodeBuffer/StringParser.cpp @@ -611,7 +611,7 @@ void StringParser::ProcessAbortCommand(const StringRef& reply) noexcept } catch (const GCodeException& e) { - e.GetMessage(reply, gb); + e.GetMessage(reply, &gb); reply.Insert(0, "invalid expression after 'abort': "); } } diff --git a/src/GCodes/GCodeException.cpp b/src/GCodes/GCodeException.cpp index 44e74a76..23108bc7 100644 --- a/src/GCodes/GCodeException.cpp +++ b/src/GCodes/GCodeException.cpp @@ -11,10 +11,13 @@ #include <GCodes/GCodeBuffer/GCodeBuffer.h> // Construct the error message. This will be prefixed with "Error: " when it is returned to the user. -void GCodeException::GetMessage(const StringRef &reply, const GCodeBuffer& gb) const noexcept +void GCodeException::GetMessage(const StringRef &reply, const GCodeBuffer *gb) const noexcept { - reply.copy((gb.IsDoingFileMacro()) ? "in file macro" : (gb.IsDoingFile()) ? "in GCode file" : "while executing command"); - if (line >= 0 && column >= 0 && gb.IsDoingFile()) + const char *context = (gb != nullptr && gb->IsDoingFileMacro()) ? "in file macro" + : (gb != nullptr && gb->IsDoingFile()) ? "in GCode file" + : "while executing command"; + reply.copy(context); + if (line >= 0 && column >= 0 && gb != nullptr && gb->IsDoingFile()) { reply.catf(", line %d column %d", line, column + 1); } diff --git a/src/GCodes/GCodeException.h b/src/GCodes/GCodeException.h index f11d6e1d..d9e9228e 100644 --- a/src/GCodes/GCodeException.h +++ b/src/GCodes/GCodeException.h @@ -33,7 +33,7 @@ public: param.i = iparam; } - void GetMessage(const StringRef& reply, const GCodeBuffer& gb) const noexcept; + void GetMessage(const StringRef& reply, const GCodeBuffer *gb) const noexcept; private: int line; diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 5e6f523b..99ae9f1b 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -28,7 +28,7 @@ #include "GCodeBuffer/GCodeBuffer.h" #include "GCodeQueue.h" #include "Heating/Heat.h" -#include "Heating/HeaterProtection.h" +//#include "Heating/HeaterProtection.h" #include "Platform.h" #include "Movement/Move.h" #include "Scanner.h" @@ -464,7 +464,7 @@ void GCodes::StartNextGCode(GCodeBuffer& gb, const StringRef& reply) } catch (GCodeException& e) { - e.GetMessage(reply, gb); + e.GetMessage(reply, &gb); HandleReply(gb, GCodeResult::error, reply.c_str()); gb.Init(); return; @@ -628,7 +628,7 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) } catch (GCodeException& e) { - e.GetMessage(reply, gb); + e.GetMessage(reply, &gb); HandleReply(gb, GCodeResult::error, reply.c_str()); gb.Init(); AbortPrint(gb); @@ -665,7 +665,7 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply) } catch (GCodeException& e) { - e.GetMessage(reply, gb); + e.GetMessage(reply, &gb); HandleReply(gb, GCodeResult::error, reply.c_str()); gb.Init(); AbortPrint(gb); @@ -3001,7 +3001,7 @@ GCodeResult GCodes::SetOrReportOffsets(GCodeBuffer &gb, const StringRef& reply) gb.GetFloatArray(active, hCount, true); for (size_t h = 0; h < hCount; ++h) { - tool->SetToolHeaterActiveTemperature(h, active[h]); + tool->SetToolHeaterActiveTemperature(h, active[h]); // may throw } } } @@ -3431,12 +3431,11 @@ void GCodes::HandleReply(GCodeBuffer& gb, OutputBuffer *reply) noexcept } } -void GCodes::SetToolHeaters(Tool *tool, float temperature, bool both) +void GCodes::SetToolHeaters(Tool *tool, float temperature, bool both) THROWS(GCodeException) { if (tool == nullptr) { - platform.Message(ErrorMessage, "Setting temperature: no tool selected\n"); - return; + throw GCodeException(-1, -1, "setting temperature: no tool selected\n"); } for (size_t h = 0; h < tool->HeaterCount(); h++) diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index c02ad69e..4490b649 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -304,7 +304,7 @@ private: bool Push(GCodeBuffer& gb, bool withinSameFile); // Push feedrate etc on the stack void Pop(GCodeBuffer& gb, bool withinSameFile); // Pop feedrate etc - void DisableDrives() noexcept; // Turn the motors off + void DisableDrives() noexcept; // Turn the motors off // Start saving GCodes in a file bool SendConfigToLine(); // Deal with M503 @@ -318,7 +318,7 @@ private: #endif GCodeResult ManageTool(GCodeBuffer& gb, const StringRef& reply); // Create a new tool definition - void SetToolHeaters(Tool *tool, float temperature, bool both); // Set all a tool's heaters to the temperature, for M104/M109 + void SetToolHeaters(Tool *tool, float temperature, bool both) THROWS(GCodeException); // Set all a tool's heaters to the temperature, for M104/M109 bool ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling, float tolerance) const noexcept; // Wait for the heaters associated with the specified tool to reach their set temperatures void GenerateTemperatureReport(const StringRef& reply) const noexcept; // Store a standard-format temperature report in reply @@ -338,10 +338,10 @@ private: GCodeResult LoadFilament(GCodeBuffer& gb, const StringRef& reply); // Load the specified filament into a tool GCodeResult UnloadFilament(GCodeBuffer& gb, const StringRef& reply); // Unload the current filament from a tool bool ChangeMicrostepping(size_t drive, unsigned int microsteps, bool interp, const StringRef& reply) const noexcept; // Change microstepping on the specified drive - void CheckTriggers() noexcept; // Check for and execute triggers - void CheckFilament() noexcept; // Check for and respond to filament errors - void CheckHeaterFault() noexcept; // Check for and respond to a heater fault, returning true if we should exit - void DoEmergencyStop() noexcept; // Execute an emergency stop + void CheckTriggers() noexcept; // Check for and execute triggers + void CheckFilament() noexcept; // Check for and respond to filament errors + void CheckHeaterFault() noexcept; // Check for and respond to a heater fault, returning true if we should exit + void DoEmergencyStop() noexcept; // Execute an emergency stop void DoPause(GCodeBuffer& gb, PauseReason reason, const char *msg) noexcept // Pause the print pre(resourceOwners[movementResource] = &gb); diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index a5ce5967..2c8b7613 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -98,7 +98,7 @@ bool GCodes::ActOnCode(GCodeBuffer& gb, const StringRef& reply) } catch (const GCodeException& e) { - e.GetMessage(reply, gb); + e.GetMessage(reply, &gb); HandleReply(gb, GCodeResult::error, reply.c_str()); return true; } @@ -1353,7 +1353,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) // because any slicer that uses M109 doesn't understand that there are separate active and standby temperatures. if (simulationMode == 0) { - SetToolHeaters(applicableTool.Ptr(), temperature, true); + SetToolHeaters(applicableTool.Ptr(), temperature, true); // this may throw } Tool * const currentTool = reprap.GetCurrentTool(); @@ -1732,7 +1732,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) } else { - heat.SetActiveTemperature(currentHeater, temperature); + heat.SetActiveTemperature(currentHeater, temperature); // may throw result = heat.Activate(currentHeater, reply); } } @@ -1769,7 +1769,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) break; case 143: // Configure heater protection - result = reprap.GetHeat().SetHeaterProtection(gb, reply); + result = reprap.GetHeat().HandleM143(gb, reply); break; case 144: // Set bed to standby, or to active if S1 parameter given @@ -1829,7 +1829,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) break; // no target temperature given } - reprap.GetHeat().SetActiveTemperature(heater, temperature); + reprap.GetHeat().SetActiveTemperature(heater, temperature); // may throw result = reprap.GetHeat().Activate(heater, reply); if (cancelWait || reprap.GetHeat().HeaterAtSetTemperature(heater, waitWhenCooling, TEMPERATURE_CLOSE_ENOUGH)) { diff --git a/src/GPIO/GpioPorts.h b/src/GPIO/GpioPorts.h index b8290c28..30d5a074 100644 --- a/src/GPIO/GpioPorts.h +++ b/src/GPIO/GpioPorts.h @@ -25,6 +25,7 @@ public: boardAddress (CanId::MasterAddress), #endif currentState(false) { } + GpInputPort(const GpInputPort&) = delete; bool GetState() const noexcept; bool IsUnused() const noexcept; @@ -50,9 +51,13 @@ private: class GpOutputPort { public: + GpOutputPort() noexcept #if SUPPORT_CAN_EXPANSION - GpOutputPort() noexcept { boardAddress = CanId::MasterAddress; } + : boardAddress(CanId::MasterAddress) #endif + { } + + GpOutputPort(const GpOutputPort&) = delete; GCodeResult WriteAnalog(uint32_t gpioPortNumber, bool isServo, float pwm, const GCodeBuffer& gb, const StringRef& reply) const noexcept; GCodeResult Configure(uint32_t gpioNumber, bool isServo, GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException); diff --git a/src/Heating/FOPDT.cpp b/src/Heating/FOPDT.cpp index 95f4ec54..c2347cf1 100644 --- a/src/Heating/FOPDT.cpp +++ b/src/Heating/FOPDT.cpp @@ -138,7 +138,7 @@ void FopDt::CalcPidConstants() noexcept #if SUPPORT_CAN_EXPANSION -void FopDt::SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) noexcept +void FopDt::SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) const noexcept { msg.heater = heater; msg.gain = gain; diff --git a/src/Heating/FOPDT.h b/src/Heating/FOPDT.h index 0a7c033e..fa911f9c 100644 --- a/src/Heating/FOPDT.h +++ b/src/Heating/FOPDT.h @@ -65,7 +65,7 @@ public: #endif #if SUPPORT_CAN_EXPANSION - void SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) noexcept; + void SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) const noexcept; #endif private: diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index 914dbfde..f1d6e1ec 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -20,7 +20,7 @@ Licence: GPL #include "Heat.h" #include "LocalHeater.h" -#include "HeaterProtection.h" +#include "HeaterMonitor.h" #include "Platform.h" #include "RepRap.h" #include "Sensors/TemperatureSensor.h" @@ -67,13 +67,6 @@ constexpr ObjectModelArrayDescriptor Heat::heatersArrayDescriptor = [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->heaters[context.GetLastIndex()]); } }; -constexpr ObjectModelArrayDescriptor Heat::sensorsArrayDescriptor = -{ - &sensorsLock, - [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Heat*)self)->GetNumSensorsToReport(); }, - [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->FindSensor(context.GetLastIndex()).Ptr()); } -}; - // Macro to build a standard lambda function that includes the necessary type conversions #define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Heat, __VA_ARGS__) @@ -84,10 +77,9 @@ constexpr ObjectModelTableEntry Heat::objectModelTable[] = { "coldExtrudeTemperature", OBJECT_MODEL_FUNC(self->extrusionMinTemp, 1), ObjectModelEntryFlags::none}, { "coldRetractTemperature", OBJECT_MODEL_FUNC(self->retractionMinTemp, 1), ObjectModelEntryFlags::none}, { "heaters", OBJECT_MODEL_FUNC_NOSELF(&heatersArrayDescriptor), ObjectModelEntryFlags::live }, - { "sensors", OBJECT_MODEL_FUNC_NOSELF(&sensorsArrayDescriptor), ObjectModelEntryFlags::live }, }; -constexpr uint8_t Heat::objectModelTableDescriptor[] = { 1, 4 }; +constexpr uint8_t Heat::objectModelTableDescriptor[] = { 1, 3 }; DEFINE_GET_OBJECT_MODEL_TABLE(Heat) @@ -112,11 +104,6 @@ Heat::Heat() noexcept h = -1; } - for (size_t index : ARRAY_INDICES(heaterProtections)) - { - heaterProtections[index] = new HeaterProtection(index); - } - for (Heater*& h : heaters) { h = nullptr; @@ -142,58 +129,7 @@ GCodeResult Heat::SetOrReportHeaterModel(GCodeBuffer& gb, const StringRef& reply 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 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; - } - } - 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.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); - } - } - return GCodeResult::ok; + return h->SetOrReportModel(heater, gb, reply); } reply.printf("Heater %u not found", heater); @@ -299,15 +235,6 @@ void Heat::ResetHeaterModels() noexcept void Heat::Init() noexcept { - // Initialise the heater protection items first - for (size_t index : ARRAY_INDICES(heaterProtections)) - { - HeaterProtection * const prot = heaterProtections[index]; - - const float tempLimit = (IsBedOrChamberHeater(index)) ? DefaultBedTemperatureLimit : DefaultHotEndTemperatureLimit; - prot->Init(tempLimit); - } - extrusionMinTemp = HOT_ENOUGH_TO_EXTRUDE; retractionMinTemp = HOT_ENOUGH_TO_RETRACT; coldExtrude = false; @@ -635,12 +562,21 @@ HeaterStatus Heat::GetStatus(int heater) const noexcept void Heat::SetBedHeater(size_t index, int heater) noexcept { - const auto h = FindHeater(bedHeaters[index]); - if (h.IsNotNull()) { - h->SwitchOff(); + const auto h = FindHeater(bedHeaters[index]); + if (h.IsNotNull()) + { + h->SwitchOff(); + } } bedHeaters[index] = heater; + { + const auto h = FindHeater(bedHeaters[index]); + if (h.IsNotNull()) + { + h->SetDefaultMonitors(); + } + } } bool Heat::IsBedHeater(int heater) const noexcept @@ -657,12 +593,19 @@ bool Heat::IsBedHeater(int heater) const noexcept void Heat::SetChamberHeater(size_t index, int heater) noexcept { + { + const auto h = FindHeater(chamberHeaters[index]); + if (h.IsNotNull()) + { + h->SwitchOff(); + } + } + chamberHeaters[index] = heater; const auto h = FindHeater(chamberHeaters[index]); if (h.IsNotNull()) { - h->SwitchOff(); + h->SetDefaultMonitors(); } - chamberHeaters[index] = heater; } bool Heat::IsChamberHeater(int heater) const noexcept @@ -677,12 +620,12 @@ bool Heat::IsChamberHeater(int heater) const noexcept return false; } -void Heat::SetActiveTemperature(int heater, float t) noexcept +void Heat::SetTemperature(int heater, float t, bool activeNotStandby) THROWS(GCodeException) { const auto h = FindHeater(heater); if (h.IsNotNull()) { - h->SetActiveTemperature(t); + h->SetTemperature(t, activeNotStandby); } } @@ -692,15 +635,6 @@ float Heat::GetActiveTemperature(int heater) const noexcept return (h.IsNull()) ? ABS_ZERO : h->GetActiveTemperature(); } -void Heat::SetStandbyTemperature(int heater, float t) noexcept -{ - const auto h = FindHeater(heater); - if (h.IsNotNull()) - { - h->SetStandbyTemperature(t); - } -} - float Heat::GetStandbyTemperature(int heater) const noexcept { const auto h = FindHeater(heater); @@ -709,42 +643,14 @@ float Heat::GetStandbyTemperature(int heater) const noexcept float Heat::GetHighestTemperatureLimit(int heater) const noexcept { - float limit = BadErrorTemperature; - if (heater >= 0 && heater < (int)MaxHeaters) - { - for (const HeaterProtection *prot : heaterProtections) - { - if (prot->GetHeater() == heater && prot->GetTrigger() == HeaterProtectionTrigger::TemperatureExceeded) - { - const float t = prot->GetTemperatureLimit(); - if (limit == BadErrorTemperature || t > limit) - { - limit = t; - } - } - } - } - return limit; + const auto h = FindHeater(heater); + return (h.IsNull()) ? BadErrorTemperature : h->GetHighestTemperatureLimit(); } float Heat::GetLowestTemperatureLimit(int heater) const noexcept { - float limit = ABS_ZERO; - if (heater >= 0 && heater < (int)MaxHeaters) - { - for (const HeaterProtection *prot : heaterProtections) - { - if (prot->GetHeater() == heater && prot->GetTrigger() == HeaterProtectionTrigger::TemperatureTooLow) - { - const float t = prot->GetTemperatureLimit(); - if (limit == ABS_ZERO || t < limit) - { - limit = t; - } - } - } - } - return limit; + const auto h = FindHeater(heater); + return (h.IsNull()) ? ABS_ZERO : h->GetLowestTemperatureLimit(); } // Get the current temperature of a real or virtual heater @@ -831,14 +737,15 @@ bool Heat::IsBedOrChamberHeater(int heater) const noexcept float Heat::GetHighestTemperatureLimit() const noexcept { float limit = ABS_ZERO; - for (HeaterProtection *prot : heaterProtections) + ReadLocker lock(heatersLock); + for (const Heater *h : heaters) { - if (prot->GetHeater() >= 0 && prot->GetTrigger() == HeaterProtectionTrigger::TemperatureExceeded) + if (h != nullptr) { - const float t = prot->GetTemperatureLimit(); - if (t > limit) + const float tlimit = h->GetHighestTemperatureLimit(); + if (tlimit > limit) { - limit = t; + limit = tlimit; } } } @@ -903,11 +810,7 @@ GCodeResult Heat::TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS(GCo gb.MustSee('S'); const float temperature = gb.GetFValue(); const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : h->GetModel().GetMaxPwm(); - if (!h->CheckGood()) - { - reply.copy("Heater is not ready to perform PID auto-tuning"); - } - else if (maxPwm < 0.1 || maxPwm > 1.0) + if (maxPwm < 0.1 || maxPwm > 1.0) { reply.copy("Invalid PWM value"); } @@ -916,8 +819,7 @@ GCodeResult Heat::TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS(GCo if (heaterBeingTuned == -1) { heaterBeingTuned = (int8_t)heater; - h->StartAutoTune(temperature, maxPwm, reply); - return GCodeResult::ok; + return h->StartAutoTune(temperature, maxPwm, reply); } else { @@ -1042,164 +944,17 @@ const char *Heat::GetHeaterSensorName(size_t heater) const noexcept } // Configure heater protection (M143). Returns true if an error occurred -GCodeResult Heat::SetHeaterProtection(GCodeBuffer& gb, const StringRef& reply) +GCodeResult Heat::HandleM143(GCodeBuffer& gb, const StringRef& reply) { - WriteLocker lock(heatersLock); - - bool seen = false; - int32_t heaterNumber = 1; // default to extruder 1 if no heater number provided - gb.TryGetIValue('H', heaterNumber, seen); - const int index = (gb.Seen('P')) ? gb.GetIValue() : heaterNumber; - - if ( index < 0 - || (index >= (int)MaxHeaters && index < (int)FirstExtraHeaterProtection) - || index >= (int)(FirstExtraHeaterProtection + MaxExtraHeaterProtections) - ) + const size_t heaterNumber = (gb.Seen('H')) ? gb.GetLimitedUIValue('H', MaxHeaters) : 1; + const auto h = FindHeater(heaterNumber); + if (h.IsNull()) { - reply.printf("Invalid heater protection item '%d'", index); + reply.printf("Heater %u does not exist", heaterNumber); return GCodeResult::error; } - HeaterProtection &item = (index >= (int)FirstExtraHeaterProtection) - ? *heaterProtections[index - FirstExtraHeaterProtection + MaxHeaters] - : *heaterProtections[index]; - // Set heater to control - if (seen && heaterNumber != item.GetHeater()) - { - const int oldHeaterNumber = item.GetHeater(); - item.SetHeater(heaterNumber); - UpdateHeaterProtection(oldHeaterNumber); - UpdateHeaterProtection(heaterNumber); - } - - // Set sensor that supervises the heater - if (gb.Seen('X')) - { - item.SetSensorNumber(gb.GetIValue()); - seen = true; - } - - // Set trigger action - if (gb.Seen('A')) - { - const int action = gb.GetIValue(); - if (action < 0 || action > (int)MaxHeaterProtectionAction) - { - reply.printf("Invalid heater protection action '%d'", action); - } - - seen = true; - item.SetAction(static_cast<HeaterProtectionAction>(action)); - } - - // Set trigger condition - if (gb.Seen('C')) - { - const int trigger = gb.GetIValue(); - if (trigger < 0 || trigger > (int)MaxHeaterProtectionTrigger) - { - reply.printf("Invalid heater protection trigger '%d'", trigger); - } - - seen = true; - item.SetTrigger(static_cast<HeaterProtectionTrigger>(trigger)); - } - - // Set temperature limit - if (gb.Seen('S')) - { - const float limit = gb.GetFValue(); - if (limit <= BadLowTemperature || limit >= BadErrorTemperature) - { - reply.copy("Invalid temperature limit"); - return GCodeResult::error; - } - - seen = true; - item.SetTemperatureLimit(limit); - } - - // Report current parameters - if (!seen) - { - if (item.GetHeater() < 0) - { - reply.printf("Temperature protection item %d is not configured", index); - } - else - { - const char *actionString, *triggerString; - switch (item.GetAction()) - { - case HeaterProtectionAction::GenerateFault: - actionString = "generate a heater fault"; - break; - case HeaterProtectionAction::PermanentSwitchOff: - actionString = "permanently switch off"; - break; - case HeaterProtectionAction::TemporarySwitchOff: - actionString = "temporarily switch off"; - break; - default: - actionString = "(undefined)"; - break; - } - - switch (item.GetTrigger()) - { - case HeaterProtectionTrigger::TemperatureExceeded: - triggerString = "exceeds"; - break; - case HeaterProtectionTrigger::TemperatureTooLow: - triggerString = "falls below"; - break; - default: - triggerString = "(undefined)"; - break; - } - - reply.printf("Temperature protection item %d is configured for heater %d and uses sensor %d to %s if the temperature %s %.1f" DEGREE_SYMBOL "C", - index, item.GetHeater(), item.GetSensorNumber(), actionString, triggerString, (double)item.GetTemperatureLimit()); - } - } - - return GCodeResult::ok; -} - -// Updates the PIDs and HeaterProtection items after a heater change. Caller must already have a write lock on the heaters. -void Heat::UpdateHeaterProtection(int heaterNumber) noexcept -{ - auto h = FindHeater(heaterNumber); - if (h.IsNotNull()) - { - // Rebuild linked lists - h->SetHeaterProtection(nullptr); - HeaterProtection *firstProtectionItem = nullptr; - HeaterProtection *lastElementInList = nullptr; - for (HeaterProtection *prot : heaterProtections) - { - if (prot->GetHeater() == heaterNumber) - { - if (firstProtectionItem == nullptr) - { - firstProtectionItem = prot; - prot->SetNext(nullptr); - } - else if (lastElementInList == nullptr) - { - firstProtectionItem->SetNext(prot); - lastElementInList = prot; - } - else - { - lastElementInList->SetNext(prot); - lastElementInList = prot; - } - } - } - - h->SetHeaterProtection(firstProtectionItem); - } + return h->ConfigureMonitor(gb, reply); } // Get the temperature of a sensor diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h index 6878bb8e..3789a721 100644 --- a/src/Heating/Heat.h +++ b/src/Heating/Heat.h @@ -34,7 +34,7 @@ Licence: GPL #include <RTOSIface/RTOSIface.h> class TemperatureSensor; -class HeaterProtection; +class HeaterMonitor; class GCodeBuffer; class CanMessageSensorTemperatures; class CanMessageHeatersStatus; @@ -43,6 +43,7 @@ class Heat INHERIT_OBJECT_MODEL { public: Heat() noexcept; + Heat(const Heat&) = delete; // Methods that don't relate to a particular heater void HeaterTask() noexcept; @@ -79,9 +80,7 @@ public: GCodeResult TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS_GCODE_EXCEPTION; GCodeResult ConfigureSensor(GCodeBuffer& gb, const StringRef& reply) THROWS_GCODE_EXCEPTION; // Create a sensor or change the parameters for an existing sensor GCodeResult SetPidParameters(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) THROWS_GCODE_EXCEPTION; // Set the P/I/D parameters for a heater - GCodeResult SetHeaterProtection(GCodeBuffer &gb, const StringRef &reply) THROWS_GCODE_EXCEPTION; // Configure heater protection (M143) - - void UpdateHeaterProtection(int heaterNumber) noexcept; // Updates the PIDs and HeaterProtection items when a heater is remapped + GCodeResult HandleM143(GCodeBuffer &gb, const StringRef &reply) THROWS_GCODE_EXCEPTION; // Configure heater protection (M143) void SensorsTask() noexcept; static void EnsureSensorsTask() noexcept; @@ -119,16 +118,16 @@ public: float GetStandbyTemperature(int heater) const noexcept; float GetHighestTemperatureLimit(int heater) const noexcept; float GetLowestTemperatureLimit(int heater) const noexcept; - float GetHeaterTemperature(int heater) const noexcept; // Get the current temperature of a heater float GetTargetTemperature(int heater) const noexcept; // Get the target temperature + float GetHeaterTemperature(int heater) const noexcept; // Get the current temperature of a heater HeaterStatus GetStatus(int heater) const noexcept; // Get the off/standby/active status bool HeaterAtSetTemperature(int heater, bool waitWhenCooling, float tolerance) const noexcept; GCodeResult ConfigureHeater(size_t heater, GCodeBuffer& gb, const StringRef& reply); GCodeResult ConfigureHeaterMonitoring(size_t heater, GCodeBuffer& gb, const StringRef& reply); - void SetActiveTemperature(int heater, float t) noexcept; - void SetStandbyTemperature(int heater, float t) noexcept; + void SetActiveTemperature(int heater, float t) THROWS(GCodeException) { SetTemperature(heater, t, true); } + void SetStandbyTemperature(int heater, float t) THROWS(GCodeException) { SetTemperature(heater, t, false); } GCodeResult Activate(int heater, const StringRef& reply) noexcept; // Turn on a heater void Standby(int heater, const Tool* tool) noexcept; // Set a heater to standby void SwitchOff(int heater) noexcept; // Turn off a specific heater @@ -143,24 +142,22 @@ public: void ProcessRemoteHeatersReport(CanAddress src, const CanMessageHeatersStatus& msg) noexcept; #endif + static ReadWriteLock sensorsLock; // needs to be public so that the OMT in EndstopsManager can lock it + protected: DECLARE_OBJECT_MODEL OBJECT_MODEL_ARRAY(heaters) - OBJECT_MODEL_ARRAY(sensors) private: - Heat(const Heat&) = delete; // Private copy constructor to prevent copying - ReadLockedPointer<Heater> FindHeater(int heater) const noexcept; void DeleteSensor(unsigned int sn) noexcept; void InsertSensor(TemperatureSensor *newSensor) noexcept; + void SetTemperature(int heater, float t, bool activeNotStandby) THROWS(GCodeException); static ReadWriteLock heatersLock; - static ReadWriteLock sensorsLock; uint8_t volatile sensorCount; TemperatureSensor * volatile sensorsRoot; // The sensor list - HeaterProtection *heaterProtections[MaxHeaters + MaxExtraHeaterProtections]; // Heater protection instances to guarantee legal heater temperature ranges Heater* heaters[MaxHeaters]; // A local or remote heater const Tool* lastStandbyTools[MaxHeaters]; // The last tool that caused the corresponding heater to be set to standby diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp index a3a64add..24b10302 100644 --- a/src/Heating/Heater.cpp +++ b/src/Heating/Heater.cpp @@ -9,8 +9,10 @@ #include "RepRap.h" #include "Platform.h" #include "Heat.h" -#include "HeaterProtection.h" +#include "HeaterMonitor.h" #include "Sensors/TemperatureSensor.h" +#include <GCodes/GCodeBuffer/GCodeBuffer.h> +#include <GCodes/GCodeException.h> #if SUPPORT_OBJECT_MODEL @@ -41,19 +43,100 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Heater) Heater::Heater(unsigned int num) noexcept : heaterNumber(num), sensorNumber(-1), activeTemperature(0.0), standbyTemperature(0.0), maxTempExcursion(DefaultMaxTempExcursion), maxHeatingFaultTime(DefaultMaxHeatingFaultTime), - heaterProtection(nullptr), active(false) + active(false) { } Heater::~Heater() noexcept { + for (HeaterMonitor& h : monitors) + { + h.Disable(); + } +} + +void Heater::SetSensorNumber(int sn) noexcept +{ + if (sn != sensorNumber) + { + sensorNumber = sn; + SetDefaultMonitors(); + } +} + +void Heater::SetDefaultMonitors() noexcept +{ + for (HeaterMonitor& h : monitors) + { + h.Disable(); + } + + if (sensorNumber >= 0 && sensorNumber < (int)MaxSensors) + { + const float limit = (reprap.GetHeat().IsBedOrChamberHeater(heaterNumber)) ? DefaultBedTemperatureLimit : DefaultHotEndTemperatureLimit; + monitors[0].Set(sensorNumber, limit, HeaterMonitorAction::GenerateFault, HeaterMonitorTrigger::TemperatureExceeded); + } +} + +GCodeResult Heater::SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) noexcept +{ + 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 = SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply); + if (rslt != GCodeResult::ok) + { + return rslt; + } + } + 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.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); + } + } + return GCodeResult::ok; } // Set the process model returning true if successful GCodeResult Heater::SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept { - const float temperatureLimit = GetHighestTemperatureLimit(); - const bool rslt = model.SetParameters(gain, tc, td, maxPwm, temperatureLimit, voltage, usePid, inverted); + const bool rslt = model.SetParameters(gain, tc, td, maxPwm, GetHighestTemperatureLimit(), voltage, usePid, inverted); if (rslt) { if (model.IsEnabled()) @@ -64,7 +147,7 @@ GCodeResult Heater::SetModel(float gain, float tc, float td, float maxPwm, float return rslt; } const float predictedMaxTemp = gain + NormalAmbientTemperature; - const float noWarnTemp = (temperatureLimit - NormalAmbientTemperature) * 1.5 + 50.0; // allow 50% extra power plus enough for an extra 50C + const float noWarnTemp = (GetHighestTemperatureLimit() - NormalAmbientTemperature) * 1.5 + 50.0; // allow 50% extra power plus enough for an extra 50C if (predictedMaxTemp > noWarnTemp) { reply.printf("heater %u appears to be over-powered. If left on at full power, its temperature is predicted to reach %dC.\n", @@ -90,6 +173,88 @@ GCodeResult Heater::SetFaultDetectionParameters(float pMaxTempExcursion, float p return UpdateFaultDetectionParameters(reply); } +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; + + const bool seenSensor = gb.Seen('T'); + const int monitoringSensor = (seenSensor) ? gb.GetLimitedUIValue('T', MaxSensors) : GetSensorNumber(); + + const bool seenAction = gb.Seen('A'); + const HeaterMonitorAction action = (seenAction) + ? static_cast<HeaterMonitorAction>(gb.GetLimitedUIValue('A', (unsigned int)MaxHeaterMonitorAction + 1)) + : HeaterMonitorAction::GenerateFault; + + const bool seenCondition = gb.Seen('C'); + const HeaterMonitorTrigger trigger = (seenCondition) + ? static_cast<HeaterMonitorTrigger>(gb.GetLimitedIValue('C', -1, (int)MaxHeaterMonitorTrigger)) + : HeaterMonitorTrigger::TemperatureExceeded; + + const bool seenLimit = gb.Seen('S'); + const float limit = (seenLimit) ? gb.GetFValue() : monitors[index].GetTemperatureLimit(); + if (limit <= BadLowTemperature || limit >= BadErrorTemperature) + { + reply.copy("Invalid temperature limit"); + return GCodeResult::error; + } + + if (seenSensor || seenLimit || seenAction || seenCondition) + { + monitors[index].Set(monitoringSensor, limit, action, trigger); + return UpdateHeaterMonitors(reply); + } + + // Else we are reporting on one or all of the monitors + if (seenP) + { + monitors[index].Report(heaterNumber, index, reply); + } + else + { + for (size_t i = 0; i < MaxMonitorsPerHeater; ++i) + { + monitors[i].Report(heaterNumber, i, reply); + } + } + return GCodeResult::ok; +} + +float Heater::GetHighestTemperatureLimit() const noexcept +{ + float limit = BadErrorTemperature; + for (const HeaterMonitor& prot : monitors) + { + if (prot.GetTrigger() == HeaterMonitorTrigger::TemperatureExceeded) + { + const float t = prot.GetTemperatureLimit(); + if (limit == BadErrorTemperature || t > limit) + { + limit = t; + } + } + } + return limit; +} + +float Heater::GetLowestTemperatureLimit() const noexcept +{ + float limit = ABS_ZERO; + for (const HeaterMonitor& prot : monitors) + { + if (prot.GetTrigger() == HeaterMonitorTrigger::TemperatureTooLow) + { + const float t = prot.GetTemperatureLimit(); + if (limit == ABS_ZERO || t < limit) + { + limit = t; + } + } + } + return limit; +} + HeaterStatus Heater::GetStatus() const noexcept { const HeaterMode mode = GetMode(); @@ -128,19 +293,19 @@ void Heater::Standby() noexcept } } -void Heater::SetActiveTemperature(float t) noexcept +void Heater::SetTemperature(float t, bool activeNotStandby) THROWS(GCodeException) { if (t > GetHighestTemperatureLimit()) { - reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too high for heater %u\n", (double)t, GetHeaterNumber()); + throw GCodeException(-1, -1, "Temperature too high for heater %" PRIu32, (uint32_t)GetHeaterNumber()); } else if (t < GetLowestTemperatureLimit()) { - reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too low for heater %u\n", (double)t, GetHeaterNumber()); + throw GCodeException(-1, -1, "Temperature too low for heater %" PRIu32, (uint32_t)GetHeaterNumber()); } else { - activeTemperature = t; + ((activeNotStandby) ? activeTemperature : standbyTemperature) = t; if (GetMode() > HeaterMode::suspended && active) { String<1> dummy; @@ -149,66 +314,9 @@ void Heater::SetActiveTemperature(float t) noexcept } } -void Heater::SetStandbyTemperature(float t) noexcept -{ - if (t > GetHighestTemperatureLimit()) - { - reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too high for heater %u\n", (double)t, GetHeaterNumber()); - } - else if (t < GetLowestTemperatureLimit()) - { - reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too low for heater %u\n", (double)t, GetHeaterNumber()); - } - else - { - standbyTemperature = t; - if (GetMode() > HeaterMode::suspended && !active) - { - String<1> dummy; - (void)SwitchOn(dummy.GetRef()); - } - } -} - -// Get the highest temperature limit -float Heater::GetHighestTemperatureLimit() const noexcept -{ - return reprap.GetHeat().GetHighestTemperatureLimit(GetHeaterNumber()); -} - -// Get the lowest temperature limit -float Heater::GetLowestTemperatureLimit() const noexcept -{ - return reprap.GetHeat().GetLowestTemperatureLimit(GetHeaterNumber()); -} - -void Heater::SetHeaterProtection(HeaterProtection *h) noexcept -{ - heaterProtection = h; -} - -// Check heater protection elements and return true if everything is good -bool Heater::CheckProtection() const noexcept -{ - for (HeaterProtection *prot = heaterProtection; prot != nullptr; prot = prot->Next()) - { - if (!prot->Check()) - { - // Something is not right - return false; - } - } - return true; -} - -bool Heater::CheckGood() const noexcept -{ - return GetMode() != HeaterMode::fault && CheckProtection(); -} - void Heater::SetModelDefaults() noexcept { - if (reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber())) + if (reprap.GetHeat().IsBedOrChamberHeater(heaterNumber)) { model.SetParameters(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime, 1.0, DefaultBedTemperatureLimit, 0.0, false, false); } diff --git a/src/Heating/Heater.h b/src/Heating/Heater.h index c237195e..5108d548 100644 --- a/src/Heating/Heater.h +++ b/src/Heating/Heater.h @@ -11,6 +11,7 @@ #include <RepRapFirmware.h> #include <NamedEnum.h> #include "FOPDT.h" +#include "HeaterMonitor.h" #include "GCodes/GCodeResult.h" #include <ObjectModel/ObjectModel.h> @@ -18,7 +19,7 @@ # include "CanId.h" #endif -class HeaterProtection; +class HeaterMonitor; struct CanHeaterReport; // Enumeration to describe the status of a heater. Note that the web interface returns the numerical values, so don't change them. @@ -29,6 +30,7 @@ class Heater INHERIT_OBJECT_MODEL public: Heater(unsigned int num) noexcept; virtual ~Heater() noexcept; + Heater(const Heater&) = delete; // Configuration methods virtual GCodeResult ConfigurePortAndSensor(const char *portName, PwmFrequency freq, unsigned int sensorNumber, const StringRef& reply) = 0; @@ -40,36 +42,35 @@ public: virtual GCodeResult ResetFault(const StringRef& reply) noexcept = 0; // Reset a fault condition - only call this if you know what you are doing virtual void SwitchOff() noexcept = 0; virtual void Spin() noexcept = 0; - virtual void StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept = 0; // Start an auto tune cycle for this PID + virtual GCodeResult StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept = 0; // Start an auto tune cycle for this PID virtual void GetAutoTuneStatus(const StringRef& reply) const = 0; // Get the auto tune status or last result - virtual void Suspend(bool sus) noexcept = 0; // Suspend the heater to conserve power or while doing Z probing + virtual void Suspend(bool sus) noexcept = 0; // Suspend the heater to conserve power or while doing Z probing virtual float GetAccumulator() const noexcept = 0; // Get the inertial term accumulator #if SUPPORT_CAN_EXPANSION virtual void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept = 0; #endif - HeaterStatus GetStatus() const noexcept; // Get the status of the heater + HeaterStatus GetStatus() const noexcept; // Get the status of the heater unsigned int GetHeaterNumber() const noexcept { return heaterNumber; } const char *GetSensorName() const noexcept; // Get the name of the sensor for this heater, or nullptr if it hasn't been named - void SetActiveTemperature(float t) noexcept; + void SetTemperature(float t, bool activeNotStandby) THROWS(GCodeException); float GetActiveTemperature() const noexcept { return activeTemperature; } - void SetStandbyTemperature(float t) noexcept; float GetStandbyTemperature() const noexcept { return standbyTemperature; } GCodeResult Activate(const StringRef& reply) noexcept; // Switch from idle to active - void Standby() noexcept; // Switch from active to idle + void Standby() noexcept; // Switch from active to idle void GetFaultDetectionParameters(float& pMaxTempExcursion, float& pMaxFaultTime) const noexcept { pMaxTempExcursion = maxTempExcursion; pMaxFaultTime = maxHeatingFaultTime; } - GCodeResult SetFaultDetectionParameters(float pMaxTempExcursion, float pMaxFaultTime, const StringRef& reply) noexcept; - float GetHighestTemperatureLimit() const noexcept; // Get the highest temperature limit + GCodeResult ConfigureMonitor(GCodeBuffer &gb, const StringRef &reply) THROWS(GCodeException); + + float GetHighestTemperatureLimit() const noexcept; float GetLowestTemperatureLimit() const noexcept; // Get the lowest temperature limit - void SetHeaterProtection(HeaterProtection *h) noexcept; - const FopDt& GetModel() const noexcept { return model; } // Get the process model - GCodeResult SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // Set the process model + const FopDt& GetModel() const noexcept { return model; } // Get the process model + GCodeResult SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) noexcept; void SetModelDefaults() noexcept; bool IsHeaterEnabled() const noexcept // Is this heater enabled? @@ -78,7 +79,7 @@ public: void SetM301PidParameters(const M301PidParameters& params) noexcept { model.SetM301PidParameters(params); } - bool CheckGood() const noexcept; + void SetDefaultMonitors() noexcept; protected: DECLARE_OBJECT_MODEL @@ -102,31 +103,31 @@ protected: lastTuningMode = tuning3 }; +protected: virtual void ResetHeater() noexcept = 0; virtual HeaterMode GetMode() const noexcept = 0; virtual GCodeResult SwitchOn(const StringRef& reply) noexcept = 0; virtual GCodeResult UpdateModel(const StringRef& reply) noexcept = 0; virtual GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept = 0; + virtual GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept = 0; int GetSensorNumber() const noexcept { return sensorNumber; } - void SetSensorNumber(int sn) noexcept { sensorNumber = sn; } + void SetSensorNumber(int sn) noexcept; float GetMaxTemperatureExcursion() const noexcept { return maxTempExcursion; } float GetMaxHeatingFaultTime() const noexcept { return maxHeatingFaultTime; } float GetTargetTemperature() const noexcept { return (active) ? activeTemperature : standbyTemperature; } - HeaterProtection *GetHeaterProtections() const noexcept { return heaterProtection; } + GCodeResult SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // Set the process model - FopDt model; + HeaterMonitor monitors[MaxMonitorsPerHeater]; // embedding them in the Heater uses less memory than dynamic allocation private: - bool CheckProtection() const noexcept; // Check heater protection elements and return true if everything is good - + FopDt model; unsigned int heaterNumber; int sensorNumber; // the sensor number used by this heater float activeTemperature; // The required active temperature float standbyTemperature; // The required standby temperature float maxTempExcursion; // The maximum temperature excursion permitted while maintaining the setpoint float maxHeatingFaultTime; // How long a heater fault is permitted to persist before a heater fault is raised - HeaterProtection *heaterProtection; // The first element of assigned heater protection items bool active; // Are we active or standby? }; diff --git a/src/Heating/HeaterMonitor.cpp b/src/Heating/HeaterMonitor.cpp new file mode 100644 index 00000000..bb1874b3 --- /dev/null +++ b/src/Heating/HeaterMonitor.cpp @@ -0,0 +1,99 @@ +/* + * HeaterProtection.cpp + * + * Created on: 16 Nov 2017 + * Author: Christian + */ + +#include "HeaterMonitor.h" + +#include <Platform.h> +#include <RepRap.h> +#include "Heat.h" + +HeaterMonitor::HeaterMonitor() noexcept + : sensorNumber(-1), trigger(HeaterMonitorTrigger::Disabled), badTemperatureCount(0) +{ +} + +// Check if any action needs to be taken. Returns true if everything is OK +bool HeaterMonitor::Check() noexcept +{ + if (sensorNumber >= 0 && trigger != HeaterMonitorTrigger::Disabled) + { + TemperatureError err; + const float temperature = reprap.GetHeat().GetSensorTemperature(sensorNumber, err); + + if (err != TemperatureError::success) + { + badTemperatureCount++; + if (badTemperatureCount > MaxBadTemperatureCount) + { + reprap.GetPlatform().MessageF(ErrorMessage, "Temperature reading error on sensor %d\n", sensorNumber); + return false; + } + } + else + { + badTemperatureCount = 0; + switch (trigger) + { + case HeaterMonitorTrigger::TemperatureExceeded: + return (temperature <= limit); + + case HeaterMonitorTrigger::TemperatureTooLow: + return (temperature >= limit); + + default: + break; + } + } + } + return true; +} + +// Append a report of this monitor to the string +void HeaterMonitor::Report(unsigned int heater, unsigned int index, const StringRef& reply) const noexcept +{ + reply.lcatf("Heater %d monitor %d ", heater, index); + if (trigger == HeaterMonitorTrigger::Disabled) + { + reply.cat("is disabled"); + } + else + { + const char *actionString, *triggerString; + switch (action) + { + case HeaterMonitorAction::GenerateFault: + actionString = "generate a heater fault"; + break; + case HeaterMonitorAction::PermanentSwitchOff: + actionString = "permanently switch off"; + break; + case HeaterMonitorAction::TemporarySwitchOff: + actionString = "temporarily switch off"; + break; + default: + actionString = "(undefined)"; + break; + } + + switch (trigger) + { + case HeaterMonitorTrigger::TemperatureExceeded: + triggerString = "exceeds"; + break; + case HeaterMonitorTrigger::TemperatureTooLow: + triggerString = "falls below"; + break; + default: + triggerString = "(undefined)"; + break; + } + + reply.catf("uses sensor %d to %s if the reading %s %.1f" DEGREE_SYMBOL "C", sensorNumber, actionString, triggerString, (double)limit); + } +} + +// End diff --git a/src/Heating/HeaterMonitor.h b/src/Heating/HeaterMonitor.h new file mode 100644 index 00000000..42bdac38 --- /dev/null +++ b/src/Heating/HeaterMonitor.h @@ -0,0 +1,81 @@ +/* + * HeaterMonitor.h + * + * Created on: 16 Nov 2017 + * Author: Christian + */ + +#ifndef HEATERMONITOR_H +#define HEATERMONITOR_H + +#include <RepRapFirmware.h> +#include <General/FreelistManager.h> + +// Condition of a heater monitor event +enum class HeaterMonitorTrigger : int8_t +{ + Disabled = -1, + TemperatureExceeded = 0, + TemperatureTooLow +}; + +const HeaterMonitorTrigger MaxHeaterMonitorTrigger = HeaterMonitorTrigger::TemperatureTooLow; + +// The action to trigger when the target condition is met +enum class HeaterMonitorAction : uint8_t +{ + GenerateFault = 0, + PermanentSwitchOff, + TemporarySwitchOff +}; + +const HeaterMonitorAction MaxHeaterMonitorAction = HeaterMonitorAction::TemporarySwitchOff; + +// A note about using this class. Its size is currently 8 bytes, and will be 12 bytes of object model support is added. +// - If we allocate them statically within the heater object, then assuming 3 per heater we need 24 bytes, or 36 bytes with OM support. +// - If we allocate them dynamically then there is an overhead of at least 8 bytes per object. All heaters have at least 2. +// So each heater needs 12 bytes for the pointers plus 32 bytes (without OM support) or 40 bytes (with OM support). Total 44 or 52 bytes. +// For unconfigured heaters, the cost is 24 or 36 bytes each using static allocation, or 4 bytes each using dynamic allocation. +// Summary: +// - Static allocation saves 20 bytes (no OM) or 16 bytes (with OM) per configured heater, if the 3rd heater monitor is not used (and more if it is used) +// - Dynamic allocation saves 20 bytes (no OM) or 32 bytes (with OM) per unconfigured heater +// For now we use static allocation, i.e. we embed the heater monitor in the heater object. +class HeaterMonitor +{ +public: + HeaterMonitor() noexcept; + + void Set(int sn, float lim, HeaterMonitorAction act, HeaterMonitorTrigger trig) noexcept; + void Disable() noexcept; + bool Check() noexcept; // Check if any action needs to be taken + + int GetSensorNumber() const noexcept { return sensorNumber; } // Get the supervisory sensor number + float GetTemperatureLimit() const noexcept { return limit; } // Get the temperature limit + HeaterMonitorAction GetAction() const noexcept { return action; } // Get the action to trigger when a temperature event occurs + HeaterMonitorTrigger GetTrigger() const noexcept { return trigger; } // Get the condition for a temperature event + + void Report(unsigned int heater, unsigned int index, const StringRef& reply) const noexcept; // Append a report of this monitor to the string + +private: + float limit; // temperature limit + int8_t sensorNumber; // the sensor that we use to monitor the heater + HeaterMonitorAction action; // what action we take of we detect a fault + HeaterMonitorTrigger trigger; // what is treated a fault + uint8_t badTemperatureCount; // how many consecutive sensor reading faults we have had +}; + +inline void HeaterMonitor::Set(int sn, float lim, HeaterMonitorAction act, HeaterMonitorTrigger trig) noexcept +{ + sensorNumber = sn; + limit = lim; + action = act; + trigger = trig; + badTemperatureCount = 0; +} + +inline void HeaterMonitor::Disable() noexcept +{ + trigger = HeaterMonitorTrigger::Disabled; +} + +#endif diff --git a/src/Heating/HeaterProtection.cpp b/src/Heating/HeaterProtection.cpp deleted file mode 100644 index f13911d0..00000000 --- a/src/Heating/HeaterProtection.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * HeaterProtection.cpp - * - * Created on: 16 Nov 2017 - * Author: Christian - */ - -#include "HeaterProtection.h" - -#include "Platform.h" -#include "RepRap.h" -#include "Heat.h" - - -HeaterProtection::HeaterProtection(size_t index) noexcept : next(nullptr) -{ - // By default each heater protection element is mapped to its corresponding heater. - // All other heater protection elements are unused and can be optionally assigned. - heater = (index >= MaxHeaters) ? -1 : (int8_t)index; - sensorNumber = -1; -} - -void HeaterProtection::Init(float tempLimit) noexcept -{ - next = nullptr; - limit = tempLimit; - action = HeaterProtectionAction::GenerateFault; - trigger = HeaterProtectionTrigger::TemperatureExceeded; - - badTemperatureCount = 0; -} - -// Check if any action needs to be taken. Returns true if everything is OK -bool HeaterProtection::Check() noexcept -{ - if (sensorNumber >= 0) - { - TemperatureError err; - const float temperature = reprap.GetHeat().GetSensorTemperature(sensorNumber, err); - - if (err != TemperatureError::success) - { - badTemperatureCount++; - if (badTemperatureCount > MaxBadTemperatureCount) - { - reprap.GetPlatform().MessageF(ErrorMessage, "Temperature reading error on sensor %d\n", sensorNumber); - return false; - } - } - else - { - badTemperatureCount = 0; - switch (trigger) - { - case HeaterProtectionTrigger::TemperatureExceeded: - return (temperature <= limit); - - case HeaterProtectionTrigger::TemperatureTooLow: - return (temperature >= limit); - } - } - } - return true; -} - -void HeaterProtection::SetHeater(int newHeater) noexcept -{ - heater = newHeater; -} - -// End diff --git a/src/Heating/HeaterProtection.h b/src/Heating/HeaterProtection.h deleted file mode 100644 index 19f5df36..00000000 --- a/src/Heating/HeaterProtection.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * HeaterProtection.h - * - * Created on: 16 Nov 2017 - * Author: Christian - */ - -#ifndef HEATERPROTECTION_H -#define HEATERPROTECTION_H - -#include "RepRapFirmware.h" - - -// Condition of a heater protection event -enum class HeaterProtectionTrigger : uint8_t -{ - TemperatureExceeded, - TemperatureTooLow -}; - -const HeaterProtectionTrigger MaxHeaterProtectionTrigger = HeaterProtectionTrigger::TemperatureTooLow; - -// The action to trigger when the target condition is met -enum class HeaterProtectionAction : uint8_t -{ - GenerateFault = 0, - PermanentSwitchOff, - TemporarySwitchOff -}; - -const HeaterProtectionAction MaxHeaterProtectionAction = HeaterProtectionAction::TemporarySwitchOff; - - -class Heat; - -class HeaterProtection -{ -public: - friend class Heat; - - HeaterProtection(size_t index) noexcept; - void Init(float tempLimit) noexcept; - - HeaterProtection *Next() const noexcept { return next; } - void SetNext(HeaterProtection *n) noexcept { next = n; } - - bool Check() noexcept; // Check if any action needs to be taken - - int GetHeater() const noexcept { return heater; } - void SetHeater(int newHeater) noexcept; // Set the heater to control - - int GetSensorNumber() const noexcept { return sensorNumber; } // Get the supervisory sensor number - void SetSensorNumber(int sn) noexcept; // Set the supervisory sensor number - - float GetTemperatureLimit() const noexcept { return limit; } // Get the temperature limit - void SetTemperatureLimit(float newLimit) noexcept; // Set the temperature limit - - HeaterProtectionAction GetAction() const noexcept { return action; } // Get the action to trigger when a temperature event occurs - void SetAction(HeaterProtectionAction newAction) noexcept; // Set the action to trigger when a temperature event occurs - - HeaterProtectionTrigger GetTrigger() const noexcept { return trigger; } // Get the condition for a temperature event - void SetTrigger(HeaterProtectionTrigger newTrigger) noexcept; // Set the condition for a temperature event - -private: - HeaterProtection *next; // link to next HeaterProtection item for the same heater - - float limit; // temperature limit - int heater; // number of the heater we are protecting - int sensorNumber; // the sensor that we use to monitor the heater - HeaterProtectionAction action; // what action we take of we detect a fault - HeaterProtectionTrigger trigger; // what is treated a fault - - size_t badTemperatureCount; // how many consecutive sensor reading faults we have had -}; - -inline void HeaterProtection::SetSensorNumber(int sn) noexcept -{ - sensorNumber = sn; -} - -inline void HeaterProtection::SetTemperatureLimit(float newLimit) noexcept -{ - limit = newLimit; -} - -inline void HeaterProtection::SetAction(HeaterProtectionAction newAction) noexcept -{ - action = newAction; -} - -inline void HeaterProtection::SetTrigger(HeaterProtectionTrigger newTrigger) noexcept -{ - trigger = newTrigger; -} - -#endif diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp index 5cb822e4..f4429f87 100644 --- a/src/Heating/LocalHeater.cpp +++ b/src/Heating/LocalHeater.cpp @@ -9,7 +9,7 @@ #include "GCodes/GCodes.h" #include "GCodes/GCodeBuffer/GCodeBuffer.h" #include "Heat.h" -#include "HeaterProtection.h" +#include "HeaterMonitor.h" #include "Platform.h" #include "RepRap.h" @@ -395,24 +395,24 @@ void LocalHeater::Spin() noexcept } // Verify that everything is operating in the required temperature range - for (HeaterProtection *prot = GetHeaterProtections(); prot != nullptr; prot = prot->Next()) + for (HeaterMonitor& prot : monitors) { - if (!prot->Check()) + if (!prot.Check()) { lastPwm = 0.0; - switch (prot->GetAction()) + switch (prot.GetAction()) { - case HeaterProtectionAction::GenerateFault: + case HeaterMonitorAction::GenerateFault: mode = HeaterMode::fault; reprap.GetGCodes().HandleHeaterFault(GetHeaterNumber()); - reprap.GetPlatform().MessageF(ErrorMessage, "Heating fault on heater %u\n", GetHeaterNumber()); + reprap.GetPlatform().MessageF(ErrorMessage, "Heating fault on heater %u: heater monitor was triggered\n", GetHeaterNumber()); break; - case HeaterProtectionAction::TemporarySwitchOff: + case HeaterMonitorAction::TemporarySwitchOff: // Do nothing, the PWM value has already been set above break; - case HeaterProtectionAction::PermanentSwitchOff: + case HeaterMonitorAction::PermanentSwitchOff: SwitchOff(); break; } @@ -474,41 +474,49 @@ float LocalHeater::GetExpectedHeatingRate() const noexcept } // Auto tune this PID -void LocalHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept +GCodeResult LocalHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept { // Starting an auto tune if (!GetModel().IsEnabled()) { - reply.printf("Error: heater %u cannot be auto tuned while it is disabled", GetHeaterNumber()); + reply.printf("heater %u cannot be auto tuned while it is disabled", GetHeaterNumber()); + return GCodeResult::error; } - else if (lastPwm > 0.0 || GetAveragePWM() > 0.02) + + if (lastPwm > 0.0 || GetAveragePWM() > 0.02) { - reply.printf("Error: heater %u must be off and cold before auto tuning it", GetHeaterNumber()); + reply.printf("heater %u must be off and cold before auto tuning it", GetHeaterNumber()); + return GCodeResult::error; } - else + + const float limit = GetHighestTemperatureLimit(); + if (targetTemp > limit) { - const TemperatureError err = ReadTemperature(); - if (err != TemperatureError::success) - { - reply.printf("Error: heater %u reported error '%s' at start of auto tuning", GetHeaterNumber(), TemperatureErrorString(err)); - } - else - { - mode = HeaterMode::tuning0; - tuningReadingsTaken = 0; - tuned = false; // assume failure - - // We don't normally allow dynamic memory allocation when running. However, auto tuning is rarely done and it - // would be wasteful to allocate a permanent array just in case we are going to run it, so we make an exception here. - tuningTempReadings = new float[MaxTuningTempReadings]; - tuningTempReadings[0] = temperature; - tuningReadingInterval = HeatSampleIntervalMillis; - tuningPwm = maxPwm; - tuningTargetTemp = targetTemp; - reply.printf("Auto tuning heater %u using target temperature %.1f" DEGREE_SYMBOL "C and PWM %.2f - do not leave printer unattended", - GetHeaterNumber(), (double)targetTemp, (double)maxPwm); - } + reply.printf("heater %u target temperature must be no hiher than the temperature limit for this heater (%.1fC)", GetHeaterNumber(), (double)limit); + return GCodeResult::error; + } + + const TemperatureError err = ReadTemperature(); + if (err != TemperatureError::success) + { + reply.printf("heater %u reported error '%s' at start of auto tuning", GetHeaterNumber(), TemperatureErrorString(err)); + return GCodeResult::error; } + + mode = HeaterMode::tuning0; + tuningReadingsTaken = 0; + tuned = false; // assume failure + + // We don't normally allow dynamic memory allocation when running. However, auto tuning is rarely done and it + // would be wasteful to allocate a permanent array just in case we are going to run it, so we make an exception here. + tuningTempReadings = new float[MaxTuningTempReadings]; + tuningTempReadings[0] = temperature; + tuningReadingInterval = HeatSampleIntervalMillis; + tuningPwm = maxPwm; + tuningTargetTemp = targetTemp; + reply.printf("Auto tuning heater %u using target temperature %.1f" DEGREE_SYMBOL "C and PWM %.2f - do not leave printer unattended", + GetHeaterNumber(), (double)targetTemp, (double)maxPwm); + return GCodeResult::ok; } // Get the auto tune status or last result diff --git a/src/Heating/LocalHeater.h b/src/Heating/LocalHeater.h index 3ad48f5a..c4e283ef 100644 --- a/src/Heating/LocalHeater.h +++ b/src/Heating/LocalHeater.h @@ -18,7 +18,7 @@ #include "Hardware/IoPorts.h" #include "GCodes/GCodeResult.h" -class HeaterProtection; +class HeaterMonitor; class LocalHeater : public Heater { @@ -38,7 +38,7 @@ public: float GetTemperature() const noexcept override; // Get the current temperature float GetAveragePWM() const noexcept override; // Return the running average PWM to the heater. Answer is a fraction in [0, 1]. float GetAccumulator() const noexcept override; // Return the integral accumulator - void StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID + GCodeResult StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID void GetAutoTuneStatus(const StringRef& reply) const noexcept override; // Get the auto tune status or last result void Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing @@ -52,10 +52,11 @@ protected: GCodeResult SwitchOn(const StringRef& reply) noexcept override; // Turn the heater on and set the mode GCodeResult UpdateModel(const StringRef& reply) noexcept override; // Called when the heater model has been changed GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept override { return GCodeResult::ok; } + GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept override { return GCodeResult::ok; } private: void SetHeater(float power) const noexcept; // Power is a fraction in [0,1] - TemperatureError ReadTemperature() noexcept; // Read and store the temperature of this heater + TemperatureError ReadTemperature() noexcept; // Read and store the temperature of this heater void DoTuningStep() noexcept; // Called on each temperature sample when auto tuning static bool ReadingsStable(size_t numReadings, float maxDiff) noexcept pre(numReadings >= 2; numReadings <= MaxTuningTempReadings); @@ -65,27 +66,27 @@ private: void DisplayBuffer(const char *intro) noexcept; // Debug helper float GetExpectedHeatingRate() const noexcept; // Get the minimum heating rate we expect - PwmPort port; // The port that drives the heater - float temperature; // The current temperature - float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative - size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next - float iAccumulator; // The integral LocalHeater component - float lastPwm; // The last PWM value we output, before scaling by kS - float averagePWM; // The running average of the PWM, after scaling. - uint32_t timeSetHeating; // When we turned on the heater - uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin() + PwmPort port; // The port that drives the heater + float temperature; // The current temperature + float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative + size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next + float iAccumulator; // The integral LocalHeater component + float lastPwm; // The last PWM value we output, before scaling by kS + float averagePWM; // The running average of the PWM, after scaling. + uint32_t timeSetHeating; // When we turned on the heater + uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin() - uint16_t heatingFaultCount; // Count of questionable heating behaviours + uint16_t heatingFaultCount; // Count of questionable heating behaviours - uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings - HeaterMode mode; // Current state of the heater - bool tuned; // True if tuning was successful - uint8_t badTemperatureCount; // Count of sequential dud readings + uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings + HeaterMode mode; // Current state of the heater + bool tuned; // True if tuning was successful + uint8_t badTemperatureCount; // Count of sequential dud readings static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood"); // Variables used during heater tuning - static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number. + static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number. static float *tuningTempReadings; // the readings from the heater being tuned static float tuningStartTemp; // the temperature when we turned on the heater diff --git a/src/Heating/RemoteHeater.cpp b/src/Heating/RemoteHeater.cpp index ace55336..ba00d3e2 100644 --- a/src/Heating/RemoteHeater.cpp +++ b/src/Heating/RemoteHeater.cpp @@ -128,14 +128,15 @@ float RemoteHeater::GetAccumulator() const noexcept return 0.0; // not supported } -void RemoteHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept +GCodeResult RemoteHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept { - //TODO + reply.copy("remote heater auto tune not implemented"); + return GCodeResult::error; } void RemoteHeater::GetAutoTuneStatus(const StringRef& reply) const noexcept { - //TODO + reply.copy("remote heater auto tune not implemented"); } void RemoteHeater::Suspend(bool sus) noexcept @@ -189,7 +190,7 @@ GCodeResult RemoteHeater::UpdateModel(const StringRef& reply) noexcept { const CanRequestId rid = CanInterface::AllocateRequestId(boardAddress); CanMessageUpdateHeaterModel * const msg = buf->SetupRequestMessage<CanMessageUpdateHeaterModel>(rid, CanInterface::GetCanAddress(), boardAddress); - model.SetupCanMessage(GetHeaterNumber(), *msg); + GetModel().SetupCanMessage(GetHeaterNumber(), *msg); return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply); } @@ -214,6 +215,29 @@ GCodeResult RemoteHeater::UpdateFaultDetectionParameters(const StringRef& reply) return GCodeResult::error; } +GCodeResult RemoteHeater::UpdateHeaterMonitors(const StringRef& reply) noexcept +{ + CanMessageBuffer *buf = CanMessageBuffer::Allocate(); + if (buf != nullptr) + { + const CanRequestId rid = CanInterface::AllocateRequestId(boardAddress); + CanMessageSetHeaterMonitors * const msg = buf->SetupRequestMessage<CanMessageSetHeaterMonitors>(rid, CanInterface::GetCanAddress(), boardAddress); + msg->heater = GetHeaterNumber(); + msg->numMonitors = MaxMonitorsPerHeater; + for (size_t i = 0; i < MaxMonitorsPerHeater; ++i) + { + msg->monitors[i].limit = monitors[i].GetTemperatureLimit(); + msg->monitors[i].sensor = monitors[i].GetSensorNumber(); + msg->monitors[i].action = (uint8_t)monitors[i].GetAction(); + msg->monitors[i].trigger = (int8_t)monitors[i].GetTrigger(); + } + return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply); + } + + reply.copy("No CAN buffer"); + return GCodeResult::error; +} + void RemoteHeater::UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept { if (src == boardAddress) diff --git a/src/Heating/RemoteHeater.h b/src/Heating/RemoteHeater.h index dab1b70d..5801fe7e 100644 --- a/src/Heating/RemoteHeater.h +++ b/src/Heating/RemoteHeater.h @@ -28,7 +28,7 @@ public: float GetTemperature() const noexcept override; // Get the current temperature float GetAveragePWM() const noexcept override; // Return the running average PWM to the heater. Answer is a fraction in [0, 1]. float GetAccumulator() const noexcept override; // Return the integral accumulator - void StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID + GCodeResult StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID void GetAutoTuneStatus(const StringRef& reply) const noexcept override; // Get the auto tune status or last result void Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept override; @@ -39,6 +39,7 @@ protected: GCodeResult SwitchOn(const StringRef& reply) noexcept override; // Turn the heater on and set the mode GCodeResult UpdateModel(const StringRef& reply) noexcept override; // Called when the heater model has been changed GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept override; + GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept override; private: static constexpr uint32_t RemoteStatusTimeout = 2000; diff --git a/src/Heating/Sensors/TemperatureSensor.h b/src/Heating/Sensors/TemperatureSensor.h index fc522974..7b561c4f 100644 --- a/src/Heating/Sensors/TemperatureSensor.h +++ b/src/Heating/Sensors/TemperatureSensor.h @@ -14,6 +14,7 @@ class TemperatureSensor INHERIT_OBJECT_MODEL { public: TemperatureSensor(unsigned int sensorNum, const char *type) noexcept; + TemperatureSensor(const TemperatureSensor&) = delete; // Virtual destructor virtual ~TemperatureSensor() noexcept; diff --git a/src/Networking/Network.cpp b/src/Networking/Network.cpp index 547bc7b6..05769be7 100644 --- a/src/Networking/Network.cpp +++ b/src/Networking/Network.cpp @@ -103,10 +103,11 @@ constexpr ObjectModelArrayDescriptor Network::interfacesArrayDescriptor = constexpr ObjectModelTableEntry Network::objectModelTable[] = { // These entries must be in alphabetical order - { "interfaces", OBJECT_MODEL_FUNC_NOSELF(&interfacesArrayDescriptor), ObjectModelEntryFlags::none } + { "interfaces", OBJECT_MODEL_FUNC_NOSELF(&interfacesArrayDescriptor), ObjectModelEntryFlags::none }, + { "name", OBJECT_MODEL_FUNC_NOSELF(reprap.GetName()), ObjectModelEntryFlags::none }, }; -constexpr uint8_t Network::objectModelTableDescriptor[] = { 1, 1 }; +constexpr uint8_t Network::objectModelTableDescriptor[] = { 1, 2 }; DEFINE_GET_OBJECT_MODEL_TABLE(Network) diff --git a/src/Networking/Network.h b/src/Networking/Network.h index f0e0d5ae..fe7e568b 100644 --- a/src/Networking/Network.h +++ b/src/Networking/Network.h @@ -54,6 +54,7 @@ class Network INHERIT_OBJECT_MODEL { public: Network(Platform& p) noexcept; + Network(const Network&) = delete; void Init() noexcept; void Activate() noexcept; diff --git a/src/Networking/NetworkInterface.h b/src/Networking/NetworkInterface.h index bfd88c86..739f19c2 100644 --- a/src/Networking/NetworkInterface.h +++ b/src/Networking/NetworkInterface.h @@ -14,6 +14,9 @@ class NetworkInterface INHERIT_OBJECT_MODEL { public: + NetworkInterface() { } + NetworkInterface(const NetworkInterface&) = delete; + virtual void Init() noexcept = 0; virtual void Activate() noexcept = 0; virtual void Exit() noexcept = 0; diff --git a/src/Networking/NetworkResponder.h b/src/Networking/NetworkResponder.h index 0b13089b..ee5aa26d 100644 --- a/src/Networking/NetworkResponder.h +++ b/src/Networking/NetworkResponder.h @@ -24,6 +24,8 @@ class Socket; class NetworkResponder { public: + NetworkResponder(const NetworkResponder&) = delete; + NetworkResponder *GetNext() const noexcept { return next; } virtual bool Spin() noexcept = 0; // do some work, returning true if we did anything significant virtual bool Accept(Socket *s, NetworkProtocol protocol) noexcept = 0; // ask the responder to accept this connection, returns true if it did diff --git a/src/Networking/Socket.h b/src/Networking/Socket.h index 2cc86cf2..41774670 100644 --- a/src/Networking/Socket.h +++ b/src/Networking/Socket.h @@ -23,6 +23,8 @@ class Socket { public: Socket(NetworkInterface *iface) noexcept : interface(iface), localPort(0), remotePort(0), remoteIPAddress(), state(SocketState::disabled) { } + Socket(const Socket&) = delete; + NetworkInterface *GetInterface() const noexcept { return interface; } Port GetLocalPort() const noexcept { return localPort; } diff --git a/src/OutputMemory.h b/src/OutputMemory.h index 27d12e46..85a411df 100644 --- a/src/OutputMemory.h +++ b/src/OutputMemory.h @@ -23,99 +23,100 @@ class OutputStack; // This class is used to hold data for sending (either for Serial or Network destinations) class OutputBuffer { - public: - friend class OutputStack; +public: + friend class OutputStack; - OutputBuffer(OutputBuffer *n) noexcept : next(n) { } + OutputBuffer(OutputBuffer *n) noexcept : next(n) { } + OutputBuffer(const OutputBuffer&) = delete; - void Append(OutputBuffer *other) noexcept; - OutputBuffer *Next() const noexcept { return next; } - bool IsReferenced() const noexcept { return isReferenced; } - bool HadOverflow() const noexcept { return hadOverflow; } - void IncreaseReferences(size_t refs) noexcept; + void Append(OutputBuffer *other) noexcept; + OutputBuffer *Next() const noexcept { return next; } + bool IsReferenced() const noexcept { return isReferenced; } + bool HadOverflow() const noexcept { return hadOverflow; } + void IncreaseReferences(size_t refs) noexcept; - const char *Data() const noexcept { return data; } - const char *UnreadData() const noexcept { return data + bytesRead; } - size_t DataLength() const noexcept { return dataLength; } // How many bytes have been written to this instance? - size_t Length() const noexcept; // How many bytes have been written to the whole chain? + const char *Data() const noexcept { return data; } + const char *UnreadData() const noexcept { return data + bytesRead; } + size_t DataLength() const noexcept { return dataLength; } // How many bytes have been written to this instance? + size_t Length() const noexcept; // How many bytes have been written to the whole chain? - char& operator[](size_t index) noexcept; - char operator[](size_t index) const noexcept; - const char *Read(size_t len) noexcept; - void Taken(size_t len) noexcept { bytesRead += len; } - size_t BytesLeft() const noexcept { return dataLength - bytesRead; } // How many bytes have not been sent yet? + char& operator[](size_t index) noexcept; + char operator[](size_t index) const noexcept; + const char *Read(size_t len) noexcept; + void Taken(size_t len) noexcept { bytesRead += len; } + size_t BytesLeft() const noexcept { return dataLength - bytesRead; } // How many bytes have not been sent yet? - size_t printf(const char *fmt, ...) noexcept __attribute__ ((format (printf, 2, 3))); - size_t vprintf(const char *fmt, va_list vargs) noexcept; - size_t catf(const char *fmt, ...) noexcept __attribute__ ((format (printf, 2, 3))); - size_t lcatf(const char *fmt, ...) noexcept __attribute__ ((format (printf, 2, 3))); + size_t printf(const char *fmt, ...) noexcept __attribute__ ((format (printf, 2, 3))); + size_t vprintf(const char *fmt, va_list vargs) noexcept; + size_t catf(const char *fmt, ...) noexcept __attribute__ ((format (printf, 2, 3))); + size_t lcatf(const char *fmt, ...) noexcept __attribute__ ((format (printf, 2, 3))); - size_t copy(const char c) noexcept; - size_t copy(const char *src) noexcept; - size_t copy(const char *src, size_t len) noexcept; + size_t copy(const char c) noexcept; + size_t copy(const char *src) noexcept; + size_t copy(const char *src, size_t len) noexcept; - size_t cat(const char c) noexcept; - size_t cat(const char *src) noexcept; - size_t lcat(const char *src) noexcept; - size_t cat(const char *src, size_t len) noexcept; - size_t lcat(const char *src, size_t len) noexcept; - size_t cat(StringRef &str) noexcept; + size_t cat(const char c) noexcept; + size_t cat(const char *src) noexcept; + size_t lcat(const char *src) noexcept; + size_t cat(const char *src, size_t len) noexcept; + size_t lcat(const char *src, size_t len) noexcept; + size_t cat(StringRef &str) noexcept; - size_t EncodeString(const char *src, bool allowControlChars, bool prependAsterisk = false) noexcept; - size_t EncodeChar(char c) noexcept; + size_t EncodeString(const char *src, bool allowControlChars, bool prependAsterisk = false) noexcept; + size_t EncodeChar(char c) noexcept; - template<size_t Len> size_t EncodeString(const String<Len>& str, bool allowControlChars, bool prependAsterisk = false) noexcept - { - return EncodeString(str.c_str(), allowControlChars, prependAsterisk); - } + template<size_t Len> size_t EncodeString(const String<Len>& str, bool allowControlChars, bool prependAsterisk = false) noexcept + { + return EncodeString(str.c_str(), allowControlChars, prependAsterisk); + } - size_t EncodeReply(OutputBuffer *src) noexcept; + size_t EncodeReply(OutputBuffer *src) noexcept; - uint32_t GetAge() const noexcept; + uint32_t GetAge() const noexcept; #if HAS_MASS_STORAGE - // Write the buffer to file returning true if successful - bool WriteToFile(FileData& f) const noexcept; + // Write the buffer to file returning true if successful + bool WriteToFile(FileData& f) const noexcept; #endif - // Initialise the output buffers manager - static void Init() noexcept; + // Initialise the output buffers manager + static void Init() noexcept; - // Allocate an unused OutputBuffer instance. Returns true on success or false if no instance could be allocated. - static bool Allocate(OutputBuffer *&buf) noexcept; + // Allocate an unused OutputBuffer instance. Returns true on success or false if no instance could be allocated. + static bool Allocate(OutputBuffer *&buf) noexcept; - // Get the number of bytes left for allocation. If writingBuffer is not NULL, this returns the number of free bytes for - // continuous writes, i.e. for writes that need to allocate an extra OutputBuffer instance to finish the message. - static size_t GetBytesLeft(const OutputBuffer *writingBuffer) noexcept; + // Get the number of bytes left for allocation. If writingBuffer is not NULL, this returns the number of free bytes for + // continuous writes, i.e. for writes that need to allocate an extra OutputBuffer instance to finish the message. + static size_t GetBytesLeft(const OutputBuffer *writingBuffer) noexcept; - // Truncate an OutputBuffer instance to free up more memory. Returns the number of released bytes. - static size_t Truncate(OutputBuffer *buffer, size_t bytesNeeded) noexcept; + // Truncate an OutputBuffer instance to free up more memory. Returns the number of released bytes. + static size_t Truncate(OutputBuffer *buffer, size_t bytesNeeded) noexcept; - // Release one OutputBuffer instance. Returns the next item from the chain or nullptr if this was the last instance. - static OutputBuffer *Release(OutputBuffer *buf) noexcept; + // Release one OutputBuffer instance. Returns the next item from the chain or nullptr if this was the last instance. + static OutputBuffer *Release(OutputBuffer *buf) noexcept; - // Release all OutputBuffer objects in a chain - static void ReleaseAll(OutputBuffer * volatile &buf) noexcept; + // Release all OutputBuffer objects in a chain + static void ReleaseAll(OutputBuffer * volatile &buf) noexcept; - static void Diagnostics(MessageType mtype) noexcept; + static void Diagnostics(MessageType mtype) noexcept; - static unsigned int GetFreeBuffers() { return OUTPUT_BUFFER_COUNT - usedOutputBuffers; } + static unsigned int GetFreeBuffers() { return OUTPUT_BUFFER_COUNT - usedOutputBuffers; } - private: - OutputBuffer *next; - OutputBuffer *last; +private: + OutputBuffer *next; + OutputBuffer *last; - uint32_t whenQueued; + uint32_t whenQueued; - char data[OUTPUT_BUFFER_SIZE]; - size_t dataLength, bytesRead; + char data[OUTPUT_BUFFER_SIZE]; + size_t dataLength, bytesRead; - bool isReferenced; - bool hadOverflow; - volatile size_t references; + bool isReferenced; + bool hadOverflow; + volatile size_t references; - static OutputBuffer * volatile freeOutputBuffers; // Messages may be sent by multiple tasks - static volatile size_t usedOutputBuffers; // so make these volatile. - static volatile size_t maxUsedOutputBuffers; + static OutputBuffer * volatile freeOutputBuffers; // Messages may be sent by multiple tasks + static volatile size_t usedOutputBuffers; // so make these volatile. + static volatile size_t maxUsedOutputBuffers; }; inline uint32_t OutputBuffer::GetAge() const noexcept @@ -127,60 +128,61 @@ inline uint32_t OutputBuffer::GetAge() const noexcept // Note that OutputStack objects should normally be declared volatile. class OutputStack { - public: - OutputStack() noexcept : count(0) { } +public: + OutputStack() noexcept : count(0) { } + OutputStack(const OutputStack&) = delete; - // Is there anything on this stack? - bool IsEmpty() const volatile noexcept { return count == 0; } + // Is there anything on this stack? + bool IsEmpty() const volatile noexcept { return count == 0; } - // Clear the reference list - void Clear() volatile noexcept { count = 0; } + // Clear the reference list + void Clear() volatile noexcept { count = 0; } - // Push an OutputBuffer chain. Return true if successful, else release the buffer and return false. - bool Push(OutputBuffer *buffer, MessageType type = NoDestinationMessage) volatile noexcept; + // Push an OutputBuffer chain. Return true if successful, else release the buffer and return false. + bool Push(OutputBuffer *buffer, MessageType type = NoDestinationMessage) volatile noexcept; - // Pop an OutputBuffer chain or return NULL if none is available - OutputBuffer *Pop() volatile noexcept; + // Pop an OutputBuffer chain or return NULL if none is available + OutputBuffer *Pop() volatile noexcept; - // Returns the first item from the stack or NULL if none is available - OutputBuffer *GetFirstItem() const volatile noexcept; + // Returns the first item from the stack or NULL if none is available + OutputBuffer *GetFirstItem() const volatile noexcept; - // Returns the first item's type from the stack or NoDestinationMessage if none is available - MessageType GetFirstItemType() const volatile noexcept; + // Returns the first item's type from the stack or NoDestinationMessage if none is available + MessageType GetFirstItemType() const volatile noexcept; #if HAS_LINUX_INTERFACE - // Set the first item of the stack. If it's NULL, then the first item will be removed - void SetFirstItem(OutputBuffer *buffer) volatile noexcept; + // Set the first item of the stack. If it's NULL, then the first item will be removed + void SetFirstItem(OutputBuffer *buffer) volatile noexcept; #endif - // Release the first item at the top of the stack - void ReleaseFirstItem() volatile noexcept; + // Release the first item at the top of the stack + void ReleaseFirstItem() volatile noexcept; - // Apply a timeout to the first item at the top of the stack - bool ApplyTimeout(uint32_t ticks) volatile noexcept; + // Apply a timeout to the first item at the top of the stack + bool ApplyTimeout(uint32_t ticks) volatile noexcept; - // Returns the last item from the stack or NULL if none is available - OutputBuffer *GetLastItem() const volatile noexcept; + // Returns the last item from the stack or NULL if none is available + OutputBuffer *GetLastItem() const volatile noexcept; - // Returns the type of the last item from the stack or NoDestinationMessage if none is available - MessageType GetLastItemType() const volatile noexcept; + // Returns the type of the last item from the stack or NoDestinationMessage if none is available + MessageType GetLastItemType() const volatile noexcept; - // Get the total length of all queued buffers - size_t DataLength() const volatile noexcept; + // Get the total length of all queued buffers + size_t DataLength() const volatile noexcept; - // Append another OutputStack to this instance. If no more space is available, - // all OutputBuffers that can't be added are automatically released - void Append(volatile OutputStack& stack) volatile noexcept; + // Append another OutputStack to this instance. If no more space is available, + // all OutputBuffers that can't be added are automatically released + void Append(volatile OutputStack& stack) volatile noexcept; - // Increase the number of references for each OutputBuffer on the stack - void IncreaseReferences(size_t num) volatile noexcept; + // Increase the number of references for each OutputBuffer on the stack + void IncreaseReferences(size_t num) volatile noexcept; - // Release all buffers and clean up - void ReleaseAll() volatile noexcept; + // Release all buffers and clean up + void ReleaseAll() volatile noexcept; - private: - size_t count; - OutputBuffer * items[OUTPUT_STACK_DEPTH]; - MessageType types[OUTPUT_STACK_DEPTH]; +private: + size_t count; + OutputBuffer * items[OUTPUT_STACK_DEPTH]; + MessageType types[OUTPUT_STACK_DEPTH]; }; #endif /* OUTPUTMEMORY_H_ */ diff --git a/src/Pccb/Pins_Pccb.h b/src/Pccb/Pins_Pccb.h index d80f248c..97d29283 100644 --- a/src/Pccb/Pins_Pccb.h +++ b/src/Pccb/Pins_Pccb.h @@ -91,7 +91,7 @@ constexpr size_t MaxSmartDrivers = 2; // The maximum number of smart drivers constexpr size_t MaxSensors = 32; constexpr size_t MaxHeaters = 1; // The number of heaters in the machine. PCCB has no heaters. -constexpr size_t MaxExtraHeaterProtections = 4; // The number of extra heater protection instances +constexpr size_t MaxMonitorsPerHeater = 3; // The maximum number of monitors per heater constexpr size_t MaxBedHeaters = 1; constexpr size_t MaxChamberHeaters = 1; diff --git a/src/RepRap.cpp b/src/RepRap.cpp index b55244e4..365ad8e5 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -181,9 +181,18 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] = { "axes", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxAxes), ObjectModelEntryFlags::verbose }, { "axesPlusExtruders", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxAxesPlusExtruders), ObjectModelEntryFlags::verbose }, { "bedHeaters", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxBedHeaters), ObjectModelEntryFlags::verbose }, +#if SUPPORT_CAN_EXPANSION + { "boards", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxCanBoards + 1), ObjectModelEntryFlags::verbose }, +#else + { "boards", OBJECT_MODEL_FUNC_NOSELF((int32_t)1), ObjectModelEntryFlags::verbose }, +#endif { "chamberHeaters", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxChamberHeaters), ObjectModelEntryFlags::verbose }, +#if SUPPORT_CAN_EXPANSION + { "drivers", OBJECT_MODEL_FUNC_NOSELF((int32_t)(NumDirectDrivers + MaxCanDrivers)), ObjectModelEntryFlags::verbose }, +#else + { "drivers", OBJECT_MODEL_FUNC_NOSELF((int32_t)NumDirectDrivers), ObjectModelEntryFlags::verbose }, +#endif { "driversPerAxis", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxDriversPerAxis), ObjectModelEntryFlags::verbose }, - { "extraHeaterProtections", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxExtraHeaterProtections), ObjectModelEntryFlags::verbose }, { "extruders", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxExtruders), ObjectModelEntryFlags::verbose }, { "extrudersPerTool", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxExtrudersPerTool), ObjectModelEntryFlags::verbose }, { "fans", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxFans), ObjectModelEntryFlags::verbose }, @@ -191,6 +200,7 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] = { "gpOutPorts", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxGpOutPorts), ObjectModelEntryFlags::verbose }, { "heaters", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxHeaters), ObjectModelEntryFlags::verbose }, { "heatersPerTool", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxHeatersPerTool), ObjectModelEntryFlags::verbose }, + { "monitorsPerHeater", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxMonitorsPerHeater), ObjectModelEntryFlags::verbose }, { "sensors", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxSensors), ObjectModelEntryFlags::verbose }, { "spindles", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxSpindles), ObjectModelEntryFlags::verbose }, { "triggers", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxTriggers), ObjectModelEntryFlags::verbose }, @@ -198,7 +208,7 @@ constexpr ObjectModelTableEntry RepRap::objectModelTable[] = { "zProbes", OBJECT_MODEL_FUNC_NOSELF((int32_t)MaxZProbes), ObjectModelEntryFlags::verbose }, }; -constexpr uint8_t RepRap::objectModelTableDescriptor[] = { 3, 10, 5, 18 }; +constexpr uint8_t RepRap::objectModelTableDescriptor[] = { 3, 10, 5, 20 }; DEFINE_GET_OBJECT_MODEL_TABLE(RepRap) diff --git a/src/RepRap.h b/src/RepRap.h index 85b6a63d..0561bbc9 100644 --- a/src/RepRap.h +++ b/src/RepRap.h @@ -61,6 +61,8 @@ class RepRap INHERIT_OBJECT_MODEL { public: RepRap() noexcept; + RepRap(const RepRap&) = delete; + void EmergencyStop() noexcept; void Init() noexcept; void Spin() noexcept; diff --git a/src/Tools/Tool.cpp b/src/Tools/Tool.cpp index 0aab2ae7..36d81f2b 100644 --- a/src/Tools/Tool.cpp +++ b/src/Tools/Tool.cpp @@ -361,8 +361,17 @@ void Tool::Activate() noexcept { for (size_t heater = 0; heater < heaterCount; heater++) { - reprap.GetHeat().SetActiveTemperature(heaters[heater], activeTemperatures[heater]); - reprap.GetHeat().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]); + try + { + reprap.GetHeat().SetActiveTemperature(heaters[heater], activeTemperatures[heater]); + reprap.GetHeat().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]); + } + catch (GCodeException& exc) + { + String<StringLength100> message; + exc.GetMessage(message.GetRef(), nullptr); + reprap.GetPlatform().Message(ErrorMessage, message.c_str()); + } String<1> dummy; (void)reprap.GetHeat().Activate(heaters[heater], dummy.GetRef()); } @@ -377,8 +386,17 @@ void Tool::Standby() noexcept // 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().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]); - reprap.GetHeat().Standby(heaters[heater], this); + try + { + reprap.GetHeat().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]); + reprap.GetHeat().Standby(heaters[heater], this); + } + catch (GCodeException& exc) + { + String<StringLength100> message; + exc.GetMessage(message.GetRef(), nullptr); + reprap.GetPlatform().Message(ErrorMessage, message.c_str()); + } } } state = ToolState::standby; @@ -494,10 +512,11 @@ float Tool::GetToolHeaterStandbyTemperature(size_t heaterNumber) const noexcept return (heaterNumber < heaterCount) ? standbyTemperatures[heaterNumber] : 0.0; } -void Tool::SetToolHeaterActiveTemperature(size_t heaterNumber, float temp) noexcept +void Tool::SetToolHeaterActiveTemperature(size_t heaterNumber, float temp) THROWS(GCodeException) { if (heaterNumber < heaterCount) { + const int8_t heater = heaters[heaterNumber]; const Tool * const currentTool = reprap.GetCurrentTool(); const bool setHeater = (currentTool == nullptr || currentTool == this); if (temp < NEARLY_ABS_ZERO) // temperatures close to ABS_ZERO turn off the heater @@ -505,26 +524,26 @@ void Tool::SetToolHeaterActiveTemperature(size_t heaterNumber, float temp) noexc activeTemperatures[heaterNumber] = 0; if (setHeater) { - reprap.GetHeat().SwitchOff(heaters[heaterNumber]); + reprap.GetHeat().SwitchOff(heater); } } else { - const float minTemperatureLimit = reprap.GetHeat().GetLowestTemperatureLimit(heaters[heaterNumber]); - const float maxTemperatureLimit = reprap.GetHeat().GetHighestTemperatureLimit(heaters[heaterNumber]); + const float minTemperatureLimit = reprap.GetHeat().GetLowestTemperatureLimit(heater); + const float maxTemperatureLimit = reprap.GetHeat().GetHighestTemperatureLimit(heater); if (temp > minTemperatureLimit && temp < maxTemperatureLimit) { activeTemperatures[heaterNumber] = temp; if (setHeater) { - reprap.GetHeat().SetActiveTemperature(heaters[heaterNumber], activeTemperatures[heaterNumber]); + reprap.GetHeat().SetActiveTemperature(heater, activeTemperatures[heaterNumber]); } } } } } -void Tool::SetToolHeaterStandbyTemperature(size_t heaterNumber, float temp) noexcept +void Tool::SetToolHeaterStandbyTemperature(size_t heaterNumber, float temp) THROWS(GCodeException) { if (heaterNumber < heaterCount) { diff --git a/src/Tools/Tool.h b/src/Tools/Tool.h index d70d429f..c64388e1 100644 --- a/src/Tools/Tool.h +++ b/src/Tools/Tool.h @@ -83,8 +83,8 @@ public: float GetToolHeaterActiveTemperature(size_t heaterNumber) const noexcept; float GetToolHeaterStandbyTemperature(size_t heaterNumber) const noexcept; - void SetToolHeaterActiveTemperature(size_t heaterNumber, float temp) noexcept; - void SetToolHeaterStandbyTemperature(size_t heaterNumber, float temp) noexcept; + void SetToolHeaterActiveTemperature(size_t heaterNumber, float temp) THROWS(GCodeException); + void SetToolHeaterStandbyTemperature(size_t heaterNumber, float temp) THROWS(GCodeException); bool HasTemperatureFault() const noexcept { return heaterFault; } diff --git a/src/Version.h b/src/Version.h index 7562c680..1ff557a7 100644 --- a/src/Version.h +++ b/src/Version.h @@ -19,7 +19,7 @@ #endif #ifndef DATE -# define DATE "2020-02-12b1" +# define DATE "2020-02-16b1" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman, printm3d" |