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

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/RepRap.cpp')
-rw-r--r--src/RepRap.cpp1581
1 files changed, 1581 insertions, 0 deletions
diff --git a/src/RepRap.cpp b/src/RepRap.cpp
new file mode 100644
index 00000000..004b620c
--- /dev/null
+++ b/src/RepRap.cpp
@@ -0,0 +1,1581 @@
+#include "RepRap.h"
+
+#include "Network.h"
+#include "Movement/Move.h"
+#include "GCodes/GCodes.h"
+#include "Heating/Heat.h"
+#include "Platform.h"
+#include "PrintMonitor.h"
+#include "Tool.h"
+#include "Webserver.h"
+#include "Version.h"
+
+#ifndef __RADDS__
+# include "sam/drivers/hsmci/hsmci.h"
+#endif
+
+// Callback function from the hsmci driver, called while it is waiting for an SD card operation to complete
+extern "C" void hsmciIdle()
+{
+ if (reprap.GetSpinningModule() != moduleNetwork) // I don't think this should ever be false because the Network module doesn't do file access, but just in case...
+ {
+ reprap.GetNetwork()->Spin(false);
+ }
+}
+
+// RepRap member functions.
+
+// Do nothing more in the constructor; put what you want in RepRap:Init()
+
+RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastWarningMillis(0), activeExtruders(0),
+ activeToolHeaters(0), ticksInSpinState(0), spinningModule(noModule), debug(0), stopped(false),
+ active(false), resetting(false), processingConfig(true), beepFrequency(0), beepDuration(0)
+{
+ OutputBuffer::Init();
+ platform = new Platform();
+ network = new Network(platform);
+ webserver = new Webserver(platform, network);
+ gCodes = new GCodes(platform, webserver);
+ move = new Move(platform, gCodes);
+ heat = new Heat(platform);
+
+#if SUPPORT_ROLAND
+ roland = new Roland(platform);
+#endif
+
+ printMonitor = new PrintMonitor(platform, gCodes);
+
+ SetPassword(DEFAULT_PASSWORD);
+ SetName(DEFAULT_NAME);
+ message[0] = 0;
+}
+
+void RepRap::Init()
+{
+ // All of the following init functions must execute reasonably quickly before the watchdog times us out
+ platform->Init();
+ gCodes->Init();
+ network->Init();
+ webserver->Init();
+ move->Init();
+ heat->Init();
+#if SUPPORT_ROLAND
+ roland->Init();
+#endif
+ printMonitor->Init();
+ Platform::EnableWatchdog(); // do this after all init calls are made
+ active = true; // must do this before we start the network, else the watchdog may time out
+
+ platform->MessageF(HOST_MESSAGE, "%s Version %s dated %s\n", FIRMWARE_NAME, VERSION, DATE);
+
+ // Run the configuration file
+ const char *configFile = platform->GetConfigFile();
+ platform->Message(HOST_MESSAGE, "\nExecuting ");
+ if (platform->GetMassStorage()->FileExists(platform->GetSysDir(), configFile))
+ {
+ platform->MessageF(HOST_MESSAGE, "%s...", platform->GetConfigFile());
+ }
+ else
+ {
+ platform->MessageF(HOST_MESSAGE, "%s (no configuration file found)...", platform->GetDefaultFile());
+ configFile = platform->GetDefaultFile();
+ }
+
+ if (gCodes->RunConfigFile(configFile))
+ {
+ while (gCodes->IsDaemonBusy())
+ {
+ // GCodes::Spin will read the macro and ensure DoingFileMacro returns false when it's done
+ Spin();
+ }
+ platform->Message(HOST_MESSAGE, "Done!\n");
+ }
+ else
+ {
+ platform->Message(HOST_MESSAGE, "Error, not found\n");
+ }
+ processingConfig = false;
+
+ // Enable network (unless it's disabled)
+#ifdef DUET_NG
+ network->Activate(); // Need to do this here, as the configuration GCodes may set IP address etc.
+ if (!network->IsEnabled())
+ {
+ platform->Message(HOST_MESSAGE, "Network disabled.\n");
+ }
+#else
+ if (network->IsEnabled())
+ {
+ // Need to do this here, as the configuration GCodes may set IP address etc.
+ platform->Message(HOST_MESSAGE, "Starting network...\n");
+ network->Enable();
+ }
+ else
+ {
+ platform->Message(HOST_MESSAGE, "Network disabled.\n");
+ }
+#endif
+
+#ifndef __RADDS__
+ hsmci_set_idle_func(hsmciIdle);
+#endif
+ platform->MessageF(HOST_MESSAGE, "%s is up and running.\n", FIRMWARE_NAME);
+ fastLoop = FLT_MAX;
+ slowLoop = 0.0;
+ lastTime = platform->Time();
+}
+
+void RepRap::Exit()
+{
+ active = false;
+ heat->Exit();
+ move->Exit();
+ gCodes->Exit();
+ webserver->Exit();
+ network->Exit();
+ platform->Message(GENERIC_MESSAGE, "RepRap class exited.\n");
+ platform->Exit();
+}
+
+void RepRap::Spin()
+{
+ if(!active)
+ return;
+
+ spinningModule = modulePlatform;
+ ticksInSpinState = 0;
+ platform->Spin();
+
+ spinningModule = moduleNetwork;
+ ticksInSpinState = 0;
+ network->Spin(true);
+
+ spinningModule = moduleWebserver;
+ ticksInSpinState = 0;
+ webserver->Spin();
+
+ spinningModule = moduleGcodes;
+ ticksInSpinState = 0;
+ gCodes->Spin();
+
+ spinningModule = moduleMove;
+ ticksInSpinState = 0;
+ move->Spin();
+
+ spinningModule = moduleHeat;
+ ticksInSpinState = 0;
+ heat->Spin();
+
+#if SUPPORT_ROLAND
+ spinningModule = moduleRoland;
+ ticksInSpinState = 0;
+ roland->Spin();
+#endif
+
+ spinningModule = modulePrintMonitor;
+ ticksInSpinState = 0;
+ printMonitor->Spin();
+
+ spinningModule = noModule;
+ ticksInSpinState = 0;
+
+ // Check if we need to display a cold extrusion warning
+ const uint32_t now = millis();
+ if (now - lastWarningMillis >= MinimumWarningInterval)
+ {
+ for (Tool *t = toolList; t != nullptr; t = t->Next())
+ {
+ if (t->DisplayColdExtrudeWarning())
+ {
+ platform->MessageF(GENERIC_MESSAGE, "Warning: Tool %d was not driven because its heater temperatures were not high enough or it has a heater fault\n", t->myNumber);
+ lastWarningMillis = now;
+ }
+ }
+ }
+
+ // Keep track of the loop time
+ const float t = platform->Time();
+ const float dt = t - lastTime;
+ if (dt < fastLoop)
+ {
+ fastLoop = dt;
+ }
+ if (dt > slowLoop)
+ {
+ slowLoop = dt;
+ }
+ lastTime = t;
+}
+
+void RepRap::Timing(MessageType mtype)
+{
+ platform->MessageF(mtype, "Slowest main loop (seconds): %f; fastest: %f\n", slowLoop, fastLoop);
+ fastLoop = FLT_MAX;
+ slowLoop = 0.0;
+}
+
+void RepRap::Diagnostics(MessageType mtype)
+{
+ platform->Message(mtype, "=== Diagnostics ===\n");
+ OutputBuffer::Diagnostics(mtype);
+ platform->Diagnostics(mtype); // this includes a call to our Timing() function
+ move->Diagnostics(mtype);
+ heat->Diagnostics(mtype);
+ gCodes->Diagnostics(mtype);
+ network->Diagnostics(mtype);
+ webserver->Diagnostics(mtype);
+}
+
+// Turn off the heaters, disable the motors, and deactivate the Heat and Move classes. Leave everything else working.
+void RepRap::EmergencyStop()
+{
+ stopped = true;
+
+ // Do not turn off ATX power here. If the nozzles are still hot, don't risk melting any surrounding parts...
+ //platform->SetAtxPower(false);
+
+ Tool* tool = toolList;
+ while (tool != nullptr)
+ {
+ tool->Standby();
+ tool = tool->Next();
+ }
+
+ heat->Exit();
+ for(size_t heater = 0; heater < HEATERS; heater++)
+ {
+ platform->SetHeater(heater, 0.0);
+ }
+
+ // We do this twice, to avoid an interrupt switching a drive back on. move->Exit() should prevent interrupts doing this.
+ for(int i = 0; i < 2; i++)
+ {
+ move->Exit();
+ for(size_t drive = 0; drive < DRIVES; drive++)
+ {
+ platform->SetMotorCurrent(drive, 0.0, false);
+ platform->DisableDrive(drive);
+ }
+ }
+}
+
+void RepRap::SetDebug(Module m, bool enable)
+{
+ if (enable)
+ {
+ debug |= (1 << m);
+ }
+ else
+ {
+ debug &= ~(1 << m);
+ }
+ PrintDebug();
+}
+
+void RepRap::SetDebug(bool enable)
+{
+ debug = (enable) ? 0xFFFF : 0;
+}
+
+void RepRap::PrintDebug()
+{
+ if (debug != 0)
+ {
+ platform->Message(GENERIC_MESSAGE, "Debugging enabled for modules:");
+ for (size_t i = 0; i < numModules; i++)
+ {
+ if ((debug & (1 << i)) != 0)
+ {
+ platform->MessageF(GENERIC_MESSAGE, " %s(%u)", moduleName[i], i);
+ }
+ }
+ platform->Message(GENERIC_MESSAGE, "\nDebugging disabled for modules:");
+ for (size_t i = 0; i < numModules; i++)
+ {
+ if ((debug & (1 << i)) == 0)
+ {
+ platform->MessageF(GENERIC_MESSAGE, " %s(%u)", moduleName[i], i);
+ }
+ }
+ platform->Message(GENERIC_MESSAGE, "\n");
+ }
+ else
+ {
+ platform->Message(GENERIC_MESSAGE, "Debugging disabled\n");
+ }
+}
+
+// Add a tool.
+// Prior to calling this, delete any existing tool with the same number
+// The tool list is maintained in tool number order.
+void RepRap::AddTool(Tool* tool)
+{
+ Tool** t = &toolList;
+ while(*t != nullptr && (*t)->Number() < tool->Number())
+ {
+ t = &((*t)->next);
+ }
+ tool->next = *t;
+ *t = tool;
+ tool->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters);
+ platform->UpdateConfiguredHeaters();
+}
+
+void RepRap::DeleteTool(Tool* tool)
+{
+ // Must have a valid tool...
+ if (tool == nullptr)
+ {
+ return;
+ }
+
+ // Deselect it if necessary
+ if (GetCurrentTool() == tool)
+ {
+ SelectTool(-1);
+ }
+
+ // Switch off any associated heater
+ for (size_t i = 0; i < tool->HeaterCount(); i++)
+ {
+ reprap.GetHeat()->SwitchOff(tool->Heater(i));
+ }
+
+ // Purge any references to this tool
+ for (Tool **t = &toolList; *t != nullptr; t = &((*t)->next))
+ {
+ if (*t == tool)
+ {
+ *t = tool->next;
+ break;
+ }
+ }
+
+ // Delete it
+ Tool::Delete(tool);
+
+ // Update the number of active heaters and extruder drives
+ activeExtruders = activeToolHeaters = 0;
+ for (Tool *t = toolList; t != nullptr; t = t->Next())
+ {
+ t->UpdateExtruderAndHeaterCount(activeExtruders, activeToolHeaters);
+ }
+ platform->UpdateConfiguredHeaters();
+}
+
+void RepRap::SelectTool(int toolNumber)
+{
+ Tool* tool = toolList;
+
+ while(tool != nullptr)
+ {
+ if (tool->Number() == toolNumber)
+ {
+ tool->Activate(currentTool);
+ currentTool = tool;
+ return;
+ }
+ tool = tool->Next();
+ }
+
+ // Selecting a non-existent tool is valid. It sets them all to standby.
+ if (currentTool != nullptr)
+ {
+ StandbyTool(currentTool->Number());
+ }
+ currentTool = nullptr;
+}
+
+void RepRap::PrintTool(int toolNumber, StringRef& reply) const
+{
+ Tool* tool = GetTool(toolNumber);
+ if (tool != nullptr)
+ {
+ tool->Print(reply);
+ }
+ else
+ {
+ reply.copy("Error: Attempt to print details of non-existent tool.\n");
+ }
+}
+
+void RepRap::StandbyTool(int toolNumber)
+{
+ Tool* tool = GetTool(toolNumber);
+ if (tool != nullptr)
+ {
+ tool->Standby();
+ if (currentTool == tool)
+ {
+ currentTool = nullptr;
+ }
+ }
+ else
+ {
+ platform->MessageF(GENERIC_MESSAGE, "Error: Attempt to standby a non-existent tool: %d.\n", toolNumber);
+ }
+}
+
+Tool* RepRap::GetTool(int toolNumber) const
+{
+ Tool* tool = toolList;
+ while(tool != nullptr)
+ {
+ if (tool->Number() == toolNumber)
+ {
+ return tool;
+ }
+ tool = tool->Next();
+ }
+ return nullptr; // Not an error
+}
+
+// Get the current tool, or failing that the default tool. May return nullptr if we can't
+// Called when a M104 or M109 command doesn't specify a tool number.
+Tool* RepRap::GetCurrentOrDefaultTool() const
+{
+ // If a tool is already selected, use that one, else use the lowest-numbered tool which is the one at the start of the tool list
+ return (currentTool != nullptr) ? currentTool : toolList;
+}
+
+void RepRap::SetToolVariables(int toolNumber, const float* standbyTemperatures, const float* activeTemperatures)
+{
+ Tool* tool = GetTool(toolNumber);
+ if (tool != nullptr)
+ {
+ tool->SetVariables(standbyTemperatures, activeTemperatures);
+ }
+ else
+ {
+ platform->MessageF(GENERIC_MESSAGE, "Error: Attempt to set variables for a non-existent tool: %d.\n", toolNumber);
+ }
+}
+
+bool RepRap::IsHeaterAssignedToTool(int8_t heater) const
+{
+ for(Tool *tool = toolList; tool != nullptr; tool = tool->Next())
+ {
+ for(size_t i = 0; i < tool->HeaterCount(); i++)
+ {
+ if (tool->Heater(i) == heater)
+ {
+ // It's already in use by some tool
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+unsigned int RepRap::GetNumberOfContiguousTools() const
+{
+ unsigned int numTools = 0;
+ while (GetTool(numTools) != nullptr)
+ {
+ ++numTools;
+ }
+ return numTools;
+}
+
+void RepRap::Tick()
+{
+ if (active && !resetting)
+ {
+ platform->Tick();
+ ++ticksInSpinState;
+ if (ticksInSpinState >= 20000) // if we stall for 20 seconds, save diagnostic data and reset
+ {
+ resetting = true;
+ for(size_t i = 0; i < HEATERS; i++)
+ {
+ platform->SetHeater(i, 0.0);
+ }
+ for(size_t i = 0; i < DRIVES; i++)
+ {
+ platform->DisableDrive(i);
+ // We can't set motor currents to 0 here because that requires interrupts to be working, and we are in an ISR
+ }
+
+ platform->SoftwareReset((uint16_t)SoftwareResetReason::stuckInSpin);
+ }
+ }
+}
+
+// Get the JSON status response for the web server (or later for the M105 command).
+// Type 1 is the ordinary JSON status response.
+// Type 2 is the same except that static parameters are also included.
+// Type 3 is the same but instead of static parameters we report print estimation values.
+OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
+{
+ // Need something to write to...
+ OutputBuffer *response;
+ if (!OutputBuffer::Allocate(response))
+ {
+ // Should never happen
+ return nullptr;
+ }
+
+ // Machine status
+ char ch = GetStatusCharacter();
+ response->printf("{\"status\":\"%c\",\"coords\":{", ch);
+
+ // Coordinates
+ const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
+ {
+ float liveCoordinates[DRIVES + 1];
+#if SUPPORT_ROLAND
+ if (roland->Active())
+ {
+ roland->GetCurrentRolandPosition(liveCoordinates);
+ }
+ else
+#endif
+ {
+ move->LiveCoordinates(liveCoordinates, GetCurrentXAxes());
+ }
+
+ if (currentTool != nullptr)
+ {
+ const float *offset = currentTool->GetOffset();
+ for (size_t i = 0; i < numAxes; ++i)
+ {
+ liveCoordinates[i] += offset[i];
+ }
+ }
+
+ // Homed axes
+ response->cat("\"axesHomed\":");
+ ch = '[';
+ for (size_t axis = 0; axis < numAxes; ++axis)
+ {
+ response->catf("%c%d", ch, (gCodes->GetAxisIsHomed(axis)) ? 1 : 0);
+ ch = ',';
+ }
+
+ // Actual and theoretical extruder positions since power up, last G92 or last M23
+ response->catf("],\"extr\":"); // announce actual extruder positions
+ ch = '[';
+ for (size_t extruder = 0; extruder < GetExtrudersInUse(); extruder++)
+ {
+ response->catf("%c%.1f", ch, liveCoordinates[numAxes + extruder]);
+ ch = ',';
+ }
+ if (ch == '[')
+ {
+ response->cat("[");
+ }
+
+ // XYZ positions
+ response->cat("],\"xyz\":");
+ if (!gCodes->AllAxesAreHomed() && move->IsDeltaMode())
+ {
+ // If in Delta mode, skip these coordinates if some axes are not homed
+ response->cat("[0.00,0.00,0.00");
+ }
+ else
+ {
+ // On Cartesian printers, the live coordinates are (usually) valid
+ ch = '[';
+ for (size_t axis = 0; axis < numAxes; axis++)
+ {
+ response->catf("%c%.2f", ch, liveCoordinates[axis]);
+ ch = ',';
+ }
+ }
+ }
+
+ // Current tool number
+ const int toolNumber = (currentTool == nullptr) ? -1 : currentTool->Number();
+ response->catf("]},\"currentTool\":%d", toolNumber);
+
+ // Output - only reported once
+ {
+ bool sendBeep = (beepDuration != 0 && beepFrequency != 0);
+ bool sendMessage = (message[0] != 0);
+ if (sendBeep || sendMessage)
+ {
+ response->cat(",\"output\":{");
+
+ // Report beep values
+ if (sendBeep)
+ {
+ response->catf("\"beepDuration\":%d,\"beepFrequency\":%d", beepDuration, beepFrequency);
+ if (sendMessage)
+ {
+ response->cat(",");
+ }
+ beepFrequency = beepDuration = 0;
+ }
+
+ // Report message
+ if (sendMessage)
+ {
+ response->cat("\"message\":");
+ response->EncodeString(message, ARRAY_SIZE(message), false);
+ message[0] = 0;
+ }
+ response->cat("}");
+ }
+ }
+
+ // Parameters
+ {
+ // ATX power
+ response->catf(",\"params\":{\"atxPower\":%d", platform->AtxPower() ? 1 : 0);
+
+ // Cooling fan value
+ response->cat(",\"fanPercent\":");
+ ch = '[';
+ for(size_t i = 0; i < NUM_FANS; i++)
+ {
+ response->catf("%c%.2f", ch, platform->GetFanValue(i) * 100.0);
+ ch = ',';
+ }
+
+ // Speed and Extrusion factors
+ response->catf("],\"speedFactor\":%.2f,\"extrFactors\":", gCodes->GetSpeedFactor() * 100.0);
+ ch = '[';
+ for (size_t extruder = 0; extruder < GetExtrudersInUse(); extruder++)
+ {
+ response->catf("%c%.2f", ch, gCodes->GetExtrusionFactor(extruder) * 100.0);
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+ response->catf(",\"babystep\":%.03f}", gCodes->GetBabyStepOffset());
+ }
+
+ // G-code reply sequence for webserver (seqence number for AUX is handled later)
+ if (source == ResponseSource::HTTP)
+ {
+ response->catf(",\"seq\":%d", webserver->GetReplySeq());
+
+ // There currently appears to be no need for this one, so skip it
+ //response->catf(",\"buff\":%u", webserver->GetGCodeBufferSpace(WebSource::HTTP));
+ }
+
+ /* Sensors */
+ {
+ response->cat(",\"sensors\":{");
+
+ // Probe
+ const int v0 = platform->GetZProbeReading();
+ int v1, v2;
+ switch (platform->GetZProbeSecondaryValues(v1, v2))
+ {
+ case 1:
+ response->catf("\"probeValue\":%d,\"probeSecondary\":[%d]", v0, v1);
+ break;
+ case 2:
+ response->catf("\"probeValue\":%d,\"probeSecondary\":[%d,%d]", v0, v1, v2);
+ break;
+ default:
+ response->catf("\"probeValue\":%d", v0);
+ break;
+ }
+
+ // Fan RPM
+ response->catf(",\"fanRPM\":%d}", static_cast<unsigned int>(platform->GetFanRPM()));
+ }
+
+ /* Temperatures */
+ {
+ response->cat(",\"temps\":{");
+
+ /* Bed */
+ const int8_t bedHeater = heat->GetBedHeater();
+ if (bedHeater != -1)
+ {
+ response->catf("\"bed\":{\"current\":%.1f,\"active\":%.1f,\"state\":%d},",
+ heat->GetTemperature(bedHeater), heat->GetActiveTemperature(bedHeater),
+ heat->GetStatus(bedHeater));
+ }
+
+ /* Chamber */
+ const int8_t chamberHeater = heat->GetChamberHeater();
+ if (chamberHeater != -1)
+ {
+ response->catf("\"chamber\":{\"current\":%.1f,", heat->GetTemperature(chamberHeater));
+ response->catf("\"active\":%.1f,", heat->GetActiveTemperature(chamberHeater));
+ response->catf("\"state\":%d},", static_cast<int>(heat->GetStatus(chamberHeater)));
+ }
+
+ /* Heads */
+ {
+ response->cat("\"heads\":{\"current\":");
+
+ // Current temperatures
+ ch = '[';
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%.1f", ch, heat->GetTemperature(heater));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Active temperatures
+ response->catf(",\"active\":");
+ ch = '[';
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%.1f", ch, heat->GetActiveTemperature(heater));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Standby temperatures
+ response->catf(",\"standby\":");
+ ch = '[';
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%.1f", ch, heat->GetStandbyTemperature(heater));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Heater statuses (0=off, 1=standby, 2=active, 3=fault)
+ response->cat(",\"state\":");
+ ch = '[';
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%d", ch, static_cast<int>(heat->GetStatus(heater)));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+ }
+ response->cat("}}");
+ }
+
+ // Time since last reset
+ response->catf(",\"time\":%.1f", platform->Time());
+
+ /* Extended Status Response */
+ if (type == 2)
+ {
+ // Cold Extrude/Retract
+ response->catf(",\"coldExtrudeTemp\":%1.f", heat->ColdExtrude() ? 0 : HOT_ENOUGH_TO_EXTRUDE);
+ response->catf(",\"coldRetractTemp\":%1.f", heat->ColdExtrude() ? 0 : HOT_ENOUGH_TO_RETRACT);
+
+ // Maximum hotend temperature - DWC just wants the highest one
+ response->catf(",\"tempLimit\":%1.f", heat->GetHighestTemperatureLimit());
+
+ // Endstops
+ uint16_t endstops = 0;
+ for(size_t drive = 0; drive < DRIVES; drive++)
+ {
+ EndStopHit stopped = platform->Stopped(drive);
+ if (stopped == EndStopHit::highHit || stopped == EndStopHit::lowHit)
+ {
+ endstops |= (1 << drive);
+ }
+ }
+ response->catf(",\"endstops\":%d", endstops);
+
+ // Firmware name, machine geometry and number of axes
+ response->catf(",\"firmwareName\":\"%s\",\"geometry\":\"%s\",\"axes\":%u", FIRMWARE_NAME, move->GetGeometryString(), numAxes);
+
+ // Total and mounted volumes
+ size_t mountedCards = 0;
+ for(size_t i = 0; i < NumSdCards; i++)
+ {
+ if (platform->GetMassStorage()->IsDriveMounted(i))
+ {
+ mountedCards |= (1 << i);
+ }
+ }
+ response->catf(",\"volumes\":%u,\"mountedVolumes\":%u", NumSdCards, mountedCards);
+
+ // Machine name
+ response->cat(",\"name\":");
+ response->EncodeString(myName, ARRAY_SIZE(myName), false);
+
+ /* Probe */
+ {
+ const ZProbeParameters probeParams = platform->GetCurrentZProbeParameters();
+
+ // Trigger threshold
+ response->catf(",\"probe\":{\"threshold\":%d", probeParams.adcValue);
+
+ // Trigger height
+ response->catf(",\"height\":%.2f", probeParams.height);
+
+ // Type
+ response->catf(",\"type\":%d}", platform->GetZProbeType());
+ }
+
+ /* Tool Mapping */
+ {
+ response->cat(",\"tools\":[");
+ for(Tool *tool = toolList; tool != nullptr; tool = tool->Next())
+ {
+ // Heaters
+ response->catf("{\"number\":%d,\"heaters\":[", tool->Number());
+ for(size_t heater=0; heater<tool->HeaterCount(); heater++)
+ {
+ response->catf("%d", tool->Heater(heater));
+ if (heater + 1 < tool->HeaterCount())
+ {
+ response->cat(",");
+ }
+ }
+
+ // Extruder drives
+ response->cat("],\"drives\":[");
+ for(size_t drive=0; drive<tool->DriveCount(); drive++)
+ {
+ response->catf("%d", tool->Drive(drive));
+ if (drive + 1 < tool->DriveCount())
+ {
+ response->cat(",");
+ }
+ }
+
+ // Axis mapping. Currently we only map the X axis, but we return an array of arrays to allow for mapping other axes in future.
+ response->cat("],\"axisMap\":[[");
+ bool first = true;
+ for (size_t xi = 0; xi < MAX_AXES; ++xi)
+ {
+ if ((tool->GetXAxisMap() & (1u << xi)) != 0)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ response->cat(",");
+ }
+ response->catf("%u", xi);
+ }
+ }
+
+ // Do we have any more tools?
+ if (tool->Next() != nullptr)
+ {
+ response->cat("]]},");
+ }
+ else
+ {
+ response->cat("]]}");
+ }
+ }
+ response->cat("]");
+ }
+
+ // MCU temperatures
+#ifndef __RADDS__
+ {
+ float minT, currT, maxT;
+ platform->GetMcuTemperatures(minT, currT, maxT);
+ response->catf(",\"mcutemp\":{\"min\":%.1f,\"cur\":%.1f,\"max\":%.1f}", minT, currT, maxT);
+ }
+#endif
+
+#ifdef DUET_NG
+ // Power in voltages
+ {
+ float minV, currV, maxV;
+ platform->GetPowerVoltages(minV, currV, maxV);
+ response->catf(",\"vin\":{\"min\":%.1f,\"cur\":%.1f,\"max\":%.1f}", minV, currV, maxV);
+ }
+#endif
+ }
+ else if (type == 3)
+ {
+ // Current Layer
+ response->catf(",\"currentLayer\":%d", printMonitor->GetCurrentLayer());
+
+ // Current Layer Time
+ response->catf(",\"currentLayerTime\":%.1f", printMonitor->GetCurrentLayerTime());
+
+ // Raw Extruder Positions
+ response->cat(",\"extrRaw\":");
+ ch = '[';
+ for (size_t extruder = 0; extruder < GetExtrudersInUse(); extruder++) // loop through extruders
+ {
+ response->catf("%c%.1f", ch, gCodes->GetRawExtruderTotalByDrive(extruder));
+ ch = ',';
+ }
+ if (ch == '[')
+ {
+ response->cat(ch); // no extruders
+ }
+
+ // Fraction of file printed
+ response->catf("],\"fractionPrinted\":%.1f", (printMonitor->IsPrinting()) ? (gCodes->FractionOfFilePrinted() * 100.0) : 0.0);
+
+ // First Layer Duration
+ response->catf(",\"firstLayerDuration\":%.1f", printMonitor->GetFirstLayerDuration());
+
+ // First Layer Height
+ // NB: This shouldn't be needed any more, but leave it here for the case that the file-based first-layer detection fails
+ response->catf(",\"firstLayerHeight\":%.2f", printMonitor->GetFirstLayerHeight());
+
+ // Print Duration
+ response->catf(",\"printDuration\":%.1f", printMonitor->GetPrintDuration());
+
+ // Warm-Up Time
+ response->catf(",\"warmUpDuration\":%.1f", printMonitor->GetWarmUpDuration());
+
+ /* Print Time Estimations */
+ {
+ // Based on file progress
+ response->catf(",\"timesLeft\":{\"file\":%.1f", printMonitor->EstimateTimeLeft(fileBased));
+
+ // Based on filament usage
+ response->catf(",\"filament\":%.1f", printMonitor->EstimateTimeLeft(filamentBased));
+
+ // Based on layers
+ response->catf(",\"layer\":%.1f}", printMonitor->EstimateTimeLeft(layerBased));
+ }
+ }
+
+ if (source == ResponseSource::AUX)
+ {
+ OutputBuffer *reply = platform->GetAuxGCodeReply();
+ if (response != nullptr)
+ {
+ // Send the response to the last command. Do this last
+ response->catf(",\"seq\":%u,\"resp\":", platform->GetAuxSeq()); // send the response sequence number
+
+ // Send the JSON response
+ response->EncodeReply(reply, true); // also releases the OutputBuffer chain
+ }
+ }
+ response->cat("}");
+
+ return response;
+}
+
+OutputBuffer *RepRap::GetConfigResponse()
+{
+ // We need some resources to return a valid config response...
+ OutputBuffer *response;
+ if (!OutputBuffer::Allocate(response))
+ {
+ return nullptr;
+ }
+
+ const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
+
+ // Axis minima
+ response->copy("{\"axisMins\":");
+ char ch = '[';
+ for (size_t axis = 0; axis < numAxes; axis++)
+ {
+ response->catf("%c%.2f", ch, platform->AxisMinimum(axis));
+ ch = ',';
+ }
+
+ // Axis maxima
+ response->cat("],\"axisMaxes\":");
+ ch = '[';
+ for (size_t axis = 0; axis < numAxes; axis++)
+ {
+ response->catf("%c%.2f", ch, platform->AxisMaximum(axis));
+ ch = ',';
+ }
+
+ // Accelerations
+ response->cat("],\"accelerations\":");
+ ch = '[';
+ for (size_t drive = 0; drive < DRIVES; drive++)
+ {
+ response->catf("%c%.2f", ch, platform->Acceleration(drive));
+ ch = ',';
+ }
+
+ // Motor currents
+ response->cat("],\"currents\":");
+ ch = '[';
+ for (size_t drive = 0; drive < DRIVES; drive++)
+ {
+ response->catf("%c%.2f", ch, platform->GetMotorCurrent(drive, false));
+ ch = ',';
+ }
+
+ // Firmware details
+ response->catf("],\"firmwareElectronics\":\"%s", platform->GetElectronicsString());
+#ifdef DUET_NG
+ const char* expansionName = DuetExpansion::GetExpansionBoardName();
+ if (expansionName != nullptr)
+ {
+ response->catf(" + %s", expansionName);
+ }
+#endif
+ response->catf("\",\"firmwareName\":\"%s\"", FIRMWARE_NAME);
+ response->catf(",\"firmwareVersion\":\"%s\"", VERSION);
+#if defined(DUET_NG) && defined(DUET_WIFI)
+ response->catf(",\"dwsVersion\":\"%s\"", network->GetWiFiServerVersion());
+#endif
+ response->catf(",\"firmwareDate\":\"%s\"", DATE);
+
+ // Motor idle parameters
+ response->catf(",\"idleCurrentFactor\":%.1f", platform->GetIdleCurrentFactor() * 100.0);
+ response->catf(",\"idleTimeout\":%.1f", move->IdleTimeout());
+
+ // Minimum feedrates
+ response->cat(",\"minFeedrates\":");
+ ch = '[';
+ for (size_t drive = 0; drive < DRIVES; drive++)
+ {
+ response->catf("%c%.2f", ch, platform->ConfiguredInstantDv(drive));
+ ch = ',';
+ }
+
+ // Maximum feedrates
+ response->cat("],\"maxFeedrates\":");
+ ch = '[';
+ for (size_t drive = 0; drive < DRIVES; drive++)
+ {
+ response->catf("%c%.2f", ch, platform->MaxFeedrate(drive));
+ ch = ',';
+ }
+
+ // Config file is no longer included, because we can use rr_configfile or M503 instead
+ response->cat("]}");
+
+ return response;
+}
+
+// Get the JSON status response for PanelDue or the old web server.
+// Type 0 was the old-style webserver status response, but is no longer supported.
+// Type 1 is the new-style webserver status response.
+// Type 2 is the M105 S2 response, which is like the new-style status response but some fields are omitted.
+// Type 3 is the M105 S3 response, which is like the M105 S2 response except that static values are also included.
+// 'seq' is the response sequence number, if it is not -1 and we have a different sequence number then we send the gcode response
+OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
+{
+ // Need something to write to...
+ OutputBuffer *response;
+ if (!OutputBuffer::Allocate(response))
+ {
+ // Should never happen
+ return nullptr;
+ }
+
+ // Send the status. Note that 'S' has always meant that the machine is halted in this version of the status response, so we use A for pAused.
+ char ch = GetStatusCharacter();
+ if (ch == 'S') // if paused then send 'A'
+ {
+ ch = 'A';
+ }
+ else if (ch == 'H') // if halted then send 'S'
+ {
+ ch = 'S';
+ }
+ response->printf("{\"status\":\"%c\",\"heaters\":", ch);
+
+ // Send the heater actual temperatures
+ const int8_t bedHeater = heat->GetBedHeater();
+ if (bedHeater != -1)
+ {
+ ch = ',';
+ response->catf("[%.1f", heat->GetTemperature(bedHeater));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%.1f", ch, heat->GetTemperature(heater));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Send the heater active temperatures
+ response->catf(",\"active\":");
+ if (heat->GetBedHeater() != -1)
+ {
+ ch = ',';
+ response->catf("[%.1f", heat->GetActiveTemperature(heat->GetBedHeater()));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%.1f", ch, heat->GetActiveTemperature(heater));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Send the heater standby temperatures
+ response->catf(",\"standby\":");
+ if (bedHeater != -1)
+ {
+ ch = ',';
+ response->catf("[%.1f", heat->GetStandbyTemperature(bedHeater));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%.1f", ch, heat->GetStandbyTemperature(heater));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Send the heater statuses (0=off, 1=standby, 2=active, 3 = fault)
+ response->cat(",\"hstat\":");
+ if (bedHeater != -1)
+ {
+ ch = ',';
+ response->catf("[%d", static_cast<int>(heat->GetStatus(bedHeater)));
+ }
+ else
+ {
+ ch = '[';
+ }
+ for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++)
+ {
+ response->catf("%c%d", ch, static_cast<int>(heat->GetStatus(heater)));
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Send XYZ positions
+ const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
+ float liveCoordinates[DRIVES];
+ reprap.GetMove()->LiveCoordinates(liveCoordinates, GetCurrentXAxes());
+ const Tool* const currentTool = reprap.GetCurrentTool();
+ if (currentTool != nullptr)
+ {
+ const float *offset = currentTool->GetOffset();
+ for (size_t i = 0; i < numAxes; ++i)
+ {
+ liveCoordinates[i] += offset[i];
+ }
+ }
+ response->catf(",\"pos\":"); // announce the XYZ position
+ ch = '[';
+ for (size_t drive = 0; drive < numAxes; drive++)
+ {
+ response->catf("%c%.2f", ch, liveCoordinates[drive]);
+ ch = ',';
+ }
+
+ // Send extruder total extrusion since power up, last G92 or last M23
+ response->cat("],\"extr\":"); // announce the extruder positions
+ ch = '[';
+ for (size_t drive = 0; drive < reprap.GetExtrudersInUse(); drive++) // loop through extruders
+ {
+ response->catf("%c%.1f", ch, gCodes->GetRawExtruderPosition(drive));
+ ch = ',';
+ }
+ response->cat((ch == ']') ? "[]" : "]");
+
+ // Send the speed and extruder override factors
+ response->catf(",\"sfactor\":%.2f,\"efactor\":", gCodes->GetSpeedFactor() * 100.0);
+ ch = '[';
+ for (size_t i = 0; i < reprap.GetExtrudersInUse(); ++i)
+ {
+ response->catf("%c%.2f", ch, gCodes->GetExtrusionFactor(i) * 100.0);
+ ch = ',';
+ }
+ response->cat((ch == '[') ? "[]" : "]");
+
+ // Send the baby stepping offset
+ response->catf(",\"babystep\":%.03f", gCodes->GetBabyStepOffset());
+
+ // Send the current tool number
+ const int toolNumber = (currentTool == nullptr) ? 0 : currentTool->Number();
+ response->catf(",\"tool\":%d", toolNumber);
+
+ // Send the Z probe value
+ const int v0 = platform->GetZProbeReading();
+ int v1, v2;
+ switch (platform->GetZProbeSecondaryValues(v1, v2))
+ {
+ case 1:
+ response->catf(",\"probe\":\"%d (%d)\"", v0, v1);
+ break;
+ case 2:
+ response->catf(",\"probe\":\"%d (%d, %d)\"", v0, v1, v2);
+ break;
+ default:
+ response->catf(",\"probe\":\"%d\"", v0);
+ break;
+ }
+
+ // Send the fan settings, for PanelDue firmware 1.13 and later
+ response->catf(",\"fanPercent\":");
+ ch = '[';
+ for (size_t i = 0; i < NUM_FANS; ++i)
+ {
+ response->catf("%c%.02f", ch, platform->GetFanValue(i) * 100.0);
+ ch = ',';
+ }
+
+ // Send fan RPM value (we only support one)
+ response->catf("],\"fanRPM\":%u", static_cast<unsigned int>(platform->GetFanRPM()));
+
+ // Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false.
+ response->cat(",\"homed\":");
+ ch = '[';
+ for (size_t axis = 0; axis < numAxes; ++axis)
+ {
+ response->catf("%c%d", ch, (gCodes->GetAxisIsHomed(axis)) ? 1 : 0);
+ ch = ',';
+ }
+ response->cat(']');
+
+ if (printMonitor->IsPrinting())
+ {
+ // Send the fraction printed
+ response->catf(",\"fraction_printed\":%.4f", max<float>(0.0, gCodes->FractionOfFilePrinted()));
+ }
+
+ // Short messages are now pushed directly to PanelDue, so don't include them here as well
+ // We no longer send the amount of http buffer space here because the web interface doesn't use these formns of status response
+
+ if (type == 2)
+ {
+ if (printMonitor->IsPrinting())
+ {
+ // Send estimated times left based on file progress, filament usage, and layers
+ response->catf(",\"timesLeft\":[%.1f,%.1f,%.1f]",
+ printMonitor->EstimateTimeLeft(fileBased),
+ printMonitor->EstimateTimeLeft(filamentBased),
+ printMonitor->EstimateTimeLeft(layerBased));
+ }
+ }
+ else if (type == 3)
+ {
+ // Add the static fields
+ response->catf(",\"geometry\":\"%s\",\"axes\":%u,\"volumes\":%u,\"numTools\":%u,\"myName\":",
+ move->GetGeometryString(), numAxes, NumSdCards, GetNumberOfContiguousTools());
+ response->EncodeString(myName, ARRAY_SIZE(myName), false);
+ response->cat(",\"firmwareName\":");
+ response->EncodeString(FIRMWARE_NAME, strlen(FIRMWARE_NAME), false);
+ }
+
+ const int auxSeq = (int)platform->GetAuxSeq();
+ if (type < 2 || (seq != -1 && auxSeq != seq))
+ {
+
+ // Send the response to the last command. Do this last because it can be long and may need to be truncated.
+ response->catf(",\"seq\":%d,\"resp\":", auxSeq); // send the response sequence number
+
+ // Send the JSON response
+ response->EncodeReply(platform->GetAuxGCodeReply(), true); // also releases the OutputBuffer chain
+ }
+
+ response->cat("}");
+ return response;
+}
+
+// Copy some parameter text, stopping at the first control character or when the destination buffer is full, and removing trailing spaces
+void RepRap::CopyParameterText(const char* src, char *dst, size_t length)
+{
+ size_t i;
+ for (i = 0; i + 1 < length && src[i] >= ' '; ++i)
+ {
+ dst[i] = src[i];
+ }
+ // Remove any trailing spaces
+ while (i > 0 && dst[i - 1] == ' ')
+ {
+ --i;
+ }
+ dst[i] = 0;
+}
+
+// Get the list of files in the specified directory in JSON format.
+// If flagDirs is true then we prefix each directory with a * character.
+OutputBuffer *RepRap::GetFilesResponse(const char *dir, bool flagsDirs)
+{
+ // Need something to write to...
+ OutputBuffer *response;
+ if (!OutputBuffer::Allocate(response))
+ {
+ return nullptr;
+ }
+
+ response->copy("{\"dir\":");
+ response->EncodeString(dir, strlen(dir), false);
+ response->cat(",\"files\":[");
+ unsigned int err;
+
+ if (!platform->GetMassStorage()->CheckDriveMounted(dir))
+ {
+ err = 1;
+ }
+ else
+ {
+ err = 0;
+ FileInfo fileInfo;
+ bool firstFile = true;
+ bool gotFile = platform->GetMassStorage()->FindFirst(dir, fileInfo); // TODO error handling here
+
+ size_t bytesLeft = OutputBuffer::GetBytesLeft(response); // don't write more bytes than we can
+ char filename[FILENAME_LENGTH];
+ filename[0] = '*';
+ const char *fname;
+
+ while (gotFile)
+ {
+ if (fileInfo.fileName[0] != '.') // ignore Mac resource files and Linux hidden files
+ {
+ // Get the long filename if possible
+ if (flagsDirs && fileInfo.isDirectory)
+ {
+ strncpy(filename + sizeof(char), fileInfo.fileName, FILENAME_LENGTH - 1);
+ filename[FILENAME_LENGTH - 1] = 0;
+ fname = filename;
+ }
+ else
+ {
+ fname = fileInfo.fileName;
+ }
+
+ // Make sure we can end this response properly
+ if (bytesLeft < strlen(fname) * 2 + 4)
+ {
+ // No more space available - stop here
+ break;
+ }
+
+ // Write separator and filename
+ if (!firstFile)
+ {
+ bytesLeft -= response->cat(',');
+ }
+ bytesLeft -= response->EncodeString(fname, FILENAME_LENGTH, false);
+
+ firstFile = false;
+ }
+ gotFile = platform->GetMassStorage()->FindNext(fileInfo); // TODO error handling here
+ }
+ }
+ response->catf("],\"err\":%u}", err);
+ return response;
+}
+
+// Get a JSON-style filelist including file types and sizes
+OutputBuffer *RepRap::GetFilelistResponse(const char *dir)
+{
+ // Need something to write to...
+ OutputBuffer *response;
+ if (!OutputBuffer::Allocate(response))
+ {
+ return nullptr;
+ }
+
+ // If the requested volume is not mounted, report an error
+ if (!platform->GetMassStorage()->CheckDriveMounted(dir))
+ {
+ response->copy("{\"err\":1}");
+ return response;
+ }
+
+ // Check if the directory exists
+ if (!platform->GetMassStorage()->DirectoryExists(dir))
+ {
+ response->copy("{\"err\":2}");
+ return response;
+ }
+
+ response->copy("{\"dir\":");
+ response->EncodeString(dir, strlen(dir), false);
+ response->cat(",\"files\":[");
+
+ FileInfo fileInfo;
+ bool firstFile = true;
+ bool gotFile = platform->GetMassStorage()->FindFirst(dir, fileInfo);
+ size_t bytesLeft = OutputBuffer::GetBytesLeft(response); // don't write more bytes than we can
+
+ while (gotFile)
+ {
+ if (fileInfo.fileName[0] != '.') // ignore Mac resource files and Linux hidden files
+ {
+ // Make sure we can end this response properly
+ if (bytesLeft < strlen(fileInfo.fileName) + 70)
+ {
+ // No more space available - stop here
+ break;
+ }
+
+ // Write delimiter
+ if (!firstFile)
+ {
+ bytesLeft -= response->cat(',');
+ }
+ firstFile = false;
+
+ // Write another file entry
+ bytesLeft -= response->catf("{\"type\":\"%c\",\"name\":", fileInfo.isDirectory ? 'd' : 'f');
+ bytesLeft -= response->EncodeString(fileInfo.fileName, FILENAME_LENGTH, false);
+ bytesLeft -= response->catf(",\"size\":%u", fileInfo.size);
+
+ const struct tm * const timeInfo = gmtime(&fileInfo.lastModified);
+ if (timeInfo->tm_year <= /*19*/80)
+ {
+ // Don't send the last modified date if it is invalid
+ bytesLeft -= response->cat('}');
+ }
+ else
+ {
+ bytesLeft -= response->catf(",\"date\":\"%04u-%02u-%02uT%02u:%02u:%02u\"}",
+ timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, timeInfo->tm_mday,
+ timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
+ }
+ }
+ gotFile = platform->GetMassStorage()->FindNext(fileInfo);
+ }
+ response->cat("]}");
+
+ return response;
+}
+
+// Send a beep. We send it to both PanelDue and the web interface.
+void RepRap::Beep(int freq, int ms)
+{
+ beepFrequency = freq;
+ beepDuration = ms;
+
+ if (platform->HaveAux())
+ {
+ // If there is an LCD device present, make it beep
+ platform->Beep(freq, ms);
+ }
+}
+
+// Send a short message. We send it to both PanelDue and the web interface.
+void RepRap::SetMessage(const char *msg)
+{
+ strncpy(message, msg, MESSAGE_LENGTH);
+ message[MESSAGE_LENGTH] = 0;
+
+ if (platform->HaveAux())
+ {
+ platform->SendMessage(msg);
+ }
+}
+
+// Get the status character for the new-style status response
+char RepRap::GetStatusCharacter() const
+{
+ return (processingConfig) ? 'C' // Reading the configuration file
+ : (gCodes->IsFlashing()) ? 'F' // Flashing a new firmware binary
+ : (IsStopped()) ? 'H' // Halted
+ : (gCodes->IsPausing()) ? 'D' // Pausing / Decelerating
+ : (gCodes->IsResuming()) ? 'R' // Resuming
+ : (gCodes->IsDoingToolChange()) ? 'T' // Running tool change macros
+ : (gCodes->IsPaused()) ? 'S' // Paused / Stopped
+ : (printMonitor->IsPrinting()) ? 'P' // Printing
+ : (gCodes->DoingFileMacro() || !move->NoLiveMovement()) ? 'B' // Busy
+ : 'I'; // Idle
+}
+
+bool RepRap::NoPasswordSet() const
+{
+ return (!password[0] || StringEquals(password, DEFAULT_PASSWORD));
+}
+
+bool RepRap::CheckPassword(const char *pw) const
+{
+ return StringEquals(pw, password);
+}
+
+void RepRap::SetPassword(const char* pw)
+{
+ // Users sometimes put a tab character between the password and the comment, so allow for this
+ CopyParameterText(pw, password, ARRAY_SIZE(password));
+}
+
+const char *RepRap::GetName() const
+{
+ return myName;
+}
+
+void RepRap::SetName(const char* nm)
+{
+ // Users sometimes put a tab character between the machine name and the comment, so allow for this
+ CopyParameterText(nm, myName, ARRAY_SIZE(myName));
+
+ // Set new DHCP hostname
+ network->SetHostname(myName);
+}
+
+// Given that we want to extrude/retract the specified extruder drives, check if they are allowed.
+// For each disallowed one, log an error to report later and return a bit in the bitmap.
+// This may be called by an ISR!
+unsigned int RepRap::GetProhibitedExtruderMovements(unsigned int extrusions, unsigned int retractions)
+{
+ if (GetHeat()->ColdExtrude())
+ {
+ return 0;
+ }
+
+ Tool *tool = currentTool;
+ if (tool == nullptr)
+ {
+ // This should not happen, but if on tool is selected then don't allow any extruder movement
+ return extrusions | retractions;
+ }
+
+ unsigned int result = 0;
+ for (size_t driveNum = 0; driveNum < tool->DriveCount(); driveNum++)
+ {
+ const unsigned int extruderDrive = (unsigned int)(tool->Drive(driveNum));
+ const unsigned int mask = 1 << extruderDrive;
+ if (extrusions & mask)
+ {
+ if (!tool->ToolCanDrive(true))
+ {
+ result |= mask;
+ }
+ }
+ else if (retractions & mask)
+ {
+ if (!tool->ToolCanDrive(false))
+ {
+ result |= mask;
+ }
+ }
+ }
+
+ return result;
+}
+
+void RepRap::FlagTemperatureFault(int8_t dudHeater)
+{
+ if (toolList != nullptr)
+ {
+ toolList->FlagTemperatureFault(dudHeater);
+ }
+}
+
+void RepRap::ClearTemperatureFault(int8_t wasDudHeater)
+{
+ reprap.GetHeat()->ResetFault(wasDudHeater);
+ if (toolList != nullptr)
+ {
+ toolList->ClearTemperatureFault(wasDudHeater);
+ }
+}
+
+// Get the current axes used as X axes
+uint32_t RepRap::GetCurrentXAxes() const
+{
+ return (currentTool == nullptr) ? DefaultXAxisMapping : currentTool->GetXAxisMap();
+}
+
+// Helper function for diagnostic tests in Platform.cpp, to cause a deliberate divide-by-zero
+/*static*/ uint32_t RepRap::DoDivide(uint32_t a, uint32_t b)
+{
+ return a/b;
+}
+
+// Helper function for diagnostic tests in Platform.cpp, to cause a deliberate unaligned memory read
+/*static*/ uint32_t RepRap::ReadDword(const char* p)
+{
+ return *reinterpret_cast<const uint32_t*>(p);
+}
+
+// End