diff options
-rw-r--r-- | src/CAN/CommandProcessor.cpp | 4 | ||||
-rw-r--r-- | src/FilamentMonitors/FilamentMonitor.cpp | 63 | ||||
-rw-r--r-- | src/FilamentMonitors/FilamentMonitor.h | 1 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer/BinaryParser.cpp | 2 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer/StringParser.cpp | 2 | ||||
-rw-r--r-- | src/GCodes/GCodeMachineState.h | 2 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 20 | ||||
-rw-r--r-- | src/GCodes/GCodes.h | 1 | ||||
-rw-r--r-- | src/GCodes/GCodes3.cpp | 34 | ||||
-rw-r--r-- | src/GCodes/GCodes4.cpp | 35 | ||||
-rw-r--r-- | src/Heating/LocalHeater.cpp | 38 | ||||
-rw-r--r-- | src/Heating/LocalHeater.h | 2 | ||||
-rw-r--r-- | src/ObjectModel/Variable.h | 1 | ||||
-rw-r--r-- | src/Platform/Event.cpp | 192 | ||||
-rw-r--r-- | src/Platform/Event.h | 64 | ||||
-rw-r--r-- | src/Platform/EventManager.cpp | 14 | ||||
-rw-r--r-- | src/Platform/EventManager.h | 23 | ||||
-rw-r--r-- | src/Platform/Logger.h | 2 | ||||
-rw-r--r-- | src/Platform/MessageType.h | 3 | ||||
-rw-r--r-- | src/Platform/Platform.h | 2 |
20 files changed, 350 insertions, 155 deletions
diff --git a/src/CAN/CommandProcessor.cpp b/src/CAN/CommandProcessor.cpp index b4816351..43d99b58 100644 --- a/src/CAN/CommandProcessor.cpp +++ b/src/CAN/CommandProcessor.cpp @@ -419,6 +419,10 @@ void CommandProcessor::ProcessReceivedMessage(CanMessageBuffer *buf) noexcept return; // no reply needed #endif + case CanMessageType::acknowledgeAnnounce: + CanInterface::MainBoardAcknowledgedAnnounce(); + return; + case CanMessageType::returnInfo: requestId = buf->msg.getInfo.requestId; rslt = EutGetInfo(buf->msg.getInfo, replyRef, extra); diff --git a/src/FilamentMonitors/FilamentMonitor.cpp b/src/FilamentMonitors/FilamentMonitor.cpp index 95e68357..9cf6b686 100644 --- a/src/FilamentMonitors/FilamentMonitor.cpp +++ b/src/FilamentMonitors/FilamentMonitor.cpp @@ -12,6 +12,7 @@ #include "PulsedFilamentMonitor.h" #include <Platform/RepRap.h> #include <Platform/Platform.h> +#include <Platform/Event.h> #include <GCodes/GCodeBuffer/GCodeBuffer.h> #include <Movement/Move.h> #include <PrintMonitor/PrintMonitor.h> @@ -53,7 +54,7 @@ size_t FilamentMonitor::GetNumMonitorsToReport() noexcept FilamentMonitor::FilamentMonitor(unsigned int drv, unsigned int monitorType, DriverId did) noexcept : driveNumber(drv), type(monitorType), driverId(did), lastStatus(FilamentSensorStatus::noDataReceived) #if SUPPORT_CAN_EXPANSION - , hasRemote(false) + , lastRemoteStatus(FilamentSensorStatus::noDataReceived), hasRemote(false) #endif { } @@ -298,7 +299,6 @@ bool FilamentMonitor::IsValid(size_t extruderNumber) const noexcept fromIsr = false; locIsrMillis = 0; } - GCodes& gCodes = reprap.GetGCodes(); if (gCodes.IsReallyPrinting() && !gCodes.IsSimulating()) { @@ -309,28 +309,35 @@ bool FilamentMonitor::IsValid(size_t extruderNumber) const noexcept { fst = fs.Clear(); } - - if (fst != fs.lastStatus) - { + } +#if SUPPORT_CAN_EXPANSION + else + { + fst = fs.lastRemoteStatus; + } +#endif + if (fst != fs.lastStatus) + { #if SUPPORT_REMOTE_COMMANDS - statusChanged = true; + statusChanged = true; #endif - fs.lastStatus = fst; - if (fst != FilamentSensorStatus::ok + fs.lastStatus = fst; + if (fst != FilamentSensorStatus::ok #if SUPPORT_REMOTE_COMMANDS - && !CanInterface::InExpansionMode() + && !CanInterface::InExpansionMode() #endif - ) + ) + { + const size_t extruder = LogicalDriveToExtruder(fs.driveNumber); + if (reprap.Debug(moduleFilamentSensors)) + { + debugPrintf("Filament error: extruder %u reports %s\n", extruder, fst.ToString()); + } + else { - const size_t extruder = LogicalDriveToExtruder(fs.driveNumber); - if (reprap.Debug(moduleFilamentSensors)) - { - debugPrintf("Filament error: extruder %u reports %s\n", extruder, fst.ToString()); - } - else - { - gCodes.FilamentError(extruder, fst); - } + va_list dummy; + Event::AddEvent(EventType::filament_error, (uint16_t)fst.ToBaseType(), extruder, CanInterface::GetCanAddress(), "", dummy); +// gCodes.FilamentError(extruder, fst); } } } @@ -367,23 +374,7 @@ bool FilamentMonitor::IsValid(size_t extruderNumber) const noexcept FilamentMonitor& fs = *filamentSensors[extruder]; if (fs.driverId.boardAddress == src && fs.driverId.localDriver < msg.numMonitorsReported) { - const FilamentSensorStatus fstat(msg.data[fs.driverId.localDriver].status); - fs.lastStatus = fstat; - GCodes& gCodes = reprap.GetGCodes(); - if (gCodes.IsReallyPrinting() && !gCodes.IsSimulating()) - { - if (fstat != FilamentSensorStatus::ok) - { - if (reprap.Debug(moduleFilamentSensors)) - { - debugPrintf("Filament error: extruder %u reports %s\n", extruder, fstat.ToString()); - } - else - { - gCodes.FilamentError(extruder, fstat); - } - } - } + fs.lastRemoteStatus = FilamentSensorStatus(msg.data[fs.driverId.localDriver].status); } } } diff --git a/src/FilamentMonitors/FilamentMonitor.h b/src/FilamentMonitors/FilamentMonitor.h index af7d6bea..4f0cc406 100644 --- a/src/FilamentMonitors/FilamentMonitor.h +++ b/src/FilamentMonitors/FilamentMonitor.h @@ -164,6 +164,7 @@ private: bool haveIsrStepsCommanded; FilamentSensorStatus lastStatus; #if SUPPORT_CAN_EXPANSION + FilamentSensorStatus lastRemoteStatus; bool hasRemote; #endif }; diff --git a/src/GCodes/GCodeBuffer/BinaryParser.cpp b/src/GCodes/GCodeBuffer/BinaryParser.cpp index 954c4e29..b1e8e4fa 100644 --- a/src/GCodes/GCodeBuffer/BinaryParser.cpp +++ b/src/GCodes/GCodeBuffer/BinaryParser.cpp @@ -873,7 +873,7 @@ void BinaryParser::AddParameters(VariableSet& vs, int codeRunning) noexcept if (ev.GetType() != TypeCode::None) { char paramName[2] = { param->letter, 0 }; - vs.InsertNew(paramName, ev, -1); + vs.InsertNewParameter(paramName, ev); } } diff --git a/src/GCodes/GCodeBuffer/StringParser.cpp b/src/GCodes/GCodeBuffer/StringParser.cpp index 24a6aaa3..78669752 100644 --- a/src/GCodes/GCodeBuffer/StringParser.cpp +++ b/src/GCodes/GCodeBuffer/StringParser.cpp @@ -1914,7 +1914,7 @@ void StringParser::AddParameters(VariableSet& vs, int codeRunning) noexcept ev.Set(nullptr); } char paramName[2] = { letter, 0 }; - vs.InsertNew(paramName, ev, -1); + vs.InsertNewParameter(paramName, ev); } } ); diff --git a/src/GCodes/GCodeMachineState.h b/src/GCodes/GCodeMachineState.h index 3ea86bce..c5ec5bbb 100644 --- a/src/GCodes/GCodeMachineState.h +++ b/src/GCodes/GCodeMachineState.h @@ -109,6 +109,8 @@ enum class GCodeState : uint8_t unloadingFilament, checkError, // go to this state after doing a macro when we need to check for a stored error message + processingEvent, + finishedProcessingEvent, #if HAS_MASS_STORAGE timingSDwrite, diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 902c0ac0..01291c3f 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -34,6 +34,7 @@ #include <PrintMonitor/PrintMonitor.h> #include <Platform/RepRap.h> #include <Platform/Tasks.h> +#include <Platform/Event.h> #include <Tools/Tool.h> #include <Endstops/ZProbe.h> #include <ObjectModel/Variable.h> @@ -573,6 +574,13 @@ bool GCodes::StartNextGCode(GCodeBuffer& gb, const StringRef& reply) noexcept { return DoFilePrint(gb, reply); } + else if (&gb == autoPauseGCode) + { + if (Event::StartProcessing()) + { + ProcessEvent(gb); // call out to separate function to avoid increasing stack usage of this function + } + } else if (&gb == daemonGCode #if SUPPORT_REMOTE_COMMANDS && !CanInterface::InExpansionMode() // looking for the daemon.g file increases the loop time too much @@ -967,8 +975,8 @@ void GCodes::DoPause(GCodeBuffer& gb, PauseReason reason, const char *msg, uint1 FileData& fdata = fileGCode->LatestMachineState().fileState; if (fdata.IsLive()) { - fileGCode->RestartFrom(pauseRestorePoint.filePos); // TODO we ought to restore the line number too, but currently we don't save it - UnlockAll(*fileGCode); // release any locks it had + fileGCode->RestartFrom(pauseRestorePoint.filePos); // TODO we ought to restore the line number too, but currently we don't save it + UnlockAll(*fileGCode); // release any locks it had } } #endif @@ -1317,7 +1325,7 @@ bool GCodes::ReHomeOnStall(DriversBitmap stalledDrivers) noexcept if (cfg.driverNumbers[i].IsLocal() && stalledDrivers.IsBitSet(cfg.driverNumbers[i].localDriver)) { char str[2] = { axisLetters[axis], 0 }; - vars.InsertNew(str, ExpressionValue((int32_t)1), -1); // create a parameter with value 1 for the axis + vars.InsertNewParameter(str, ExpressionValue((int32_t)1)); // create a parameter with value 1 for the axis break; } } @@ -3357,7 +3365,7 @@ GCodeResult GCodes::DoDwell(GCodeBuffer& gb) THROWS(GCodeException) } #endif - if ( IsSimulating() // if we are simulating then simulate the G4... + if ( IsSimulating() // if we are simulating then simulate the G4... && &gb != daemonGCode // ...unless it comes from the daemon... && &gb != triggerGCode // ...or a trigger... && (&gb == fileGCode || !exitSimulationWhenFileComplete) // ...or we are simulating a file and this command doesn't come from the file @@ -4689,7 +4697,7 @@ void GCodes::CheckReportDue(GCodeBuffer& gb, const StringRef& reply) const noexc // Send a standard status response for PanelDue OutputBuffer * const statusBuf = (lastAuxStatusReportType == ObjectModelAuxStatusReportType) // PanelDueFirmware v3.2 or later, using M409 to retrieve object model - ? reprap.GetModelResponse("", "d99f") + ? reprap.GetModelResponse("", "d99fi") : GenerateJsonStatusResponse(lastAuxStatusReportType, -1, ResponseSource::AUX); // older PanelDueFirmware using M408 if (statusBuf != nullptr) { @@ -4891,7 +4899,7 @@ void GCodes::HandleHeaterFault() noexcept } } -// Check for and respond to a heater fault, returning true if we should exit +// Check for and respond to a heater fault void GCodes::CheckHeaterFault() noexcept { switch (heaterFaultState) diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index e504f742..3cbddcaa 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -462,6 +462,7 @@ private: void DoPause(GCodeBuffer& gb, PauseReason reason, const char *msg, uint16_t param = 0) noexcept // Pause the print pre(resourceOwners[movementResource] == &gb); void CheckForDeferredPause(GCodeBuffer& gb) noexcept; // Check if a pause is pending, action it if so + void ProcessEvent(GCodeBuffer& gb) noexcept; // Start processing a new event #if HAS_VOLTAGE_MONITOR || HAS_SMART_DRIVERS bool DoEmergencyPause() noexcept; // Do an emergency pause following loss of power or a motor stall diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp index 0a13640b..f321cf0d 100644 --- a/src/GCodes/GCodes3.cpp +++ b/src/GCodes/GCodes3.cpp @@ -12,6 +12,7 @@ #include <Heating/Heat.h> #include <Movement/Move.h> #include <Platform/RepRap.h> +#include <Platform/Event.h> #include <Tools/Tool.h> #include <Endstops/ZProbe.h> #include <PrintMonitor/PrintMonitor.h> @@ -1863,6 +1864,37 @@ bool GCodes::ProcessWholeLineComment(GCodeBuffer& gb, const StringRef& reply) TH return true; } +// Process and event. The autoPauseGCode buffer calls this when there is a new event to be processed. +// This is a separate function because it allocates strings on the stack. +void GCodes::ProcessEvent(GCodeBuffer& gb) noexcept +{ + // Get the event message + String<StringLength100> eventText; + const MessageType mt = Event::GetTextDescription(eventText.GetRef()); + platform.Message(mt, eventText.c_str()); // tell the user about the event and log it + + // Get the name of the macro file that we should look for + String<StringLength50> macroName; + Event::GetMacroFileName(macroName.GetRef()); + if (platform.SysFileExists(macroName.c_str())) + { + // Set up the macro parameters + VariableSet vars; + Event::GetParameters(vars); + vars.InsertNewParameter("S", ExpressionValue(StringHandle(eventText.c_str()))); + + // Run the macro + gb.SetState(GCodeState::finishedProcessingEvent); // cancel the event when we have finished processing it + if (DoFileMacro(gb, macroName.c_str(), false, AsyncSystemMacroCode, vars)) + { + return; + } + } + + // We didn't execute the macro, so do the default action. It may need to wait for the movement lock, so do it in a new state. + gb.SetState(GCodeState::processingEvent); +} + #if !HAS_MASS_STORAGE && !HAS_EMBEDDED_FILES && defined(DUET_NG) // Function called by RepRap.cpp to enable PanelDue by default in the Duet 2 SBC build @@ -1873,4 +1905,4 @@ void GCodes::SetAux0CommsProperties(uint32_t mode) const noexcept #endif - // End +// End diff --git a/src/GCodes/GCodes4.cpp b/src/GCodes/GCodes4.cpp index f2ccb6b5..961728f9 100644 --- a/src/GCodes/GCodes4.cpp +++ b/src/GCodes/GCodes4.cpp @@ -3,6 +3,7 @@ #include "GCodes.h" #include "GCodeBuffer/GCodeBuffer.h" #include <Platform/RepRap.h> +#include <Platform/Event.h> #include <Movement/Move.h> #include <Tools/Tool.h> #include <Heating/Heat.h> @@ -1482,6 +1483,40 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) noexcept gb.SetState(GCodeState::normal); break; + // Here when we need to execute the default action for an event because the macro file was not found. We have already sent a message and logged the event. + case GCodeState::processingEvent: + { + const Event::DefaultAction action = Event::GetDefaultAction(); + if (action == Event::DefaultAction::none) + { + // Nothing more to do + gb.SetState(GCodeState::finishedProcessingEvent); + } + else + { + // We are going to pause + if (pauseState != PauseState::resuming) // if we are resuming, wait for the resume to complete + { + if (pauseState != PauseState::notPaused) + { + gb.SetState(GCodeState::finishedProcessingEvent); // already paused + } + else if (LockMovementAndWaitForStandstill(gb)) + { + gb.SetState(GCodeState::finishedProcessingEvent); + DoPause(gb, qq); + } + } + } + } + break; + + // Here when we have finished processing an event + case GCodeState::finishedProcessingEvent: + Event::FinishedProcessing(); + gb.SetState(GCodeState::normal); + break; + default: // should not happen gb.LatestMachineState().SetError("Undefined GCodeState"); gb.SetState(GCodeState::normal); diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp index b105d6ea..4f21b246 100644 --- a/src/Heating/LocalHeater.cpp +++ b/src/Heating/LocalHeater.cpp @@ -12,6 +12,7 @@ #include "HeaterMonitor.h" #include <Platform/Platform.h> #include <Platform/RepRap.h> +#include <Platform/Event.h> #include <Tools/Tool.h> #if SUPPORT_REMOTE_COMMANDS @@ -252,7 +253,7 @@ void LocalHeater::Spin() noexcept badTemperatureCount++; if (badTemperatureCount > MaxBadTemperatureCount) { - RaiseHeaterFault("Temperature reading fault on heater %u: %s\n", GetHeaterNumber(), TemperatureErrorString(err)); + RaiseHeaterFault(HeaterFaultType::failedToReadSensor, "%s", TemperatureErrorString(err)); } } // We leave lastPWM alone if we have a temporary temperature reading error @@ -297,7 +298,7 @@ void LocalHeater::Spin() noexcept else { const uint32_t now = millis(); - if ((float)(now - timeSetHeating) < GetModel().GetDeadTime() * SecondsToMillis * 1.5) + if ((float)(now - timeSetHeating) < GetModel().GetDeadTime() * SecondsToMillis * 2) // wait for twice the dead time before we start looking at the temperature rise { // Record the temperature for when we are past the dead time lastTemperatureValue = temperature; @@ -313,13 +314,14 @@ void LocalHeater::Spin() noexcept // Check that we are heating fast enough, and if so, take another sample const float expectedTemperatureRise = expectedRate * actualInterval; const float actualTemperatureRise = temperature - lastTemperatureValue; - if (actualTemperatureRise < expectedTemperatureRise * 0.7) + if (actualTemperatureRise < expectedTemperatureRise * 0.5) { ++heatingFaultCount; if (heatingFaultCount * HeatSampleIntervalMillis > GetMaxHeatingFaultTime() * SecondsToMillis) { - RaiseHeaterFault("Heater %u fault: at %.1f" DEGREE_SYMBOL "C temperature is rising at %.1f" DEGREE_SYMBOL "C/sec, well below the expected %.1f" DEGREE_SYMBOL "C/sec\n", - GetHeaterNumber(), (double)temperature, (double)(actualTemperatureRise/actualInterval), (double)expectedRate); + RaiseHeaterFault(HeaterFaultType::temperatureRisingTooSlowly, + "expected %.2f" DEGREE_SYMBOL "C/sec measured %.2f" DEGREE_SYMBOL "C/sec", + (double)expectedRate, (double)(actualTemperatureRise/actualInterval)); } } else @@ -343,8 +345,9 @@ void LocalHeater::Spin() noexcept ++heatingFaultCount; if (heatingFaultCount * HeatSampleIntervalMillis > GetMaxHeatingFaultTime() * SecondsToMillis) { - RaiseHeaterFault("Heater %u fault: temperature excursion exceeded %.1f" DEGREE_SYMBOL "C (target %.1f" DEGREE_SYMBOL "C, actual %.1f" DEGREE_SYMBOL "C)\n", - GetHeaterNumber(), (double)GetMaxTemperatureExcursion(), (double)targetTemperature, (double)temperature); + RaiseHeaterFault(HeaterFaultType::exceededAllowedExcursion, + "target %.1f" DEGREE_SYMBOL "C actual %.1f" DEGREE_SYMBOL "C", + (double)targetTemperature, (double)temperature); } } else if (heatingFaultCount != 0) @@ -450,7 +453,7 @@ void LocalHeater::Spin() noexcept break; case HeaterMonitorAction::GenerateFault: - RaiseHeaterFault("Heater %u fault: heater monitor %u was triggered\n", GetHeaterNumber(), i); + RaiseHeaterFault(HeaterFaultType::monitorTriggered, "monitor %u was triggered", i); break; case HeaterMonitorAction::TemporarySwitchOff: @@ -910,20 +913,31 @@ void LocalHeater::Suspend(bool sus) noexcept } } -void LocalHeater::RaiseHeaterFault(const char *format, ...) noexcept +void LocalHeater::RaiseHeaterFault(HeaterFaultType type, const char *_ecv_array format, ...) noexcept { lastPwm = 0.0; SetHeater(0.0); if (mode != HeaterMode::fault) { mode = HeaterMode::fault; + reprap.FlagTemperatureFault(GetHeaterNumber()); + va_list vargs; va_start(vargs, format); - reprap.GetPlatform().MessageV(ErrorMessage, format, vargs); + +#if SUPPORT_REMOTE_COMMANDS + if (CanInterface::InExpansionMode()) + { + CanInterface::RaiseEvent(EventType::heater_fault, (uint16_t)type, GetHeaterNumber(), format, vargs); + } + else +#endif + { + Event::AddEvent(EventType::heater_fault, (uint16_t)type, GetHeaterNumber(), CanInterface::GetCanAddress(), format, vargs); + } va_end(vargs); +// reprap.GetGCodes().HandleHeaterFault(); } - reprap.GetGCodes().HandleHeaterFault(); - reprap.FlagTemperatureFault(GetHeaterNumber()); } #if SUPPORT_REMOTE_COMMANDS diff --git a/src/Heating/LocalHeater.h b/src/Heating/LocalHeater.h index fd31dde9..cf0840c5 100644 --- a/src/Heating/LocalHeater.h +++ b/src/Heating/LocalHeater.h @@ -66,7 +66,7 @@ private: TemperatureError ReadTemperature() noexcept; // Read and store the temperature of this heater void DoTuningStep() noexcept; // Called on each temperature sample when auto tuning float GetExpectedHeatingRate() const noexcept; // Get the minimum heating rate we expect - void RaiseHeaterFault(const char *format, ...) noexcept; + void RaiseHeaterFault(HeaterFaultType type, const char *_ecv_array format, ...) noexcept; PwmPort ports[MaxPortsPerHeater]; // The port(s) that drive the heater float temperature; // The current temperature diff --git a/src/ObjectModel/Variable.h b/src/ObjectModel/Variable.h index e107faaa..2b104608 100644 --- a/src/ObjectModel/Variable.h +++ b/src/ObjectModel/Variable.h @@ -47,6 +47,7 @@ public: Variable *Lookup(const char *_ecv_array str) noexcept; const Variable *Lookup(const char *_ecv_array str) const noexcept; void InsertNew(const char *str, ExpressionValue pVal, int8_t pScope) noexcept; + void InsertNewParameter(const char *str, ExpressionValue pVal) noexcept { InsertNew(str, pVal, -1); } void EndScope(uint8_t blockNesting) noexcept; void Delete(const char *str) noexcept; void Clear() noexcept; diff --git a/src/Platform/Event.cpp b/src/Platform/Event.cpp index ddc17eb6..5cf95a67 100644 --- a/src/Platform/Event.cpp +++ b/src/Platform/Event.cpp @@ -7,78 +7,176 @@ #include <Platform/Event.h> #include <RepRapFirmware.h> +#include <ObjectModel/ObjectModel.h> +#include <ObjectModel/Variable.h> -Event::Event(Event *p_next, EventType et, EventParameter p_param, CanAddress p_ba, uint8_t devNum) noexcept - : next(p_next), param(p_param), type(et), boardAddress(p_ba), deviceNumber(devNum) +Event *_ecv_null Event::eventsPending = nullptr; + +inline Event::Event(Event *_ecv_null pnext, EventType et, uint16_t p_param, uint8_t devNum, CanAddress p_ba, const char *_ecv_array format, va_list vargs) noexcept + : next(pnext), param(p_param), type(et), boardAddress(p_ba), deviceNumber(devNum), isBeingProcessed(false) { + text.vprintf(format, vargs); } -void Event::AppendText(const StringRef &str) const noexcept +// Queue an event unless we have a similar event pending already. Returns true if the event was added. +// The event list is held in priority order, lowest numbered (highest priority) events first. +/*static*/ bool Event::AddEvent(EventType et, uint16_t p_param, uint8_t devNum, CanAddress p_ba, const char *_ecv_array format, va_list vargs) noexcept { - // First append the event type with underscores changed to spaces - const char *p = type.ToString(); - while (*p != 0) - { - str.cat((*p == '_') ? ' ' : *p); - ++p; - } + // Search for similar events already pending or being processed. + // An event is 'similar' if it has the same type, device number and parameter even if the text is different. + TaskCriticalSectionLocker lock; - // Now append further details of the event - switch (type.ToBaseType()) + Event** pe = &eventsPending; + while (*pe != nullptr && (et >= (*pe)->type || (*pe)->isBeingProcessed)) // while the next event in the list has same or higher priority than the new one { - case EventType::Heater_fault: - str.catf("on heater %u: %s", deviceNumber, HeaterFaultType(param.heaterFaultStatus).ToString()); - break; - - case EventType::Driver_error: - case EventType::Driver_warning: + if (et == (*pe)->type && devNum == (*pe)->deviceNumber #if SUPPORT_CAN_EXPANSION - str.catf(" on %u.%u", boardAddress, deviceNumber); -#else - str.catf(" on %u", deviceNumber); + && p_ba == (*pe)->boardAddress #endif - param.driverStatus.AppendText(str, (type == EventType::Driver_error) ? 2 : 1); - break; + ) + { + return false; // there is a similar event already in the queue + } + pe = &((*pe)->next); + } + + // We didn't find a similar event, so add the new one + *pe = new Event(*pe, et, p_param, p_ba, devNum, format, vargs); + return true; +} - case EventType::Filament_error: - str.catf(" on extruder %u: %s", deviceNumber, FilamentSensorStatus(param.filamentStatus).ToString()); - break; +// Get the highest priority event and mark it as being serviced +/*static*/ bool Event::StartProcessing() noexcept +{ + TaskCriticalSectionLocker lock; - case EventType::Main_board_power_failure: - break; + Event * const ev = eventsPending; + if (ev == nullptr) + { + return false; + } + ev->isBeingProcessed = true; + return true; +} - case EventType::Trigger: - str.catf(" %u activated", deviceNumber); - break; +// Get the name of the macro that we run when this event occurs +/*static*/ void Event::GetMacroFileName(const StringRef& fname) noexcept +{ + const Event * const ep = eventsPending; + if (ep != nullptr && ep->isBeingProcessed) + { + fname.copy(ep->type.ToString()); + fname.cat(".g"); + } +} - case EventType::Mcu_temperature_warning: +// Get the macro parameters for the current event, excluding the S parameter which the caller will add +/*static*/ void Event::GetParameters(VariableSet& vars) noexcept +{ + const Event * const ep = eventsPending; + if (ep != nullptr && ep->isBeingProcessed) + { + vars.InsertNewParameter("D", ExpressionValue((int32_t)(ep->deviceNumber))); #if SUPPORT_CAN_EXPANSION - str.catf("on board %u: temperature %.1fC", boardAddress, (double)param.fVal); -#else - str.catf(": temperature %.1fC", (double)param.fVal); + vars.InsertNewParameter("B", ExpressionValue((int32_t)(ep->boardAddress))); #endif - break; + vars.InsertNewParameter("P", ExpressionValue((int32_t)(ep->param))); } } -// Append the name of the macro that we run when this event occurs -void Event::GetMacroFileName(const StringRef& fname) const noexcept +// Get the default action for the current event +/*static*/ Event::DefaultAction Event::GetDefaultAction() noexcept { - const char *p = type.ToString(); - fname.cat((char)tolower(*p++)); - while (*p != 0) + const Event * const ep = eventsPending; + if (ep != nullptr && ep->isBeingProcessed) { - if (*p == '_' && p[1] != 0) + switch (ep->type.RawValue()) { - fname.cat(toupper(p[1])); - p += 2; + case EventType::heater_fault: + case EventType::filament_error: + return DefaultAction::pauseWithMacro; + + case EventType::driver_error: + return DefaultAction::pauseNoMacro; + + default: + break; } - else + } + return DefaultAction::none; +} + +// Mark the highest priority event as completed +/*static*/ void Event::FinishedProcessing() noexcept +{ + TaskCriticalSectionLocker lock; + + const Event *ev = eventsPending; + if (ev != nullptr && ev->isBeingProcessed) + { + eventsPending = ev->next; + delete ev; + } +} + +// Get a description of the current event +/*static*/ MessageType Event::GetTextDescription(const StringRef& str) noexcept +{ + const Event * const ep = eventsPending; + if (ep != nullptr && ep->isBeingProcessed) + { + switch (ep->type.RawValue()) { - fname.cat(*p++); + case EventType::heater_fault: + { + const char *_ecv_array heaterFaultText = HeaterFaultText[max<size_t>(ep->param, ARRAY_SIZE(HeaterFaultText) - 1)]; + str.printf("Heater %u fault: %s%s", ep->deviceNumber, heaterFaultText, ep->text.c_str()); + } + return ErrorMessage; + + case EventType::filament_error: + str.printf("Filament error on extruder %u: %s", ep->deviceNumber, FilamentSensorStatus(ep->param).ToString()); + return ErrorMessage; + + case EventType::driver_error: +#if SUPPORT_CAN_EXPANSION + str.printf("Driver %u.%u error: %s", ep->boardAddress, ep->deviceNumber, ep->text.c_str()); +#else + str.printf("Driver %u error: %s", deviceNumber, ep->text.c_str()); +#endif + return ErrorMessage; + + case EventType::driver_warning: +#if SUPPORT_CAN_EXPANSION + str.printf("Driver %u.%u warning: %s", ep->boardAddress, ep->deviceNumber, ep->text.c_str()); +#else + str.printf("Driver %u warning: %s", deviceNumber, ep->text.c_str()); +#endif + return WarningMessage; + + case EventType::driver_stall: +#if SUPPORT_CAN_EXPANSION + str.printf("Driver %u.%u stall", ep->boardAddress, ep->deviceNumber); +#else + str.printf("Driver %u stall", ep->deviceNumber); +#endif + return WarningMessage; + + case EventType::main_board_power_fail: + // This does not currently generate an event, so no text + return ErrorMessage; + + case EventType::mcu_temperature_warning: +#if SUPPORT_CAN_EXPANSION + str.printf("MCU temperature warning from board %u: temperature %.1fC", ep->boardAddress, (double)((float)ep->param/10)); +#else + str.printf("MCU temperature warning: temperature %.1fC", (double)((float)ep->param/10)); +#endif + return WarningMessage; } } - fname.cat(".g"); + str.copy("Internal error in Event"); + return ErrorMessage; } // End diff --git a/src/Platform/Event.h b/src/Platform/Event.h index 46e26107..7fc78e14 100644 --- a/src/Platform/Event.h +++ b/src/Platform/Event.h @@ -3,6 +3,19 @@ * * Created on: 18 Oct 2021 * Author: David + * + * This class manages events. An event is an occurrence reported by a machine sensor that may need to be reported or may require action to be taken. + * The various event types are listed in file CANlib/RRF3Common.h. + * When an event on a main board occurs, a corresponding Event object is created and added to the event queue, unless there is a similar event already in the queue. + * When an event on an expansion board occurs, it is transmitted to the main board over CAN and then treated in the same way as a main board event. + * The event queue is kept in priority order, with the highest priority event at the head of the queue; except that if the event at the head of the queue is + * being processed, it remains at the head of the queue until processing is complete. Leaving it in the queue while it is being processed allows other similar + * events to be ignored. + * + * The event queue is emptied by the AutoPause GCode channel. It flags the entry at the head of the queue as being processed, takes whatever action is needed, + * and removes it from the queue. + * + * A main board power failure bypasses the event mechanism. Triggers do not use the event mechanism. */ #ifndef SRC_PLATFORM_EVENT_H_ @@ -13,28 +26,59 @@ #include <CoreTypes.h> #include <RRF3Common.h> #include <General/FreelistManager.h> -#include <General/StringRef.h> +#include <General/String.h> +#include <General/SafeVsnprintf.h> +#include <Platform/MessageType.h> + +class VariableSet; class Event { public: + // Type of default action for when there is no macro file to process the event + enum class DefaultAction + { + none, // do nothing other than logging ir + pauseNoMacro, // pause, but don't run pause.g + pauseWithMacro // pause, running pause.g + }; + void* operator new(size_t sz) noexcept { return FreelistManager::Allocate<Event>(); } void operator delete(void* p) noexcept { FreelistManager::Release<Event>(p); } - Event(Event *p_next, EventType et, EventParameter p_param, CanAddress p_ba, uint8_t devNum) noexcept; + // Get a description of the current event and return the appropriate message type + static MessageType GetTextDescription(const StringRef& str) noexcept; + + // Queue an event, or release it if we have a similar event pending already. Returns true if the event was added, false if it was released. + static bool AddEvent(EventType et, uint16_t p_param, CanAddress p_ba, uint8_t devNum, const char *_ecv_array format, va_list vargs) noexcept; - // Append a description of the event to a string - void AppendText(const StringRef& str) const noexcept; + // Get the highest priority event if there is one start processing it + static bool StartProcessing() noexcept; // Get the name of the macro that we run when this event occurs - void GetMacroFileName(const StringRef& fname) const noexcept; + static void GetMacroFileName(const StringRef& fname) noexcept; + + // Get the parameters for invoking the macro file the current event + static void GetParameters(VariableSet& vars) noexcept; + + // Get the default action for the current event + static DefaultAction GetDefaultAction() noexcept; + + // Mark the highest priority event as completed + static void FinishedProcessing() noexcept; private: - Event *next; // next event in a linked list - EventParameter param; // details about the event - EventType type; // what type of event it is - CanAddress boardAddress; // which board it came from - uint8_t deviceNumber; // which device raised it + Event(Event *_ecv_null pnext, EventType et, uint16_t p_param, uint8_t devNum, CanAddress p_ba, const char *_ecv_array format, va_list vargs) noexcept; + + Event *_ecv_null next; // next event in a linked list + uint16_t param; // details about the event, e.g. for a heater fault it is the type of the fault + EventType type; // what type of event it is + CanAddress boardAddress; // which board it came from + uint8_t deviceNumber; // which device raised it, e.g. heater number, driver number, trigger number + volatile bool isBeingProcessed; // true if this event is being processed, so it must remain at the head of the queue + String<50> text; // additional info to display to the user + + static Event * _ecv_null eventsPending; // linked list of events waiting to be processed }; #endif /* SRC_PLATFORM_EVENT_H_ */ diff --git a/src/Platform/EventManager.cpp b/src/Platform/EventManager.cpp deleted file mode 100644 index e0d4c3f5..00000000 --- a/src/Platform/EventManager.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * EventManager.cpp - * - * Created on: 19 Oct 2021 - * Author: David - */ - -#include "EventManager.h" - -EventManager::EventManager() : eventsPending(nullptr), lastWarningMillis(0) -{ -} - -// End diff --git a/src/Platform/EventManager.h b/src/Platform/EventManager.h deleted file mode 100644 index 8f3d23c9..00000000 --- a/src/Platform/EventManager.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * EventManager.h - * - * Created on: 19 Oct 2021 - * Author: David - */ - -#ifndef SRC_PLATFORM_EVENTMANAGER_H_ -#define SRC_PLATFORM_EVENTMANAGER_H_ - -#include "Event.h" - -class EventManager -{ -public: - EventManager(); - -private: - Event *eventsPending; // linked list of pending events - uint32_t lastWarningMillis; // when we last sent a warning message -}; - -#endif /* SRC_PLATFORM_EVENTMANAGER_H_ */ diff --git a/src/Platform/Logger.h b/src/Platform/Logger.h index b2eee38b..b612bb7d 100644 --- a/src/Platform/Logger.h +++ b/src/Platform/Logger.h @@ -40,7 +40,7 @@ public: private: NamedEnum(MessageLogLevel, uint8_t, debug, info, warn, off); - MessageLogLevel GetMessageLogLevel(MessageType mt) const noexcept { return (MessageLogLevel) ((mt & MessageType::LogOff)>>30); } + MessageLogLevel GetMessageLogLevel(MessageType mt) const noexcept { return (MessageLogLevel) ((mt & MessageType::LogLevelMask) >> MessageType::LogLevelShift); } static const uint8_t LogEnabledThreshold = 3; diff --git a/src/Platform/MessageType.h b/src/Platform/MessageType.h index 482732bb..4da31ab6 100644 --- a/src/Platform/MessageType.h +++ b/src/Platform/MessageType.h @@ -41,8 +41,11 @@ enum MessageType : uint32_t RawMessageFlag = 0x8000000u, // Do not encapsulate this message BinaryCodeReplyFlag = 0x10000000u, // This message comes from a binary G-Code buffer PushFlag = 0x20000000u, // There is more to come; the message has been truncated + LogMessageLowBit = 0x40000000u, // Log level consists of two bits this is the low bit LogMessageHighBit = 0x80000000u, // Log level consists of two bits this is the high bit + LogLevelMask = 0xC0000000u, // Mask for all the log level bits + LogLevelShift = 30, // How many bits we have to shift a MessageType right by to get the logging level // Common combinations NoDestinationMessage = 0u, // A message that is going nowhere diff --git a/src/Platform/Platform.h b/src/Platform/Platform.h index 201e10f2..33d748da 100644 --- a/src/Platform/Platform.h +++ b/src/Platform/Platform.h @@ -31,7 +31,6 @@ Licence: GPL #include <Heating/TemperatureError.h> #include "OutputMemory.h" #include "UniqueId.h" -#include "EventManager.h" #include <Storage/FileStore.h> #include <Storage/FileData.h> #include <Storage/MassStorage.h> // must be after Pins.h because it needs NumSdCards defined @@ -873,7 +872,6 @@ private: #endif // Event handling - EventManager eventManager; uint32_t lastDriverPollMillis; // when we last checked the drivers and voltage monitoring #ifdef DUET3MINI |