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:
-rw-r--r--Developer-documentation/New ESP8266 WiFi module code for Duet WiFi.odtbin20703 -> 21479 bytes
-rw-r--r--Release/Duet-0.6-0.8.5/Edge/DuetWebControl-1.15.zip (renamed from Release/Duet-0.6-0.8.5/Edge/DuetWebControl-1.15-b2.zip)bin493248 -> 495027 bytes
-rw-r--r--Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18RC1.bin (renamed from Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta2.bin)bin308908 -> 310636 bytes
-rw-r--r--Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta1.binbin308396 -> 0 bytes
-rw-r--r--Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.18RC1.bin (renamed from Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.18beta1.bin)bin281372 -> 283644 bytes
-rw-r--r--Release/Duet-Ethernet/Edge/DuetWebControl-1.15.zip (renamed from Release/Duet-Ethernet/Edge/DuetWebControl-1.15-b2.zip)bin493248 -> 495027 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWebControl-1.15.bin (renamed from Release/Duet-WiFi/Edge/DuetWebControl-1.15-b2.bin)bin3125248 -> 3125248 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18RC1.bin (renamed from Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta2.bin)bin266828 -> 268692 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta1.binbin267228 -> 0 bytes
-rw-r--r--Release/RADDS/Edge/RepRapFirmware-RADDS-1.18RC1.binbin0 -> 240596 bytes
-rw-r--r--Release/RADDS/Edge/RepRapFirmware-RADDS-1.18beta2.binbin238620 -> 0 bytes
-rw-r--r--src/Duet/Webserver.cpp266
-rw-r--r--src/Duet/Webserver.h35
-rw-r--r--src/DuetNG/DuetEthernet/Network.cpp4
-rw-r--r--src/DuetNG/DuetEthernet/Webserver.cpp265
-rw-r--r--src/DuetNG/DuetEthernet/Webserver.h35
-rw-r--r--src/DuetNG/DuetWiFi/Webserver.cpp134
-rw-r--r--src/DuetNG/DuetWiFi/Webserver.h16
-rw-r--r--src/GCodes/GCodeBuffer.cpp37
-rw-r--r--src/GCodes/GCodeBuffer.h9
-rw-r--r--src/GCodes/GCodeInput.cpp322
-rw-r--r--src/GCodes/GCodeInput.h107
-rw-r--r--src/GCodes/GCodeQueue.cpp224
-rw-r--r--src/GCodes/GCodeQueue.h53
-rw-r--r--src/GCodes/GCodes.cpp374
-rw-r--r--src/GCodes/GCodes.h29
-rw-r--r--src/GCodes/GCodes2.cpp127
-rw-r--r--src/Heating/Pid.cpp2
-rw-r--r--src/Movement/DDA.cpp4
-rw-r--r--src/Movement/Move.cpp13
-rw-r--r--src/Movement/Move.h7
-rw-r--r--src/Platform.cpp91
-rw-r--r--src/Platform.h22
-rw-r--r--src/RADDS/Webserver.h3
-rw-r--r--src/RepRap.cpp6
-rw-r--r--src/Storage/FileStore.cpp119
-rw-r--r--src/Storage/FileStore.h19
-rw-r--r--src/Storage/MassStorage.h2
-rw-r--r--src/Version.h4
39 files changed, 1162 insertions, 1167 deletions
diff --git a/Developer-documentation/New ESP8266 WiFi module code for Duet WiFi.odt b/Developer-documentation/New ESP8266 WiFi module code for Duet WiFi.odt
index 4285cce9..7b25d70e 100644
--- a/Developer-documentation/New ESP8266 WiFi module code for Duet WiFi.odt
+++ b/Developer-documentation/New ESP8266 WiFi module code for Duet WiFi.odt
Binary files differ
diff --git a/Release/Duet-0.6-0.8.5/Edge/DuetWebControl-1.15-b2.zip b/Release/Duet-0.6-0.8.5/Edge/DuetWebControl-1.15.zip
index b9ff2f4a..8f185985 100644
--- a/Release/Duet-0.6-0.8.5/Edge/DuetWebControl-1.15-b2.zip
+++ b/Release/Duet-0.6-0.8.5/Edge/DuetWebControl-1.15.zip
Binary files differ
diff --git a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta2.bin b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18RC1.bin
index 688c84ba..a7f19596 100644
--- a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta2.bin
+++ b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18RC1.bin
Binary files differ
diff --git a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta1.bin b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta1.bin
deleted file mode 100644
index 4a2b5d54..00000000
--- a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.18beta1.bin
+++ /dev/null
Binary files differ
diff --git a/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.18beta1.bin b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.18RC1.bin
index 8ab92365..781c891b 100644
--- a/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.18beta1.bin
+++ b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.18RC1.bin
Binary files differ
diff --git a/Release/Duet-Ethernet/Edge/DuetWebControl-1.15-b2.zip b/Release/Duet-Ethernet/Edge/DuetWebControl-1.15.zip
index b9ff2f4a..8f185985 100644
--- a/Release/Duet-Ethernet/Edge/DuetWebControl-1.15-b2.zip
+++ b/Release/Duet-Ethernet/Edge/DuetWebControl-1.15.zip
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWebControl-1.15-b2.bin b/Release/Duet-WiFi/Edge/DuetWebControl-1.15.bin
index aa52cfcd..c559b6ff 100644
--- a/Release/Duet-WiFi/Edge/DuetWebControl-1.15-b2.bin
+++ b/Release/Duet-WiFi/Edge/DuetWebControl-1.15.bin
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta2.bin b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18RC1.bin
index 9c772b21..4f73426d 100644
--- a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta2.bin
+++ b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18RC1.bin
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta1.bin b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta1.bin
deleted file mode 100644
index 0093c86e..00000000
--- a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.18beta1.bin
+++ /dev/null
Binary files differ
diff --git a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.18RC1.bin b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.18RC1.bin
new file mode 100644
index 00000000..53b2ddf5
--- /dev/null
+++ b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.18RC1.bin
Binary files differ
diff --git a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.18beta2.bin b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.18beta2.bin
deleted file mode 100644
index 2dd10462..00000000
--- a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.18beta2.bin
+++ /dev/null
Binary files differ
diff --git a/src/Duet/Webserver.cpp b/src/Duet/Webserver.cpp
index d21fd99b..74c6f4b4 100644
--- a/src/Duet/Webserver.cpp
+++ b/src/Duet/Webserver.cpp
@@ -259,34 +259,6 @@ void Webserver::Diagnostics(MessageType mtype)
telnetInterpreter->Diagnostics(mtype);
}
-bool Webserver::GCodeAvailable(const WebSource source) const
-{
- switch (source)
- {
- case WebSource::HTTP:
- return httpInterpreter->GCodeAvailable();
-
- case WebSource::Telnet:
- return telnetInterpreter->GCodeAvailable();
- }
-
- return false;
-}
-
-char Webserver::ReadGCode(const WebSource source)
-{
- switch (source)
- {
- case WebSource::HTTP:
- return httpInterpreter->ReadGCode();
-
- case WebSource::Telnet:
- return telnetInterpreter->ReadGCode();
- }
-
- return 0;
-}
-
void Webserver::HandleGCodeReply(const WebSource source, OutputBuffer *reply)
{
switch (source)
@@ -315,20 +287,6 @@ void Webserver::HandleGCodeReply(const WebSource source, const char *reply)
}
}
-uint16_t Webserver::GetGCodeBufferSpace(const WebSource source) const
-{
- switch (source)
- {
- case WebSource::HTTP:
- return httpInterpreter->GetGCodeBufferSpace();
-
- case WebSource::Telnet:
- return telnetInterpreter->GetGCodeBufferSpace();
- }
-
- return 0;
-}
-
// Handle immediate disconnects here (cs will be freed after this call)
// May be called by ISR, but not while LwIP is NOT locked
void Webserver::ConnectionLost(Connection conn)
@@ -521,7 +479,6 @@ bool ProtocolInterpreter::FinishUpload(uint32_t fileLength)
Webserver::HttpInterpreter::HttpInterpreter(Platform *p, Webserver *ws, Network *n)
: ProtocolInterpreter(p, ws, n), state(doingCommandWord), numSessions(0), clientsServed(0)
{
- gcodeReadIndex = gcodeWriteIndex = 0;
gcodeReply = new OutputStack();
deferredRequestConnection = NoConnection;
seq = 0;
@@ -751,6 +708,7 @@ void Webserver::HttpInterpreter::SendFile(const char* nameOfFileToSend, bool isW
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
}
const char* contentType;
@@ -830,6 +788,7 @@ void Webserver::HttpInterpreter::SendGCodeReply()
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
transaction->Write("Content-Type: text/plain\n");
transaction->Printf("Content-Length: %u\n", gcodeReply->DataLength());
transaction->Write("Connection: close\n\n");
@@ -888,16 +847,8 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
return;
}
- bool keepOpen = false;
bool mayKeepOpen;
- if (numQualKeys == 0)
- {
- GetJsonResponse(command, jsonResponse, mayKeepOpen);
- }
- else
- {
- GetJsonResponse(command, jsonResponse, mayKeepOpen);
- }
+ GetJsonResponse(command, jsonResponse, mayKeepOpen);
// Check special cases of deferred requests (rr_fileinfo) and rejected messages
NetworkTransaction *transaction = webserver->currentTransaction;
@@ -908,7 +859,7 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
}
// Send the JSON response
-
+ bool keepOpen = false;
if (mayKeepOpen)
{
// Check that the browser wants to persist the connection too
@@ -927,6 +878,7 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
transaction->Write("Content-Type: application/json\n");
transaction->Printf("Content-Length: %u\n", (jsonResponse != nullptr) ? jsonResponse->Length() : 0);
transaction->Printf("Connection: %s\n\n", keepOpen ? "keep-alive" : "close");
@@ -1011,8 +963,9 @@ void Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff
}
else if (StringEquals(request, "gcode") && GetKeyValue("gcode") != nullptr)
{
- LoadGcodeBuffer(GetKeyValue("gcode"));
- response->printf("{\"buff\":%u}", GetGCodeBufferSpace());
+ RegularGCodeInput * const httpInput = reprap.GetGCodes()->GetHTTPInput();
+ httpInput->Put(HTTP_MESSAGE, GetKeyValue("gcode"));
+ response->printf("{\"buff\":%u}", httpInput->BufferSpaceLeft());
}
else if (StringEquals(request, "upload"))
{
@@ -1548,7 +1501,27 @@ bool Webserver::HttpInterpreter::ProcessMessage()
ResetState();
return true;
}
- else if (IsAuthenticated() && StringEquals(commandWords[0], "POST"))
+
+ if (StringEquals(commandWords[0], "OPTIONS"))
+ {
+ NetworkTransaction *transaction = webserver->currentTransaction;
+
+ transaction->Write("HTTP/1.1 200 OK\n");
+ transaction->Write("Allow: OPTIONS, GET, POST\n");
+ transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
+ transaction->Write("Pragma: no-cache\n");
+ transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
+ transaction->Write("Access-Control-Allow-Headers: Content-Type\n");
+ transaction->Write("Content-Length: 0\n");
+ transaction->Write("\n");
+ transaction->Commit(false);
+
+ ResetState();
+ return true;
+ }
+
+ if (IsAuthenticated() && StringEquals(commandWords[0], "POST"))
{
const bool isUploadRequest = (StringEquals(commandWords[1], KO_START "upload"))
|| (commandWords[1][0] == '/' && StringEquals(commandWords[1] + 1, KO_START "upload"));
@@ -1715,114 +1688,6 @@ bool Webserver::HttpInterpreter::RemoveAuthentication()
return false;
}
-// Process a received string of gcodes
-void Webserver::HttpInterpreter::LoadGcodeBuffer(const char* gc)
-{
- char gcodeTempBuf[GCODE_LENGTH];
- uint16_t gtp = 0;
- bool inComment = false;
- for (;;)
- {
- char c = *gc++;
- if (c == 0)
- {
- gcodeTempBuf[gtp] = 0;
- ProcessGcode(gcodeTempBuf);
- return;
- }
-
- if (c == '\n')
- {
- gcodeTempBuf[gtp] = 0;
- ProcessGcode(gcodeTempBuf);
- gtp = 0;
- inComment = false;
- }
- else
- {
- if (c == ';')
- {
- inComment = true;
- }
-
- if (gtp == ARRAY_UPB(gcodeTempBuf))
- {
- // gcode is too long, we haven't room for another character and a null
- if (c != ' ' && !inComment)
- {
- platform->Message(HOST_MESSAGE, "Error: GCode local buffer overflow in HTTP webserver.\n");
- return;
- }
- // else we're either in a comment or the current character is a space.
- // If we're in a comment, we'll silently truncate it.
- // If the current character is a space, we'll wait until we see a non-comment character before reporting an error,
- // in case the next character is end-of-line or the start of a comment.
- }
- else
- {
- gcodeTempBuf[gtp++] = c;
- }
- }
- }
-}
-
-// Process a null-terminated gcode
-// We intercept one M Codes so we can deal with emergencies. That
-// way things don't get out of sync, and - as a file name can contain
-// a valid G code (!) - confusion is avoided.
-void Webserver::HttpInterpreter::ProcessGcode(const char* gc)
-{
- if (StringStartsWith(gc, "M112") && !isdigit(gc[4])) // emergency stop
- {
- reprap.EmergencyStop();
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
- reprap.GetGCodes()->Reset();
- }
- else
- {
- StoreGcodeData(gc, strlen(gc) + 1);
- }
-}
-
-// Process a received string of gcodes
-void Webserver::HttpInterpreter::StoreGcodeData(const char* data, uint16_t len)
-{
- if (len > GetGCodeBufferSpace())
- {
- platform->Message(HOST_MESSAGE, "Error: GCode buffer overflow in HTTP Webserver!\n");
- }
- else
- {
- uint16_t remaining = gcodeBufferLength - gcodeWriteIndex;
- if (len <= remaining)
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, len);
- }
- else
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, remaining);
- memcpy(gcodeBuffer, data + remaining, len - remaining);
- }
- gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufferLength;
- }
-}
-
-// Feeding G Codes to the GCodes class
-char Webserver::HttpInterpreter::ReadGCode()
-{
- char c;
- if (gcodeReadIndex == gcodeWriteIndex)
- {
- c = 0;
- }
- else
- {
- c = gcodeBuffer[gcodeReadIndex];
- gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufferLength;
- }
- return c;
-}
-
// Handle a G Code reply from the GCodes class
void Webserver::HttpInterpreter::HandleGCodeReply(OutputBuffer *reply)
{
@@ -1891,6 +1756,7 @@ void Webserver::HttpInterpreter::ProcessDeferredRequest()
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
transaction->Write("Content-Type: application/json\n");
transaction->Printf("Content-Length: %u\n", (jsonResponse != nullptr) ? jsonResponse->Length() : 0);
transaction->Printf("Connection: close\n\n");
@@ -2295,7 +2161,6 @@ void Webserver::FtpInterpreter::ProcessLine()
{
SendReply(500, "Unknown command.");
}
-
break;
case waitingForPasvPort:
@@ -2310,7 +2175,6 @@ void Webserver::FtpInterpreter::ProcessLine()
{
webserver->currentTransaction->Defer(DeferralMode::ResetData);
}
-
break;
case pasvPortConnected:
@@ -2566,7 +2430,7 @@ void Webserver::FtpInterpreter::ChangeDirectory(const char *newDirectory)
//********************************************************************************************
Webserver::TelnetInterpreter::TelnetInterpreter(Platform *p, Webserver *ws, Network *n)
- : ProtocolInterpreter(p, ws, n), connectedClients(0), processNextLine(false), gcodeReadIndex(0), gcodeWriteIndex(0), gcodeReply(nullptr)
+ : ProtocolInterpreter(p, ws, n), connectedClients(0), processNextLine(false), gcodeReply(nullptr)
{
ResetState();
}
@@ -2638,7 +2502,8 @@ bool Webserver::TelnetInterpreter::CanParseData()
}
// In order to support TCP streaming mode, check if we can store any more data at this time
- if (GetGCodeBufferSpace() < clientPointer + 1)
+ RegularGCodeInput * const telnetInput = reprap.GetGCodes()->GetTelnetInput();
+ if (telnetInput->BufferSpaceLeft() < clientPointer + 1)
{
webserver->currentTransaction->Defer(DeferralMode::DeferOnly);
return false;
@@ -2697,7 +2562,8 @@ bool Webserver::TelnetInterpreter::CharFromClient(char c)
{
// This line is complete, do we have enough space left to store it?
clientMessage[clientPointer] = 0;
- if (GetGCodeBufferSpace() < clientPointer + 1)
+ RegularGCodeInput * const telnetInput = reprap.GetGCodes()->GetTelnetInput();
+ if (telnetInput->BufferSpaceLeft() < clientPointer + 1)
{
// No - defer this transaction, so we can process more of it next time
webserver->currentTransaction->Defer(DeferralMode::DeferOnly);
@@ -2731,7 +2597,6 @@ void Webserver::TelnetInterpreter::ResetState()
state = idle;
connectTime = 0;
clientPointer = 0;
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
}
// Usually we should not try to send any data here, because that would purge the packet's
@@ -2774,70 +2639,15 @@ bool Webserver::TelnetInterpreter::ProcessLine()
transaction->Commit(false);
return true;
}
+
// All other codes are stored for the GCodes class
- ProcessGcode(clientMessage);
+ RegularGCodeInput * const telnetInput = reprap.GetGCodes()->GetTelnetInput();
+ telnetInput->Put(TELNET_MESSAGE, clientMessage);
break;
}
return false;
}
-// Process a null-terminated gcode
-// We intercept one M Codes so we can deal with emergencies. That
-// way things don't get out of sync, and - as a file name can contain
-// a valid G code (!) - confusion is avoided.
-void Webserver::TelnetInterpreter::ProcessGcode(const char* gc)
-{
- if (StringStartsWith(gc, "M112") && !isdigit(gc[4])) // emergency stop
- {
- reprap.EmergencyStop();
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
- reprap.GetGCodes()->Reset();
- }
- else
- {
- StoreGcodeData(gc, strlen(gc) + 1);
- }
-}
-
-// Process a received string of gcodes
-void Webserver::TelnetInterpreter::StoreGcodeData(const char* data, uint16_t len)
-{
- if (len > GetGCodeBufferSpace())
- {
- platform->Message(HOST_MESSAGE, "Error: GCode buffer overflow in Telnet Webserver!\n");
- }
- else
- {
- uint16_t remaining = gcodeBufferLength - gcodeWriteIndex;
- if (len <= remaining)
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, len);
- }
- else
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, remaining);
- memcpy(gcodeBuffer, data + remaining, len - remaining);
- }
- gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufferLength;
- }
-}
-
-// Feeding G Codes to the GCodes class
-char Webserver::TelnetInterpreter::ReadGCode()
-{
- char c;
- if (gcodeReadIndex == gcodeWriteIndex)
- {
- c = 0;
- }
- else
- {
- c = gcodeBuffer[gcodeReadIndex];
- gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufferLength;
- }
- return c;
-}
-
// Handle a G-Code reply from the GCodes class; replace \n with \r\n
void Webserver::TelnetInterpreter::HandleGCodeReply(OutputBuffer *reply)
{
diff --git a/src/Duet/Webserver.h b/src/Duet/Webserver.h
index 3e4f2c30..698f6ea4 100644
--- a/src/Duet/Webserver.h
+++ b/src/Duet/Webserver.h
@@ -130,15 +130,10 @@ public:
void Exit();
void Diagnostics(MessageType mtype);
- bool GCodeAvailable(const WebSource source) const;
- char ReadGCode(const WebSource source);
void HandleGCodeReply(const WebSource source, OutputBuffer *reply);
void HandleGCodeReply(const WebSource source, const char *reply);
uint32_t GetReplySeq() const;
- // Returns the available G-Code buffer space of the HTTP interpreter (may be dropped in a future version)
- uint16_t GetGCodeBufferSpace(const WebSource source) const;
-
void ConnectionLost(Connection conn /*const ConnectionState *cs*/);
void ConnectionError();
@@ -161,11 +156,8 @@ protected:
bool DoingFastUpload() const override;
void DoFastUpload();
- bool GCodeAvailable() const;
- char ReadGCode();
void HandleGCodeReply(OutputBuffer *reply);
void HandleGCodeReply(const char *reply);
- uint16_t GetGCodeBufferSpace() const;
uint32_t GetReplySeq() const;
private:
@@ -234,17 +226,7 @@ protected:
bool RemoveAuthentication();
const char* GetKeyValue(const char *key) const; // return the value of the specified key, or nullptr if not present
- // Deal with incoming G-Codes
-
- char gcodeBuffer[gcodeBufferLength];
- uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
-
- void LoadGcodeBuffer(const char* gc);
- void ProcessGcode(const char* gc);
- void StoreGcodeData(const char* data, uint16_t len);
-
// Responses from GCodes class
-
uint32_t seq; // Sequence number for G-Code replies
OutputStack *gcodeReply;
@@ -318,11 +300,8 @@ protected:
bool CharFromClient(const char c) override;
void ResetState();
- bool GCodeAvailable() const;
- char ReadGCode();
void HandleGCodeReply(OutputBuffer *reply);
void HandleGCodeReply(const char *reply);
- uint16_t GetGCodeBufferSpace() const;
void SendGCodeReply();
@@ -345,16 +324,7 @@ protected:
bool ProcessLine();
- // Deal with incoming G-Codes
-
- char gcodeBuffer[gcodeBufferLength];
- uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
-
- void ProcessGcode(const char* gc);
- void StoreGcodeData(const char* data, uint16_t len);
-
// Converted response from GCodes class (NL -> CRNL)
-
OutputBuffer * volatile gcodeReply;
};
TelnetInterpreter *telnetInterpreter;
@@ -376,11 +346,6 @@ inline bool ProtocolInterpreter::IsUploading() const { return uploadState != not
inline uint32_t Webserver::GetReplySeq() const { return httpInterpreter->GetReplySeq(); }
-inline uint16_t Webserver::HttpInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
-inline bool Webserver::HttpInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
inline uint32_t Webserver::HttpInterpreter::GetReplySeq() const { return seq; }
-inline uint16_t Webserver::TelnetInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
-inline bool Webserver::TelnetInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
-
#endif
diff --git a/src/DuetNG/DuetEthernet/Network.cpp b/src/DuetNG/DuetEthernet/Network.cpp
index ea27bc86..fe844b13 100644
--- a/src/DuetNG/DuetEthernet/Network.cpp
+++ b/src/DuetNG/DuetEthernet/Network.cpp
@@ -433,7 +433,7 @@ NetworkTransaction *Network::GetTransaction(Connection conn)
{
if (conn != NoConnection)
{
- NetworkTransaction *tr = conn->GetTransaction();
+ NetworkTransaction * const tr = conn->GetTransaction();
if (tr != nullptr && !tr->IsSending())
{
currentTransactionSocketNumber = conn->GetNumber();
@@ -445,7 +445,7 @@ NetworkTransaction *Network::GetTransaction(Connection conn)
size_t socketNum = currentTransactionSocketNumber;
do
{
- NetworkTransaction *tr = sockets[socketNum].GetTransaction();
+ NetworkTransaction * const tr = sockets[socketNum].GetTransaction();
if (tr != nullptr && !tr->IsSending())
{
currentTransactionSocketNumber = socketNum;
diff --git a/src/DuetNG/DuetEthernet/Webserver.cpp b/src/DuetNG/DuetEthernet/Webserver.cpp
index 6a5b5c6d..114b291b 100644
--- a/src/DuetNG/DuetEthernet/Webserver.cpp
+++ b/src/DuetNG/DuetEthernet/Webserver.cpp
@@ -259,34 +259,6 @@ void Webserver::Diagnostics(MessageType mtype)
telnetInterpreter->Diagnostics(mtype);
}
-bool Webserver::GCodeAvailable(const WebSource source) const
-{
- switch (source)
- {
- case WebSource::HTTP:
- return httpInterpreter->GCodeAvailable();
-
- case WebSource::Telnet:
- return telnetInterpreter->GCodeAvailable();
- }
-
- return false;
-}
-
-char Webserver::ReadGCode(const WebSource source)
-{
- switch (source)
- {
- case WebSource::HTTP:
- return httpInterpreter->ReadGCode();
-
- case WebSource::Telnet:
- return telnetInterpreter->ReadGCode();
- }
-
- return 0;
-}
-
void Webserver::HandleGCodeReply(const WebSource source, OutputBuffer *reply)
{
switch (source)
@@ -315,20 +287,6 @@ void Webserver::HandleGCodeReply(const WebSource source, const char *reply)
}
}
-uint16_t Webserver::GetGCodeBufferSpace(const WebSource source) const
-{
- switch (source)
- {
- case WebSource::HTTP:
- return httpInterpreter->GetGCodeBufferSpace();
-
- case WebSource::Telnet:
- return telnetInterpreter->GetGCodeBufferSpace();
- }
-
- return 0;
-}
-
// Handle immediate disconnects here (cs will be freed after this call)
// May be called by ISR, but not while LwIP is NOT locked
void Webserver::ConnectionLost(Connection conn)
@@ -521,7 +479,6 @@ bool ProtocolInterpreter::FinishUpload(uint32_t fileLength)
Webserver::HttpInterpreter::HttpInterpreter(Platform *p, Webserver *ws, Network *n)
: ProtocolInterpreter(p, ws, n), state(doingCommandWord), numSessions(0), clientsServed(0)
{
- gcodeReadIndex = gcodeWriteIndex = 0;
gcodeReply = new OutputStack();
deferredRequestConnection = NoConnection;
seq = 0;
@@ -751,6 +708,7 @@ void Webserver::HttpInterpreter::SendFile(const char* nameOfFileToSend, bool isW
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
}
const char* contentType;
@@ -830,6 +788,7 @@ void Webserver::HttpInterpreter::SendGCodeReply()
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
transaction->Write("Content-Type: text/plain\n");
transaction->Printf("Content-Length: %u\n", gcodeReply->DataLength());
transaction->Write("Connection: close\n\n");
@@ -888,16 +847,8 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
return;
}
- bool keepOpen = false;
bool mayKeepOpen;
- if (numQualKeys == 0)
- {
- GetJsonResponse(command, jsonResponse, mayKeepOpen);
- }
- else
- {
- GetJsonResponse(command, jsonResponse, mayKeepOpen);
- }
+ GetJsonResponse(command, jsonResponse, mayKeepOpen);
// Check special cases of deferred requests (rr_fileinfo) and rejected messages
NetworkTransaction *transaction = webserver->currentTransaction;
@@ -908,7 +859,7 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
}
// Send the JSON response
-
+ bool keepOpen = false;
if (mayKeepOpen)
{
// Check that the browser wants to persist the connection too
@@ -927,6 +878,7 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command)
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
transaction->Write("Content-Type: application/json\n");
transaction->Printf("Content-Length: %u\n", (jsonResponse != nullptr) ? jsonResponse->Length() : 0);
transaction->Printf("Connection: %s\n\n", keepOpen ? "keep-alive" : "close");
@@ -1011,8 +963,9 @@ void Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff
}
else if (StringEquals(request, "gcode") && GetKeyValue("gcode") != nullptr)
{
- LoadGcodeBuffer(GetKeyValue("gcode"));
- response->printf("{\"buff\":%u}", GetGCodeBufferSpace());
+ RegularGCodeInput * const httpInput = reprap.GetGCodes()->GetHTTPInput();
+ httpInput->Put(HTTP_MESSAGE, GetKeyValue("gcode"));
+ response->printf("{\"buff\":%u}", httpInput->BufferSpaceLeft());
}
else if (StringEquals(request, "upload"))
{
@@ -1548,7 +1501,27 @@ bool Webserver::HttpInterpreter::ProcessMessage()
ResetState();
return true;
}
- else if (IsAuthenticated() && StringEquals(commandWords[0], "POST"))
+
+ if (StringEquals(commandWords[0], "OPTIONS"))
+ {
+ NetworkTransaction *transaction = webserver->currentTransaction;
+
+ transaction->Write("HTTP/1.1 200 OK\n");
+ transaction->Write("Allow: OPTIONS, GET, POST\n");
+ transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
+ transaction->Write("Pragma: no-cache\n");
+ transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
+ transaction->Write("Access-Control-Allow-Headers: Content-Type\n");
+ transaction->Write("Content-Length: 0\n");
+ transaction->Write("\n");
+ transaction->Commit(false);
+
+ ResetState();
+ return true;
+ }
+
+ if (IsAuthenticated() && StringEquals(commandWords[0], "POST"))
{
const bool isUploadRequest = (StringEquals(commandWords[1], KO_START "upload"))
|| (commandWords[1][0] == '/' && StringEquals(commandWords[1] + 1, KO_START "upload"));
@@ -1715,114 +1688,6 @@ bool Webserver::HttpInterpreter::RemoveAuthentication()
return false;
}
-// Process a received string of gcodes
-void Webserver::HttpInterpreter::LoadGcodeBuffer(const char* gc)
-{
- char gcodeTempBuf[GCODE_LENGTH];
- uint16_t gtp = 0;
- bool inComment = false;
- for (;;)
- {
- char c = *gc++;
- if (c == 0)
- {
- gcodeTempBuf[gtp] = 0;
- ProcessGcode(gcodeTempBuf);
- return;
- }
-
- if (c == '\n')
- {
- gcodeTempBuf[gtp] = 0;
- ProcessGcode(gcodeTempBuf);
- gtp = 0;
- inComment = false;
- }
- else
- {
- if (c == ';')
- {
- inComment = true;
- }
-
- if (gtp == ARRAY_UPB(gcodeTempBuf))
- {
- // gcode is too long, we haven't room for another character and a null
- if (c != ' ' && !inComment)
- {
- platform->Message(HOST_MESSAGE, "Error: GCode local buffer overflow in HTTP webserver.\n");
- return;
- }
- // else we're either in a comment or the current character is a space.
- // If we're in a comment, we'll silently truncate it.
- // If the current character is a space, we'll wait until we see a non-comment character before reporting an error,
- // in case the next character is end-of-line or the start of a comment.
- }
- else
- {
- gcodeTempBuf[gtp++] = c;
- }
- }
- }
-}
-
-// Process a null-terminated gcode
-// We intercept one M Codes so we can deal with emergencies. That
-// way things don't get out of sync, and - as a file name can contain
-// a valid G code (!) - confusion is avoided.
-void Webserver::HttpInterpreter::ProcessGcode(const char* gc)
-{
- if (StringStartsWith(gc, "M112") && !isdigit(gc[4])) // emergency stop
- {
- reprap.EmergencyStop();
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
- reprap.GetGCodes()->Reset();
- }
- else
- {
- StoreGcodeData(gc, strlen(gc) + 1);
- }
-}
-
-// Process a received string of gcodes
-void Webserver::HttpInterpreter::StoreGcodeData(const char* data, uint16_t len)
-{
- if (len > GetGCodeBufferSpace())
- {
- platform->Message(HOST_MESSAGE, "Error: GCode buffer overflow in HTTP Webserver!\n");
- }
- else
- {
- uint16_t remaining = gcodeBufferLength - gcodeWriteIndex;
- if (len <= remaining)
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, len);
- }
- else
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, remaining);
- memcpy(gcodeBuffer, data + remaining, len - remaining);
- }
- gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufferLength;
- }
-}
-
-// Feeding G Codes to the GCodes class
-char Webserver::HttpInterpreter::ReadGCode()
-{
- char c;
- if (gcodeReadIndex == gcodeWriteIndex)
- {
- c = 0;
- }
- else
- {
- c = gcodeBuffer[gcodeReadIndex];
- gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufferLength;
- }
- return c;
-}
-
// Handle a G Code reply from the GCodes class
void Webserver::HttpInterpreter::HandleGCodeReply(OutputBuffer *reply)
{
@@ -1891,6 +1756,7 @@ void Webserver::HttpInterpreter::ProcessDeferredRequest()
transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n");
transaction->Write("Pragma: no-cache\n");
transaction->Write("Expires: 0\n");
+ transaction->Write("Access-Control-Allow-Origin: *\n");
transaction->Write("Content-Type: application/json\n");
transaction->Printf("Content-Length: %u\n", (jsonResponse != nullptr) ? jsonResponse->Length() : 0);
transaction->Printf("Connection: close\n\n");
@@ -2295,7 +2161,6 @@ void Webserver::FtpInterpreter::ProcessLine()
{
SendReply(500, "Unknown command.");
}
-
break;
case waitingForPasvPort:
@@ -2565,7 +2430,7 @@ void Webserver::FtpInterpreter::ChangeDirectory(const char *newDirectory)
//********************************************************************************************
Webserver::TelnetInterpreter::TelnetInterpreter(Platform *p, Webserver *ws, Network *n)
- : ProtocolInterpreter(p, ws, n), connectedClients(0), processNextLine(false), gcodeReadIndex(0), gcodeWriteIndex(0), gcodeReply(nullptr)
+ : ProtocolInterpreter(p, ws, n), connectedClients(0), processNextLine(false), gcodeReply(nullptr)
{
ResetState();
}
@@ -2637,7 +2502,8 @@ bool Webserver::TelnetInterpreter::CanParseData()
}
// In order to support TCP streaming mode, check if we can store any more data at this time
- if (GetGCodeBufferSpace() < clientPointer + 1)
+ RegularGCodeInput * const telnetInput = reprap.GetGCodes()->GetTelnetInput();
+ if (telnetInput->BufferSpaceLeft() < clientPointer + 1)
{
webserver->currentTransaction->Defer(DeferralMode::DeferOnly);
return false;
@@ -2696,7 +2562,8 @@ bool Webserver::TelnetInterpreter::CharFromClient(char c)
{
// This line is complete, do we have enough space left to store it?
clientMessage[clientPointer] = 0;
- if (GetGCodeBufferSpace() < clientPointer + 1)
+ RegularGCodeInput * const telnetInput = reprap.GetGCodes()->GetTelnetInput();
+ if (telnetInput->BufferSpaceLeft() < clientPointer + 1)
{
// No - defer this transaction, so we can process more of it next time
webserver->currentTransaction->Defer(DeferralMode::DeferOnly);
@@ -2730,7 +2597,6 @@ void Webserver::TelnetInterpreter::ResetState()
state = idle;
connectTime = 0;
clientPointer = 0;
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
}
// Usually we should not try to send any data here, because that would purge the packet's
@@ -2773,70 +2639,15 @@ bool Webserver::TelnetInterpreter::ProcessLine()
transaction->Commit(false);
return true;
}
+
// All other codes are stored for the GCodes class
- ProcessGcode(clientMessage);
+ RegularGCodeInput * const telnetInput = reprap.GetGCodes()->GetTelnetInput();
+ telnetInput->Put(TELNET_MESSAGE, clientMessage);
break;
}
return false;
}
-// Process a null-terminated gcode
-// We intercept one M Codes so we can deal with emergencies. That
-// way things don't get out of sync, and - as a file name can contain
-// a valid G code (!) - confusion is avoided.
-void Webserver::TelnetInterpreter::ProcessGcode(const char* gc)
-{
- if (StringStartsWith(gc, "M112") && !isdigit(gc[4])) // emergency stop
- {
- reprap.EmergencyStop();
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
- reprap.GetGCodes()->Reset();
- }
- else
- {
- StoreGcodeData(gc, strlen(gc) + 1);
- }
-}
-
-// Process a received string of gcodes
-void Webserver::TelnetInterpreter::StoreGcodeData(const char* data, uint16_t len)
-{
- if (len > GetGCodeBufferSpace())
- {
- platform->Message(HOST_MESSAGE, "Error: GCode buffer overflow in Telnet Webserver!\n");
- }
- else
- {
- uint16_t remaining = gcodeBufferLength - gcodeWriteIndex;
- if (len <= remaining)
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, len);
- }
- else
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, remaining);
- memcpy(gcodeBuffer, data + remaining, len - remaining);
- }
- gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufferLength;
- }
-}
-
-// Feeding G Codes to the GCodes class
-char Webserver::TelnetInterpreter::ReadGCode()
-{
- char c;
- if (gcodeReadIndex == gcodeWriteIndex)
- {
- c = 0;
- }
- else
- {
- c = gcodeBuffer[gcodeReadIndex];
- gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufferLength;
- }
- return c;
-}
-
// Handle a G-Code reply from the GCodes class; replace \n with \r\n
void Webserver::TelnetInterpreter::HandleGCodeReply(OutputBuffer *reply)
{
diff --git a/src/DuetNG/DuetEthernet/Webserver.h b/src/DuetNG/DuetEthernet/Webserver.h
index 3e4f2c30..698f6ea4 100644
--- a/src/DuetNG/DuetEthernet/Webserver.h
+++ b/src/DuetNG/DuetEthernet/Webserver.h
@@ -130,15 +130,10 @@ public:
void Exit();
void Diagnostics(MessageType mtype);
- bool GCodeAvailable(const WebSource source) const;
- char ReadGCode(const WebSource source);
void HandleGCodeReply(const WebSource source, OutputBuffer *reply);
void HandleGCodeReply(const WebSource source, const char *reply);
uint32_t GetReplySeq() const;
- // Returns the available G-Code buffer space of the HTTP interpreter (may be dropped in a future version)
- uint16_t GetGCodeBufferSpace(const WebSource source) const;
-
void ConnectionLost(Connection conn /*const ConnectionState *cs*/);
void ConnectionError();
@@ -161,11 +156,8 @@ protected:
bool DoingFastUpload() const override;
void DoFastUpload();
- bool GCodeAvailable() const;
- char ReadGCode();
void HandleGCodeReply(OutputBuffer *reply);
void HandleGCodeReply(const char *reply);
- uint16_t GetGCodeBufferSpace() const;
uint32_t GetReplySeq() const;
private:
@@ -234,17 +226,7 @@ protected:
bool RemoveAuthentication();
const char* GetKeyValue(const char *key) const; // return the value of the specified key, or nullptr if not present
- // Deal with incoming G-Codes
-
- char gcodeBuffer[gcodeBufferLength];
- uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
-
- void LoadGcodeBuffer(const char* gc);
- void ProcessGcode(const char* gc);
- void StoreGcodeData(const char* data, uint16_t len);
-
// Responses from GCodes class
-
uint32_t seq; // Sequence number for G-Code replies
OutputStack *gcodeReply;
@@ -318,11 +300,8 @@ protected:
bool CharFromClient(const char c) override;
void ResetState();
- bool GCodeAvailable() const;
- char ReadGCode();
void HandleGCodeReply(OutputBuffer *reply);
void HandleGCodeReply(const char *reply);
- uint16_t GetGCodeBufferSpace() const;
void SendGCodeReply();
@@ -345,16 +324,7 @@ protected:
bool ProcessLine();
- // Deal with incoming G-Codes
-
- char gcodeBuffer[gcodeBufferLength];
- uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
-
- void ProcessGcode(const char* gc);
- void StoreGcodeData(const char* data, uint16_t len);
-
// Converted response from GCodes class (NL -> CRNL)
-
OutputBuffer * volatile gcodeReply;
};
TelnetInterpreter *telnetInterpreter;
@@ -376,11 +346,6 @@ inline bool ProtocolInterpreter::IsUploading() const { return uploadState != not
inline uint32_t Webserver::GetReplySeq() const { return httpInterpreter->GetReplySeq(); }
-inline uint16_t Webserver::HttpInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
-inline bool Webserver::HttpInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
inline uint32_t Webserver::HttpInterpreter::GetReplySeq() const { return seq; }
-inline uint16_t Webserver::TelnetInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
-inline bool Webserver::TelnetInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
-
#endif
diff --git a/src/DuetNG/DuetWiFi/Webserver.cpp b/src/DuetNG/DuetWiFi/Webserver.cpp
index 4b290ed9..d7be7363 100644
--- a/src/DuetNG/DuetWiFi/Webserver.cpp
+++ b/src/DuetNG/DuetWiFi/Webserver.cpp
@@ -96,7 +96,6 @@ const char* badEscapeResponse = "bad escape";
// Constructor and initialisation
Webserver::Webserver(Platform* p, Network *n) : state(doingFilename), platform(p), network(n), numSessions(0), clientsServed(0)
{
- gcodeReadIndex = gcodeWriteIndex = 0;
gcodeReply = new OutputStack();
processingDeferredRequest = false;
seq = 0;
@@ -423,42 +422,6 @@ void Webserver::Diagnostics(MessageType mtype)
platform->MessageF(mtype, "HTTP sessions: %d of %d\n", numSessions, maxHttpSessions);
}
-bool Webserver::GCodeAvailable(const WebSource source) const
-{
- switch (source)
- {
- case WebSource::HTTP:
- return gcodeReadIndex != gcodeWriteIndex;
-
- case WebSource::Telnet:
- // Telnet not supported
- return false;
- }
-
- return false;
-}
-
-char Webserver::ReadGCode(const WebSource source)
-{
- switch (source)
- {
- case WebSource::HTTP:
- if (gcodeReadIndex != gcodeWriteIndex)
- {
- char c = gcodeBuffer[gcodeReadIndex];
- gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufferLength;
- return c;
- }
- break;
-
- case WebSource::Telnet:
- // Telnet not supported
- return 0;
- }
-
- return 0;
-}
-
void Webserver::HandleGCodeReply(const WebSource source, OutputBuffer *reply)
{
switch (source)
@@ -733,10 +696,11 @@ bool Webserver::ProcessFirstFragment(HttpSession& session, const char* command,
const char* gcodeVal = GetKeyValue("gcode");
if (gcodeVal != nullptr)
{
- LoadGcodeBuffer(gcodeVal);
+ RegularGCodeInput * const httpInput = reprap.GetGCodes()->GetHTTPInput();
+ httpInput->Put(HTTP_MESSAGE, gcodeVal);
if (OutputBuffer::Allocate(response))
{
- response->printf("{\"buff\":%u}", GetGCodeBufferSpace());
+ response->printf("{\"buff\":%u}", httpInput->BufferSpaceLeft());
}
}
else
@@ -1016,96 +980,4 @@ void Webserver::CheckSessions()
}
}
-// Process a received string of gcodes
-void Webserver::LoadGcodeBuffer(const char* gc)
-{
- char gcodeTempBuf[GCODE_LENGTH];
- uint16_t gtp = 0;
- bool inComment = false;
- for (;;)
- {
- char c = *gc++;
- if (c == 0)
- {
- gcodeTempBuf[gtp] = 0;
- ProcessGcode(gcodeTempBuf);
- return;
- }
-
- if (c == '\n')
- {
- gcodeTempBuf[gtp] = 0;
- ProcessGcode(gcodeTempBuf);
- gtp = 0;
- inComment = false;
- }
- else
- {
- if (c == ';')
- {
- inComment = true;
- }
-
- if (gtp == ARRAY_UPB(gcodeTempBuf))
- {
- // gcode is too long, we haven't room for another character and a null
- if (c != ' ' && !inComment)
- {
- platform->Message(HOST_MESSAGE, "Error: GCode local buffer overflow in HTTP webserver.\n");
- return;
- }
- // else we're either in a comment or the current character is a space.
- // If we're in a comment, we'll silently truncate it.
- // If the current character is a space, we'll wait until we see a non-comment character before reporting an error,
- // in case the next character is end-of-line or the start of a comment.
- }
- else
- {
- gcodeTempBuf[gtp++] = c;
- }
- }
- }
-}
-
-// Process a null-terminated gcode
-// We intercept one M Codes so we can deal with emergencies. That
-// way things don't get out of sync, and - as a file name can contain
-// a valid G code (!) - confusion is avoided.
-void Webserver::ProcessGcode(const char* gc)
-{
- if (StringStartsWith(gc, "M112") && !isdigit(gc[4])) // emergency stop
- {
- reprap.EmergencyStop();
- gcodeReadIndex = gcodeWriteIndex; // clear the buffer
- reprap.GetGCodes()->Reset();
- }
- else
- {
- StoreGcodeData(gc, strlen(gc) + 1);
- }
-}
-
-// Process a received string of gcodes
-void Webserver::StoreGcodeData(const char* data, uint16_t len)
-{
- if (len > GetGCodeBufferSpace())
- {
- platform->Message(HOST_MESSAGE, "Error: GCode buffer overflow in HTTP Webserver!\n");
- }
- else
- {
- uint16_t remaining = gcodeBufferLength - gcodeWriteIndex;
- if (len <= remaining)
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, len);
- }
- else
- {
- memcpy(gcodeBuffer + gcodeWriteIndex, data, remaining);
- memcpy(gcodeBuffer, data + remaining, len - remaining);
- }
- gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufferLength;
- }
-}
-
// End
diff --git a/src/DuetNG/DuetWiFi/Webserver.h b/src/DuetNG/DuetWiFi/Webserver.h
index d8459d8d..a1d5bbc6 100644
--- a/src/DuetNG/DuetWiFi/Webserver.h
+++ b/src/DuetNG/DuetWiFi/Webserver.h
@@ -59,13 +59,9 @@ public:
void Exit();
void Diagnostics(MessageType mtype);
- bool GCodeAvailable(const WebSource source) const;
- char ReadGCode(const WebSource source);
void HandleGCodeReply(const WebSource source, OutputBuffer *reply);
void HandleGCodeReply(const WebSource source, const char *reply);
uint32_t GetReplySeq() const { return seq; }
- // Returns the available G-Code buffer space of the HTTP interpreter (may be dropped in a future version)
- uint16_t GetGCodeBufferSpace(const WebSource source) const { return 0; }
private:
static const uint32_t lastFragmentFlag = 0x80000000;
@@ -112,16 +108,9 @@ private:
bool GetJsonResponse(uint32_t remoteIp, const char* request, OutputBuffer *&response, const char* key, const char* value, size_t valueLength, bool& keepOpen);
void SendFile(const char* nameOfFileToSend, HttpSession& session);
- // Deal with incoming G-Codes
- char gcodeBuffer[gcodeBufferLength];
- uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
uint32_t seq; // sequence number for G-Code replies
- void LoadGcodeBuffer(const char* gc);
- void ProcessGcode(const char* gc);
- void StoreGcodeData(const char* data, uint16_t len);
void SendGCodeReply(HttpSession& session);
- uint16_t GetGCodeBufferSpace() const;
HttpSession *StartSession(uint32_t ip); // start a new session for this requester
HttpSession *FindSession(uint32_t ip); // find an existing session for this requester
@@ -170,9 +159,4 @@ private:
size_t numSessions, clientsServed;
};
-inline uint16_t Webserver::GetGCodeBufferSpace() const
-{
- return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength;
-}
-
#endif
diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp
index 4b770829..d6659b7c 100644
--- a/src/GCodes/GCodeBuffer.cpp
+++ b/src/GCodes/GCodeBuffer.cpp
@@ -12,13 +12,19 @@
#include "RepRap.h"
// Create a default GCodeBuffer
-GCodeBuffer::GCodeBuffer(const char* id, MessageType mt)
+GCodeBuffer::GCodeBuffer(const char* id, MessageType mt, bool usesCodeQueue)
: machineState(new GCodeMachineState()), identity(id), checksumRequired(false), writingFileDirectory(nullptr),
- toolNumberAdjust(0), responseMessageType(mt)
+ toolNumberAdjust(0), responseMessageType(mt), queueCodes(usesCodeQueue)
{
Init();
}
+void GCodeBuffer::Reset()
+{
+ while (PopState()) { }
+ Init();
+}
+
void GCodeBuffer::Init()
{
gcodePointer = 0;
@@ -31,17 +37,28 @@ void GCodeBuffer::Diagnostics(MessageType mtype)
{
switch (bufferState)
{
- case GCodeBufferState::idle:
- reprap.GetPlatform()->MessageF(mtype, "%s is idle\n", identity);
- break;
+ case GCodeBufferState::idle:
+ scratchString.printf("%s is idle", identity);
+ break;
- case GCodeBufferState::ready:
- reprap.GetPlatform()->MessageF(mtype, "%s is ready with \"%s\"\n", identity, Buffer());
- break;
+ case GCodeBufferState::ready:
+ scratchString.printf("%s is ready with \"%s\"", identity, Buffer());
+ break;
- case GCodeBufferState::executing:
- reprap.GetPlatform()->MessageF(mtype, "%s is doing \"%s\"\n", identity, Buffer());
+ case GCodeBufferState::executing:
+ scratchString.printf("%s is doing \"%s\"", identity, Buffer());
+ }
+
+ scratchString.cat(" in state(s)");
+ const GCodeMachineState *ms = machineState;
+ do
+ {
+ scratchString.catf(" %d", ms->state);
+ ms = ms->previous;
}
+ while (ms != nullptr);
+ scratchString.cat('\n');
+ reprap.GetPlatform()->Message(mtype, scratchString.Pointer());
}
int GCodeBuffer::CheckSum() const
diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h
index 49141d22..ffca0c1f 100644
--- a/src/GCodes/GCodeBuffer.h
+++ b/src/GCodes/GCodeBuffer.h
@@ -16,8 +16,9 @@
class GCodeBuffer
{
public:
- GCodeBuffer(const char* id, MessageType mt);
- void Init(); // Set it up
+ GCodeBuffer(const char* id, MessageType mt, bool useCodeQueue);
+ void Reset(); // Reset it to its state after start-up
+ void Init(); // Set it up to parse another G-code
void Diagnostics(MessageType mtype); // Write some debug info
bool Put(char c); // Add a character to the end
bool Put(const char *str, size_t len); // Add an entire string
@@ -53,6 +54,7 @@ public:
void SetState(GCodeState newState);
void AdvanceState();
const char *GetIdentity() const { return identity; }
+ const bool CanQueueCodes() const { return queueCodes; }
uint32_t whenTimerStarted; // when we started waiting
bool timerRunning; // true if we are waiting
@@ -75,10 +77,11 @@ private:
int readPointer; // Where in the buffer to read next
bool inComment; // Are we after a ';' character?
bool checksumRequired; // True if we only accept commands with a valid checksum
- GCodeBufferState bufferState; // Idle, executing or paused
+ GCodeBufferState bufferState; // Idle, executing or paused
const char* writingFileDirectory; // If the G Code is going into a file, where that is
int toolNumberAdjust; // The adjustment to tool numbers in commands we receive
const MessageType responseMessageType; // The message type we use for responses to commands coming from this channel
+ bool queueCodes; // Can we queue certain G-codes from this source?
};
inline const char* GCodeBuffer::Buffer() const
diff --git a/src/GCodes/GCodeInput.cpp b/src/GCodes/GCodeInput.cpp
new file mode 100644
index 00000000..ee97ec71
--- /dev/null
+++ b/src/GCodes/GCodeInput.cpp
@@ -0,0 +1,322 @@
+/*
+ * GCodeInput.cpp
+ *
+ * Created on: 16 Sep 2016
+ * Author: Christian
+ */
+
+#include "GCodeInput.h"
+
+#include "RepRap.h"
+#include "GCodes.h"
+
+
+// G-code input class for wrapping around Stream-based hardware ports
+
+void StreamGCodeInput::Reset()
+{
+ while (device.available() > 0)
+ {
+ device.read();
+ }
+}
+
+bool StreamGCodeInput::FillBuffer(GCodeBuffer *gb)
+{
+ size_t bytesToPass = min<size_t>(device.available(), GCODE_LENGTH);
+ for(size_t i = 0; i < bytesToPass; i++)
+ {
+ char c = static_cast<char>(device.read());
+
+ if (gb->WritingFileDirectory() == reprap.GetPlatform()->GetWebDir())
+ {
+ // HTML uploads are handled by the GCodes class
+ reprap.GetGCodes()->WriteHTMLToFile(*gb, c);
+ }
+ else if (gb->Put(c))
+ {
+ // Check if we can finish a file upload
+ if (gb->WritingFileDirectory() != nullptr)
+ {
+ reprap.GetGCodes()->WriteGCodeToFile(*gb);
+ gb->SetFinished(true);
+ }
+
+ // Code is complete, stop here
+ return true;
+ }
+ }
+
+ return false;
+}
+
+size_t StreamGCodeInput::BytesCached() const
+{
+ return device.available();
+}
+
+
+// Dynamic G-code input class for caching codes from software-defined sources
+
+
+RegularGCodeInput::RegularGCodeInput(bool removeComments): stripComments(removeComments),
+ state(GCodeInputState::idle), buffer(reinterpret_cast<char * const>(buf32)), writingPointer(0), readingPointer(0)
+{
+}
+
+void RegularGCodeInput::Reset()
+{
+ state = GCodeInputState::idle;
+ writingPointer = readingPointer = 0;
+}
+
+bool RegularGCodeInput::FillBuffer(GCodeBuffer *gb)
+{
+ size_t bytesToPass = min<size_t>(BytesCached(), GCODE_LENGTH);
+ for(size_t i = 0; i < bytesToPass; i++)
+ {
+ // Get a char from the buffer
+ char c = buffer[readingPointer++];
+ if (readingPointer == GCodeInputBufferSize)
+ {
+ readingPointer = 0;
+ }
+
+ // Pass it on to the GCodeBuffer
+ if (gb->WritingFileDirectory() == reprap.GetPlatform()->GetWebDir())
+ {
+ // HTML uploads are handled by the GCodes class
+ reprap.GetGCodes()->WriteHTMLToFile(*gb, c);
+ }
+ else if (gb->Put(c))
+ {
+ // Check if we can finish a file upload
+ if (gb->WritingFileDirectory() != nullptr)
+ {
+ reprap.GetGCodes()->WriteGCodeToFile(*gb);
+ gb->SetFinished(true);
+ }
+
+ // Code is complete, stop here
+ return true;
+ }
+ }
+
+ return false;
+}
+
+size_t RegularGCodeInput::BytesCached() const
+{
+ if (writingPointer >= readingPointer)
+ {
+ return writingPointer - readingPointer;
+ }
+ return GCodeInputBufferSize - readingPointer + writingPointer;
+}
+
+void RegularGCodeInput::Put(MessageType mtype, const char c)
+{
+ if (BufferSpaceLeft() == 0)
+ {
+ // Don't let the buffer overflow if we run out of space
+ return;
+ }
+
+ // Check for M112 (emergency stop) and for M122 (diagnostics) while receiving new characters
+ switch (state)
+ {
+ case GCodeInputState::idle:
+ if (c <= ' ')
+ {
+ // ignore whitespaces at the beginning
+ return;
+ }
+
+ state = (c == 'M') ? GCodeInputState::doingMCode : GCodeInputState::doingCode;
+ break;
+
+
+ case GCodeInputState::doingCode:
+ if (stripComments && c == ';')
+ {
+ // ignore comments if possible
+ state = GCodeInputState::inComment;
+ break;
+ }
+ // no break
+
+ case GCodeInputState::inComment:
+ if (c == 0 || c == '\r' || c == '\n')
+ {
+ state = GCodeInputState::idle;
+ }
+ break;
+
+ case GCodeInputState::doingMCode:
+ if (c == '1')
+ {
+ state = GCodeInputState::doingMCode1;
+ }
+ break;
+
+ case GCodeInputState::doingMCode1:
+ if (c == '1')
+ {
+ state = GCodeInputState::doingMCode11;
+ }
+ else if (c == '2')
+ {
+ state = GCodeInputState::doingMCode12;
+ }
+ else
+ {
+ state = GCodeInputState::doingCode;
+ }
+ break;
+
+ case GCodeInputState::doingMCode11:
+ if (c == '2')
+ {
+ state = GCodeInputState::doingMCode112;
+ break;
+ }
+ state = GCodeInputState::doingCode;
+ break;
+
+ case GCodeInputState::doingMCode12:
+ if (c == '2')
+ {
+ state = GCodeInputState::doingMCode122;
+ break;
+ }
+ state = GCodeInputState::doingCode;
+ break;
+
+ case GCodeInputState::doingMCode112:
+ if (c <= ' ' || c == ';')
+ {
+ // Emergency stop requested - perform it now
+ reprap.EmergencyStop();
+ reprap.GetGCodes()->Reset();
+
+ // But don't run it twice
+ Reset();
+ return;
+ }
+
+ state = GCodeInputState::doingCode;
+ break;
+
+ case GCodeInputState::doingMCode122:
+ if (c <= ' ' || c == ';')
+ {
+ // Diagnostics requested - report them now
+ // Only send the report to the appropriate channel, because if we send it as a generic message instead then it gets truncated.
+ reprap.Diagnostics(mtype);
+
+ // But don't report them twice
+ Reset();
+ return;
+ }
+ break;
+ }
+
+ // Feed another character into the buffer
+ if (state != GCodeInputState::inComment)
+ {
+ buffer[writingPointer++] = c;
+ if (writingPointer == GCodeInputBufferSize)
+ {
+ writingPointer = 0;
+ }
+ }
+}
+
+void RegularGCodeInput::Put(MessageType mtype, const char *buf)
+{
+ Put(mtype, buf, strlen(buf) + 1);
+}
+
+void RegularGCodeInput::Put(MessageType mtype, const char *buf, size_t len)
+{
+ if (len > BufferSpaceLeft())
+ {
+ // Don't cache this if we don't have enough space left
+ return;
+ }
+
+ for (size_t i = 0; i < len; i++)
+ {
+ Put(mtype, buf[i]);
+ }
+}
+
+size_t RegularGCodeInput::BufferSpaceLeft() const
+{
+ return (readingPointer - writingPointer - 1u) % GCodeInputBufferSize;
+}
+
+
+// File-based G-code input source
+
+// Reset this input. Should be called when the associated file is being closed
+void FileGCodeInput::Reset()
+{
+ lastFile = nullptr;
+ RegularGCodeInput::Reset();
+}
+
+// Read another chunk of G-codes from the file and return true if more data is available
+bool FileGCodeInput::ReadFromFile(FileData &file)
+{
+ const size_t bytesCached = BytesCached();
+
+ // Keep track of the last file we read from
+ if (lastFile != nullptr && lastFile != file.f)
+ {
+ if (bytesCached > 0)
+ {
+ // Rewind back to the right position so we can resume at the right position later.
+ // This may be necessary when nested macros are executed.
+ lastFile->Seek(lastFile->Position() - bytesCached);
+ }
+
+ RegularGCodeInput::Reset();
+ }
+ lastFile = file.f;
+
+ // Read more from the file
+ if (bytesCached < GCodeInputFileReadThreshold)
+ {
+ // Reset the read+write pointers for better performance if possible
+ if (readingPointer == writingPointer)
+ {
+ readingPointer = writingPointer = 0;
+ }
+
+ // Read blocks with sizes multiple of 4 for HSMCI efficiency
+ uint32_t readBuffer32[(GCodeInputBufferSize + 3) / 4];
+ char * const readBuffer = reinterpret_cast<char * const>(readBuffer32);
+
+ int bytesRead = file.Read(readBuffer, BufferSpaceLeft() & (~3));
+ if (bytesRead > 0)
+ {
+ int remaining = GCodeInputBufferSize - writingPointer;
+ if (bytesRead <= remaining)
+ {
+ memcpy(buffer + writingPointer, readBuffer, bytesRead);
+ }
+ else
+ {
+ memcpy(buffer + writingPointer, readBuffer, remaining);
+ memcpy(buffer, readBuffer + remaining, bytesRead - remaining);
+ }
+ writingPointer = (writingPointer + bytesRead) % GCodeInputBufferSize;
+
+ return true;
+ }
+ }
+
+ return bytesCached > 0;
+}
+
diff --git a/src/GCodes/GCodeInput.h b/src/GCodes/GCodeInput.h
new file mode 100644
index 00000000..1c0084ba
--- /dev/null
+++ b/src/GCodes/GCodeInput.h
@@ -0,0 +1,107 @@
+/*
+ * GCodeInput.h
+ *
+ * Created on: 16 Sep 2016
+ * Author: Christian
+ */
+
+#ifndef GCODEINPUT_H
+#define GCODEINPUT_H
+
+#include "RepRapFirmware.h"
+
+#include "GCodeBuffer.h"
+#include "Storage/FileStore.h"
+
+
+const size_t GCodeInputBufferSize = 256; // How many bytes can we cache per input source?
+const size_t GCodeInputFileReadThreshold = 128; // How many free bytes must be available before data is read from the SD card?
+
+
+// This base class is intended to provide incoming G-codes for the GCodeBuffer class
+class GCodeInput
+{
+public:
+ virtual void Reset() = 0; // Clean all the cached data from this input
+ virtual bool FillBuffer(GCodeBuffer *gb) = 0; // Fill a GCodeBuffer with the last available G-code
+ virtual size_t BytesCached() const = 0; // How many bytes have been cached?
+};
+
+
+// This class wraps around an existing Stream device which lets us avoid double buffering.
+// The only downside is that we cannot (yet) look through the hardware buffer and check for requested emergency stops.
+// TODO: This will require some more work in the Arduino core.
+class StreamGCodeInput : GCodeInput
+{
+public:
+ StreamGCodeInput(Stream &dev) : device(dev) { }
+
+ void Reset() override;
+ bool FillBuffer(GCodeBuffer *gb) override; // Fill a GCodeBuffer with the last available G-code
+ size_t BytesCached() const override; // How many bytes have been cached?
+
+private:
+ Stream &device;
+};
+
+
+// When characters from input sources are received, they should be checked consequently for M112 (Emergency Stop).
+// This allows us to react faster to an incoming emergency stop since other codes may be blocking the associated
+// GCodeBuffer instance.
+enum class GCodeInputState
+{
+ idle,
+ doingCode,
+ inComment,
+ doingMCode,
+ doingMCode1,
+ doingMCode11,
+ doingMCode12,
+ doingMCode112,
+ doingMCode122
+};
+
+// This class allows caching of dynamic content (from web-based sources) and implements a simple ring buffer.
+// In addition, incoming codes are checked for M112 (emergency stop) to execute perform emergency stops as quickly
+// as possible. Comments can be optionally stripped from sources where comments are not needed (e.g. HTTP).
+class RegularGCodeInput : GCodeInput
+{
+public:
+ RegularGCodeInput(bool removeComments);
+
+ void Reset() override;
+ bool FillBuffer(GCodeBuffer *gb) override; // Fill a GCodeBuffer with the last available G-code
+ size_t BytesCached() const override; // How many bytes have been cached?
+
+ void Put(MessageType mtype, const char c); // Append a single character
+ void Put(MessageType mtype, const char *buf); // Append a null-terminated string to the buffer
+ void Put(MessageType mtype, const char *buf, size_t len); // Append a generic string to the buffer
+
+ size_t BufferSpaceLeft() const; // How much space do we have left?
+
+private:
+ bool stripComments;
+ GCodeInputState state;
+
+protected:
+ uint32_t buf32[(GCodeInputBufferSize + 3) / 4];
+ char * const buffer;
+ size_t writingPointer, readingPointer;
+};
+
+// This class is an expansion of the RegularGCodeInput class to buffer G-codes and to rewind file positions when
+// nested G-code files are started. However buffered codes are not explicitly checked for M112.
+class FileGCodeInput : public RegularGCodeInput
+{
+public:
+ FileGCodeInput() : RegularGCodeInput(false), lastFile(nullptr) { }
+
+ void Reset() override; // This should be called when the last file is being closed
+
+ bool ReadFromFile(FileData &file); // Read another chunk of G-codes from the file and return true if more data is available
+
+private:
+ FileStore *lastFile;
+};
+
+#endif
diff --git a/src/GCodes/GCodeQueue.cpp b/src/GCodes/GCodeQueue.cpp
new file mode 100644
index 00000000..f881e4ea
--- /dev/null
+++ b/src/GCodes/GCodeQueue.cpp
@@ -0,0 +1,224 @@
+/*
+ * GCodeQueue.cpp
+ *
+ * Created on: 22 Jun 2016
+ * Author: Christian
+ */
+
+#include "GCodeQueue.h"
+
+#include "RepRap.h"
+#include "GCodes.h"
+#include "Movement/Move.h"
+
+// GCodeQueue class
+
+GCodeQueue::GCodeQueue() : freeItems(nullptr), queuedItems(nullptr)
+{
+ for(size_t i = 0; i < maxQueuedCodes; i++)
+ {
+ freeItems = new QueuedCode(freeItems);
+ }
+}
+
+bool GCodeQueue::QueueCode(GCodeBuffer &gb, uint32_t segmentsLeft)
+{
+ // Don't queue anything if no moves are being performed
+ uint32_t scheduledMoves = reprap.GetMove()->GetScheduledMoves() + segmentsLeft;
+ if (scheduledMoves == reprap.GetMove()->GetCompletedMoves())
+ {
+ return false;
+ }
+
+#if SUPPORT_ROLAND
+ // Don't queue codes if the Roland module is active
+ if (reprap.GetRoland()->Active())
+ {
+ return false;
+ }
+#endif
+
+ // Check for G-Codes that can be queued
+ bool queueCode = false;
+ switch (gb.GetCommandLetter())
+ {
+ case 'G':
+ {
+ const int code = gb.GetIValue();
+
+ // Set active/standby temperatures
+ queueCode = (code == 10 && gb.Seen('P'));
+ break;
+ }
+
+ case 'M':
+ {
+ const int code = gb.GetIValue();
+
+ // Fan control
+ queueCode |= (code == 106 || code == 107);
+
+ // Set temperatures and return immediately
+ queueCode |= (code == 104 || code == 140 || code == 141 || code == 144);
+
+ // Display Message (LCD), Beep, RGB colour, Set servo position
+ queueCode |= (code == 117 || code == 300 || code == 280 || code == 420);
+
+ // Valve control
+ queueCode |= (code == 126 || code == 127);
+ break;
+ }
+ }
+
+ // Does it make sense to queue this code?
+ if (queueCode)
+ {
+ char codeToRun[GCODE_LENGTH];
+ size_t codeToRunLength;
+
+ // Can we queue this code somewhere?
+ if (freeItems == nullptr)
+ {
+ // No - we've run out of free items. Run the first outstanding code
+ queueCode = false;
+ codeToRunLength = strlen(queuedItems->code);
+ strncpy(codeToRun, queuedItems->code, codeToRunLength);
+ codeToRun[ARRAY_UPB(codeToRun)] = 0;
+
+ // Release the first queued item so that it can be reused later
+ QueuedCode *item = queuedItems;
+ queuedItems = item->next;
+ item->next = nullptr;
+ freeItems = item;
+ }
+
+ // Unlink a free element and assign gb's code to it
+ QueuedCode *code = freeItems;
+ freeItems = code->next;
+ code->AssignFrom(gb);
+ code->executeAtMove = scheduledMoves;
+
+ // Append it to the list of queued codes
+ if (queuedItems == nullptr)
+ {
+ queuedItems = code;
+ }
+ else
+ {
+ QueuedCode *last = queuedItems;
+ while (last->Next() != nullptr)
+ {
+ last = last->Next();
+ }
+ last->next = code;
+ }
+ code->next = nullptr;
+
+ // Overwrite the passed gb's content if we could not store its original code
+ if (!queueCode && !gb.Put(codeToRun, codeToRunLength))
+ {
+ gb.Put('\n');
+ }
+ }
+
+ return queueCode;
+}
+
+bool GCodeQueue::FillBuffer(GCodeBuffer *gb)
+{
+ // Can this buffer be filled?
+ if (queuedItems == nullptr || queuedItems->executeAtMove > reprap.GetMove()->GetCompletedMoves())
+ {
+ // No - stop here
+ return false;
+ }
+
+ // Yes - load it into the passed GCodeBuffer instance
+ QueuedCode *code = queuedItems;
+ code->AssignTo(gb);
+
+ // Release this item again
+ queuedItems = queuedItems->next;
+ code->next = freeItems;
+ freeItems = code;
+ return true;
+}
+
+// Because some moves may end before the print is actually paused, we need a method to
+// remove all the entries that will not be executed after the print has finally paused
+void GCodeQueue::PurgeEntries()
+{
+ QueuedCode *item = queuedItems, *lastItem = nullptr;
+ while (item != nullptr)
+ {
+ if (item->executeAtMove > reprap.GetMove()->GetScheduledMoves())
+ {
+ // Release this item
+ QueuedCode *nextItem = item->Next();
+ item->next = freeItems;
+ freeItems = item;
+
+ // Unlink it from the list
+ if (lastItem == nullptr)
+ {
+ queuedItems = nextItem;
+ }
+ else
+ {
+ lastItem->next = nextItem;
+ }
+ item = nextItem;
+ }
+ else
+ {
+ lastItem = item;
+ item = item->Next();
+ }
+ }
+}
+
+void GCodeQueue::Clear()
+{
+ while (queuedItems != nullptr)
+ {
+ QueuedCode *item = queuedItems;
+ queuedItems = item->Next();
+ item->next = freeItems;
+ freeItems = item;
+ }
+}
+
+void GCodeQueue::Diagnostics(MessageType mtype)
+{
+ reprap.GetPlatform()->MessageF(mtype, "Code queue is %s\n", (queuedItems == nullptr) ? "empty." : "not empty:");
+ if (queuedItems != nullptr)
+ {
+ QueuedCode *item = queuedItems;
+ size_t queueLength = 0;
+ do
+ {
+ queueLength++;
+ reprap.GetPlatform()->MessageF(mtype, "Queued '%s' for move %d\n", item->code, item->executeAtMove);
+ } while ((item = item->Next()) != nullptr);
+ reprap.GetPlatform()->MessageF(mtype, "%d of %d codes have been queued.\n", queueLength, maxQueuedCodes);
+ }
+}
+
+
+// QueuedCode class
+
+void QueuedCode::AssignFrom(GCodeBuffer &gb)
+{
+ toolNumberAdjust = gb.GetToolNumberAdjust();
+ strncpy(code, gb.Buffer(), GCODE_LENGTH);
+ code[ARRAY_UPB(code)] = 0;
+}
+
+void QueuedCode::AssignTo(GCodeBuffer *gb)
+{
+ gb->SetToolNumberAdjust(toolNumberAdjust);
+ if (!gb->Put(code, strlen(code)))
+ {
+ gb->Put('\n');
+ }
+}
diff --git a/src/GCodes/GCodeQueue.h b/src/GCodes/GCodeQueue.h
new file mode 100644
index 00000000..fb1d8772
--- /dev/null
+++ b/src/GCodes/GCodeQueue.h
@@ -0,0 +1,53 @@
+/*
+ * GCodeQueue.h
+ *
+ * Created on: 22 Jun 2016
+ * Author: Christian
+ */
+#ifndef GCODEQUEUE_H
+#define GCODEQUEUE_H
+
+#include "RepRapFirmware.h"
+#include "GCodeBuffer.h"
+
+const size_t maxQueuedCodes = 8; // How many codes can be queued?
+
+class QueuedCode;
+
+class GCodeQueue
+{
+ public:
+ GCodeQueue();
+
+ bool QueueCode(GCodeBuffer &gb, uint32_t segmentsLeft); // Attempt to queue a G-code and return true on success
+ bool FillBuffer(GCodeBuffer *gb); // If there is another move to execute at this time, fill a buffer
+ void PurgeEntries(); // Remove stored codes when a print is being paused
+ void Clear(); // Clean up all the stored codes
+
+ void Diagnostics(MessageType mtype);
+
+ private:
+ QueuedCode *freeItems;
+ QueuedCode *queuedItems;
+};
+
+class QueuedCode
+{
+ public:
+ friend class GCodeQueue;
+
+ QueuedCode(QueuedCode *n) : next(n) { }
+ QueuedCode *Next() const { return next; }
+
+ private:
+ QueuedCode *next;
+
+ char code[GCODE_LENGTH];
+ uint32_t executeAtMove;
+ int toolNumberAdjust;
+
+ void AssignFrom(GCodeBuffer &gb);
+ void AssignTo(GCodeBuffer *gb);
+};
+
+#endif
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index f537eb5c..3e12f83c 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -25,6 +25,7 @@
#include "GCodes.h"
#include "GCodeBuffer.h"
+#include "GCodeQueue.h"
#include "Heating/Heat.h"
#include "Platform.h"
#include "Movement/Move.h"
@@ -49,6 +50,7 @@ const char* DefaultHeightMapFile = "heightmap.csv";
const size_t gcodeReplyLength = 2048; // long enough to pass back a reasonable number of files in response to M20
+
void GCodes::RestorePoint::Init()
{
for (size_t i = 0; i < DRIVES; ++i)
@@ -62,12 +64,21 @@ GCodes::GCodes(Platform* p, Webserver* w) :
platform(p), webserver(w), active(false), isFlashing(false),
fileBeingHashed(nullptr), lastWarningMillis(0)
{
- httpGCode = new GCodeBuffer("http", HTTP_MESSAGE);
- telnetGCode = new GCodeBuffer("telnet", TELNET_MESSAGE);
- fileGCode = new GCodeBuffer("file", GENERIC_MESSAGE);
- serialGCode = new GCodeBuffer("serial", HOST_MESSAGE);
- auxGCode = new GCodeBuffer("aux", AUX_MESSAGE);
- daemonGCode = new GCodeBuffer("daemon", GENERIC_MESSAGE);
+ httpInput = new RegularGCodeInput(true);
+ telnetInput = new RegularGCodeInput(true);
+ fileInput = new FileGCodeInput();
+ serialInput = new StreamGCodeInput(SERIAL_MAIN_DEVICE);
+ auxInput = new StreamGCodeInput(SERIAL_AUX_DEVICE);
+
+ httpGCode = new GCodeBuffer("http", HTTP_MESSAGE, false);
+ telnetGCode = new GCodeBuffer("telnet", TELNET_MESSAGE, true);
+ fileGCode = new GCodeBuffer("file", GENERIC_MESSAGE, true);
+ serialGCode = new GCodeBuffer("serial", HOST_MESSAGE, true);
+ auxGCode = new GCodeBuffer("aux", AUX_MESSAGE, false);
+ daemonGCode = new GCodeBuffer("daemon", GENERIC_MESSAGE, false);
+ queuedGCode = new GCodeBuffer("queue", GENERIC_MESSAGE, false);
+
+ codeQueue = new GCodeQueue();
}
void GCodes::Exit()
@@ -94,7 +105,6 @@ void GCodes::Init()
eofStringLength = strlen(eofString);
offSetSet = false;
runningConfigFile = false;
- doingToolChange = false;
active = true;
longWait = platform->Time();
limitAxes = true;
@@ -114,18 +124,22 @@ void GCodes::Init()
retractHop = 0.0;
retractSpeed = unRetractSpeed = DefaultRetractSpeed * SecondsToMinutes;
isRetracted = false;
+ lastAuxStatusReportType = -1; // no status reports requested yet
}
// This is called from Init and when doing an emergency stop
void GCodes::Reset()
{
- httpGCode->Init();
- telnetGCode->Init();
- fileGCode->Init();
- serialGCode->Init();
- auxGCode->Init();
+ // Here we could reset the input sources as well, but this would mess up M122\nM999
+ // because both codes are sent at once from the web interface. Hence we don't do this here
+
+ httpGCode->Reset();
+ telnetGCode->Reset();
+ fileGCode->Reset();
+ serialGCode->Reset();
+ auxGCode->Reset();
auxGCode->SetCommsProperties(1); // by default, we require a checksum on the aux port
- daemonGCode->Init();
+ daemonGCode->Reset();
nextGcodeSource = 0;
@@ -160,10 +174,12 @@ void GCodes::Reset()
simulationMode = 0;
simulationTime = 0.0;
isPaused = false;
+ doingToolChange = false;
moveBuffer.filePos = noFilePosition;
lastEndstopStates = platform->GetAllEndstopStates();
firmwareUpdateModuleMap = 0;
+ codeQueue->Clear();
cancelWait = isWaiting = displayNoToolWarning = displayDeltaNotHomedWarning = false;
for (size_t i = 0; i < NumResources; ++i)
@@ -335,28 +351,8 @@ void GCodes::Spin()
}
else
{
+ CheckReportDue(gb, reply);
isWaiting = true;
-
- // In Marlin emulation mode we should return some sort of undocumented message here every second. Try a standard temperature report.
- if (platform->Emulating() == marlin && gb.GetResponseMessageType() == MessageType::HOST_MESSAGE)
- {
- const uint32_t now = millis();
- if (gb.timerRunning)
- {
- if (now - gb.whenTimerStarted >= 1000)
- {
- gb.whenTimerStarted = now;
- GenerateTemperatureReport(reply);
- reply.cat('\n');
- platform->Message(HOST_MESSAGE, reply.Pointer());
- }
- }
- else
- {
- gb.whenTimerStarted = now;
- gb.timerRunning = true;
- }
- }
}
break;
@@ -696,80 +692,33 @@ void GCodes::StartNextGCode(GCodeBuffer& gb, StringRef& reply)
{
DoFilePrint(gb, reply);
}
+ else if (&gb == queuedGCode)
+ {
+ // Code queue
+ codeQueue->FillBuffer(queuedGCode);
+ }
else if (&gb == httpGCode)
{
// Webserver
- for (unsigned int i = 0; i < 16 && webserver->GCodeAvailable(WebSource::HTTP); ++i)
- {
- const char b = webserver->ReadGCode(WebSource::HTTP);
- if (gb.Put(b))
- {
- // We have a complete gcode
- if (gb.WritingFileDirectory() != nullptr)
- {
- WriteGCodeToFile(gb);
- gb.SetFinished(true);
- }
- else
- {
- gb.SetFinished(ActOnCode(gb, reply));
- }
- break;
- }
- }
+ httpInput->FillBuffer(httpGCode);
}
else if (&gb == telnetGCode)
{
// Telnet
- for (unsigned int i = 0; i < GCODE_LENGTH && webserver->GCodeAvailable(WebSource::Telnet); ++i)
- {
- char b = webserver->ReadGCode(WebSource::Telnet);
- if (gb.Put(b))
- {
- gb.SetFinished(ActOnCode(gb, reply));
- break;
- }
- }
+ telnetInput->FillBuffer(telnetGCode);
}
else if (&gb == serialGCode)
{
// USB interface
- for (unsigned int i = 0; i < 16 && platform->GCodeAvailable(SerialSource::USB); ++i)
- {
- const char b = platform->ReadFromSource(SerialSource::USB);
- // Check the special case of uploading the reprap.htm file
- if (gb.WritingFileDirectory() == platform->GetWebDir())
- {
- WriteHTMLToFile(gb, b);
- }
- else if (gb.Put(b)) // add char to buffer and test whether the gcode is complete
- {
- // We have a complete gcode
- if (gb.WritingFileDirectory() != nullptr)
- {
- WriteGCodeToFile(gb);
- gb.SetFinished(true);
- }
- else
- {
- gb.SetFinished(ActOnCode(gb, reply));
- }
- break;
- }
- }
+ serialInput->FillBuffer(serialGCode);
}
else if (&gb == auxGCode)
{
// Aux serial port (typically PanelDue)
- for (unsigned int i = 0; i < 16 && platform->GCodeAvailable(SerialSource::AUX); ++i)
+ if (auxInput->FillBuffer(auxGCode))
{
- char b = platform->ReadFromSource(SerialSource::AUX);
- if (gb.Put(b)) // add char to buffer and test whether the gcode is complete
- {
- platform->SetAuxDetected();
- gb.SetFinished(ActOnCode(gb, reply));
- break;
- }
+ // by default we assume no PanelDue is attached
+ platform->SetAuxDetected();
}
}
}
@@ -777,66 +726,65 @@ void GCodes::StartNextGCode(GCodeBuffer& gb, StringRef& reply)
void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
{
FileData& fd = gb.MachineState().fileState;
- for (int i = 0; i < 50 && fd.IsLive(); ++i)
+
+ // Do we have more data to process?
+ if (fileInput->ReadFromFile(fd))
{
- char b;
- if (fd.Read(b))
+ // Yes - fill up the GCodeBuffer and run the next code
+ if (fileInput->FillBuffer(&gb))
{
- if (gb.Put(b))
+ gb.SetFinished(ActOnCode(gb, reply));
+ }
+ }
+ else
+ {
+ // We have reached the end of the file. Check for the last line of gcode not ending in newline.
+ if (!gb.StartingNewCode()) // if there is something in the buffer
+ {
+ if (gb.Put('\n')) // in case there wasn't a newline ending the file
{
gb.SetFinished(ActOnCode(gb, reply));
return;
}
}
- else
+
+ gb.Init(); // mark buffer as empty
+
+ if (gb.MachineState().previous == nullptr)
{
- // We have reached the end of the file. Check for the last line of gcode not ending in newline.
- if (!gb.StartingNewCode()) // if there is something in the buffer
+ // Finished printing SD card file
+ // Don't close the file until all moves have been completed, in case the print gets paused.
+ // Also, this keeps the state as 'Printing' until the print really has finished.
+ if (LockMovementAndWaitForStandstill(gb))
{
- if (gb.Put('\n')) // in case there wasn't a newline ending the file
+ fileInput->Reset();
+ fd.Close();
+ UnlockAll(gb);
+ reprap.GetPrintMonitor()->StoppedPrint();
+ if (platform->Emulating() == marlin)
{
- gb.SetFinished(ActOnCode(gb, reply));
- return;
+ // Pronterface expects a "Done printing" message
+ HandleReply(gb, false, "Done printing file");
}
}
-
- gb.Init(); // mark buffer as empty
-
- if (gb.MachineState().previous == nullptr)
+ }
+ else
+ {
+ // Finished a macro or finished processing config.g
+ fileInput->Reset();
+ fd.Close();
+ if (runningConfigFile)
{
- // Finished printing SD card file
- // Don't close the file until all moves have been completed, in case the print gets paused.
- // Also, this keeps the state as 'Printing' until the print really has finished.
- if (LockMovementAndWaitForStandstill(gb))
- {
- fd.Close();
- UnlockAll(gb);
- reprap.GetPrintMonitor()->StoppedPrint();
- if (platform->Emulating() == marlin)
- {
- // Pronterface expects a "Done printing" message
- HandleReply(gb, false, "Done printing file");
- }
- }
+ CopyConfigFinalValues(gb);
+ runningConfigFile = false;
}
- else
+ Pop(gb);
+ gb.Init();
+ if (gb.GetState() == GCodeState::normal)
{
- // Finished a macro or finished processing config.g
- fd.Close();
- if (runningConfigFile)
- {
- CopyConfigFinalValues(gb);
- runningConfigFile = false;
- }
- Pop(gb);
- gb.Init();
- if (gb.GetState() == GCodeState::normal)
- {
- UnlockAll(gb);
- HandleReply(gb, false, "");
- }
+ UnlockAll(gb);
+ HandleReply(gb, false, "");
}
- return;
}
}
}
@@ -934,6 +882,9 @@ void GCodes::DoPause(GCodeBuffer& gb)
{
fdata.Seek(fPos); // replay the abandoned instructions if/when we resume
}
+ fileInput->Reset();
+ codeQueue->PurgeEntries();
+
if (segmentsLeft != 0)
{
for (size_t drive = numAxes; drive < DRIVES; ++drive)
@@ -969,13 +920,15 @@ void GCodes::Diagnostics(MessageType mtype)
platform->Message(mtype, "=== GCodes ===\n");
platform->MessageF(mtype, "Segments left: %u\n", segmentsLeft);
platform->MessageF(mtype, "Stack records: %u allocated, %u in use\n", GCodeMachineState::GetNumAllocated(), GCodeMachineState::GetNumInUse());
- const GCodeBuffer *movementOwner = resourceOwners[MoveResource];
+ const GCodeBuffer * const movementOwner = resourceOwners[MoveResource];
platform->MessageF(mtype, "Movement lock held by %s\n", (movementOwner == nullptr) ? "null" : movementOwner->GetIdentity());
for (size_t i = 0; i < ARRAY_SIZE(gcodeSources); ++i)
{
gcodeSources[i]->Diagnostics(mtype);
}
+
+ codeQueue->Diagnostics(mtype);
}
// Lock movement and wait for pending moves to finish.
@@ -1360,7 +1313,7 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply)
break;
}
}
- moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() : noFilePosition;
+ moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() - fileInput->BytesCached() : noFilePosition;
moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0);
//debugPrintf("Queue move pos %u\n", moveFilePos);
}
@@ -1425,16 +1378,19 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
// Deal with the X axes
for (size_t axis = 0; axis < numAxes; ++axis)
{
- arcCentre[axis] = moveBuffer.initialCoords[axis] + iParam;
- if ((arcAxesMoving & (1 << axis)) != 0)
+ if (axis != Y_AXIS)
{
- if (axesRelative)
- {
- moveBuffer.coords[axis] += xParam;
- }
- else
+ arcCentre[axis] = moveBuffer.initialCoords[axis] + iParam;
+ if ((arcAxesMoving & (1 << axis)) != 0)
{
- moveBuffer.coords[axis] = xParam - currentTool->GetOffset()[axis];
+ if (axesRelative)
+ {
+ moveBuffer.coords[axis] += xParam;
+ }
+ else
+ {
+ moveBuffer.coords[axis] = xParam - currentTool->GetOffset()[axis];
+ }
}
}
}
@@ -1548,9 +1504,10 @@ bool GCodes::ReadMove(RawMove& m)
{
// Calculate the move length, to see how much new babystepping is appropriate for this move
float xMoveLength = 0.0;
+ const uint32_t xAxes = reprap.GetCurrentXAxes();
for (size_t drive = 0; drive < numAxes; ++drive)
{
- if ((arcAxesMoving & (1 << drive)) != 0)
+ if ((xAxes & (1 << drive)) != 0)
{
xMoveLength = max<float>(xMoveLength, fabs(m.coords[drive] - m.initialCoords[drive]));
}
@@ -2389,24 +2346,16 @@ bool GCodes::SaveHeightMap(GCodeBuffer& gb, StringRef& reply) const
return err;
}
-// Clear the height map
-void GCodes::ClearHeightMap() const
-{
- HeightMap& heightMap = reprap.GetMove()->AccessBedProbeGrid();
- heightMap.ClearGridHeights();
- heightMap.UseHeightMap(false);
-}
-
// Return the current coordinates as a printable string.
// Coordinates are updated at the end of each movement, so this won't tell you where you are mid-movement.
void GCodes::GetCurrentCoordinates(StringRef& s) const
{
float liveCoordinates[DRIVES];
reprap.GetMove()->LiveCoordinates(liveCoordinates, reprap.GetCurrentXAxes());
- const Tool *currentTool = reprap.GetCurrentTool();
+ const Tool * const currentTool = reprap.GetCurrentTool();
if (currentTool != nullptr)
{
- const float *offset = currentTool->GetOffset();
+ const float * const offset = currentTool->GetOffset();
for (size_t i = 0; i < numAxes; ++i)
{
liveCoordinates[i] += offset[i];
@@ -2416,7 +2365,7 @@ void GCodes::GetCurrentCoordinates(StringRef& s) const
s.Clear();
for (size_t axis = 0; axis < numAxes; ++axis)
{
- s.catf("%c: %.2f ", axisLetters[axis], liveCoordinates[axis]);
+ s.catf("%c: %.3f ", axisLetters[axis], liveCoordinates[axis]);
}
for (size_t i = numAxes; i < DRIVES; i++)
{
@@ -2477,6 +2426,8 @@ void GCodes::WriteHTMLToFile(GCodeBuffer& gb, char b)
}
else
{
+ // NB: This approach isn't very efficient, but I (chrishamm) think the whole uploading
+ // code should be rewritten anyway in the future and moved away from the GCodes class.
fileBeingWritten->Write(b);
}
}
@@ -2969,21 +2920,8 @@ void GCodes::HandleReply(GCodeBuffer& gb, bool error, const char* reply)
}
const Compatibility c = (&gb == serialGCode || &gb == telnetGCode) ? platform->Emulating() : me;
- MessageType type = GENERIC_MESSAGE;
- if (&gb == httpGCode)
- {
- type = HTTP_MESSAGE;
- }
- else if (&gb == telnetGCode)
- {
- type = TELNET_MESSAGE;
- }
- else if (&gb == serialGCode)
- {
- type = HOST_MESSAGE;
- }
-
- const char* response = (gb.Seen('M') && gb.GetIValue() == 998) ? "rs " : "ok";
+ const MessageType type = gb.GetResponseMessageType();
+ const char* const response = (gb.Seen('M') && gb.GetIValue() == 998) ? "rs " : "ok";
const char* emulationType = 0;
switch (c)
@@ -3072,21 +3010,8 @@ void GCodes::HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply)
}
const Compatibility c = (&gb == serialGCode || &gb == telnetGCode) ? platform->Emulating() : me;
- MessageType type = GENERIC_MESSAGE;
- if (&gb == httpGCode)
- {
- type = HTTP_MESSAGE;
- }
- else if (&gb == telnetGCode)
- {
- type = TELNET_MESSAGE;
- }
- else if (&gb == serialGCode)
- {
- type = HOST_MESSAGE;
- }
-
- const char* response = (gb.Seen('M') && gb.GetIValue() == 998) ? "rs " : "ok";
+ const MessageType type = gb.GetResponseMessageType();
+ const char* const response = (gb.Seen('M') && gb.GetIValue() == 998) ? "rs " : "ok";
const char* emulationType = nullptr;
switch (c)
@@ -3325,7 +3250,7 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
moveBuffer.moveType = 0;
moveBuffer.isFirmwareRetraction = true;
moveBuffer.usePressureAdvance = false;
- moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() : noFilePosition;
+ moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() - fileInput->BytesCached() : noFilePosition;
moveBuffer.canPauseAfter = !retract; // don't pause after a retraction because that could cause too much retraction
moveBuffer.xAxes = xAxes;
segmentsLeft = 1;
@@ -3354,7 +3279,9 @@ void GCodes::CancelPrint()
segmentsLeft = 0;
isPaused = false;
+ fileInput->Reset();
fileGCode->Init();
+
FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
if (fileBeingPrinted.IsLive())
{
@@ -3362,6 +3289,9 @@ void GCodes::CancelPrint()
}
reprap.GetPrintMonitor()->StoppedPrint();
+
+ reprap.GetMove()->ResetMoveCounters();
+ codeQueue->Clear();
}
// Return true if all the heaters for the specified tool are at their set temperatures
@@ -3559,7 +3489,7 @@ bool GCodes::WriteConfigOverrideFile(StringRef& reply, const char *fileName) con
}
// Store a standard-format temperature report in 'reply'. This doesn't put a newline character at the end.
-void GCodes::GenerateTemperatureReport(StringRef& reply)
+void GCodes::GenerateTemperatureReport(StringRef& reply) const
{
const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
@@ -3590,6 +3520,70 @@ void GCodes::GenerateTemperatureReport(StringRef& reply)
}
}
+// Check whether we need to report temperatures or status.
+// 'reply' is a convenient buffer that is free for us to use.
+void GCodes::CheckReportDue(GCodeBuffer& gb, StringRef& reply) const
+{
+ const uint32_t now = millis();
+ if (gb.timerRunning)
+ {
+ if (now - gb.whenTimerStarted >= 1000)
+ {
+ if (platform->Emulating() == marlin && (&gb == serialGCode || &gb == telnetGCode))
+ {
+ // In Marlin emulation mode we should return a standard temperature report every second
+ GenerateTemperatureReport(reply);
+ reply.cat('\n');
+ platform->Message(HOST_MESSAGE, reply.Pointer());
+ }
+ if (lastAuxStatusReportType >= 0)
+ {
+ // Send a standard status response for PanelDue
+ OutputBuffer * const statusBuf = GenerateJsonStatusResponse(0, -1, ResponseSource::AUX);
+ if (statusBuf != nullptr)
+ {
+ platform->AppendAuxReply(statusBuf);
+ }
+ }
+ gb.whenTimerStarted = now;
+ }
+ }
+ else
+ {
+ gb.whenTimerStarted = now;
+ gb.timerRunning = true;
+ }
+}
+
+// Generate a M408 response
+// Return the output buffer containing the response, or nullptr if we failed
+OutputBuffer *GCodes::GenerateJsonStatusResponse(int type, int seq, ResponseSource source) const
+{
+ OutputBuffer *statusResponse = nullptr;
+ switch (type)
+ {
+ case 0:
+ case 1:
+ statusResponse = reprap.GetLegacyStatusResponse(type + 2, seq);
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ statusResponse = reprap.GetStatusResponse(type - 1, source);
+ break;
+
+ case 5:
+ statusResponse = reprap.GetConfigResponse();
+ break;
+ }
+ if (statusResponse != nullptr)
+ {
+ statusResponse->cat('\n');
+ }
+ return statusResponse;
+}
+
// Resource locking/unlocking
// Lock the resource, returning true if success.
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index ade82841..d3d5298e 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -23,10 +23,13 @@ Licence: GPL
#define GCODES_H
#include "RepRapFirmware.h"
+#include "RepRap.h" // for type ResponseSource
#include "Libraries/sha1/sha1.h"
#include "Platform.h" // for type EndStopHit
+#include "GCodeInput.h"
class GCodeBuffer;
+class GCodeQueue;
const char feedrateLetter = 'F'; // GCode feedrate
const char extrudeLetter = 'E'; // GCode extrude
@@ -109,6 +112,12 @@ public:
float GetTotalRawExtrusion() const { return rawExtruderTotal; } // Get the total extrusion since start of print, all drives
float GetBabyStepOffset() const; // Get the current baby stepping Z offset
+ RegularGCodeInput *GetHTTPInput() const { return httpInput; }
+ RegularGCodeInput *GetTelnetInput() const { return telnetInput; }
+
+ void WriteGCodeToFile(GCodeBuffer& gb); // Write this GCode into a file
+ void WriteHTMLToFile(GCodeBuffer& gb, char b); // Save an HTML file (usually to upload a new web interface)
+
bool IsFlashing() const { return isFlashing; } // Is a new firmware binary going to be flashed?
bool IsPaused() const;
@@ -194,16 +203,16 @@ private:
void HandleReply(GCodeBuffer& gb, bool error, const char *reply); // Handle G-Code replies
void HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply);
bool OpenFileToWrite(GCodeBuffer& gb, const char* directory, const char* fileName); // Start saving GCodes in a file
- void WriteGCodeToFile(GCodeBuffer& gb); // Write this GCode into a file
bool SendConfigToLine(); // Deal with M503
- void WriteHTMLToFile(GCodeBuffer& gb, char b); // Save an HTML file (usually to upload a new web interface)
bool OffsetAxes(GCodeBuffer& gb); // Set offsets - deprecated, use G10
void SetPidParameters(GCodeBuffer& gb, int heater, StringRef& reply); // Set the P/I/D parameters for a heater
void SetHeaterParameters(GCodeBuffer& gb, StringRef& reply); // Set the thermistor and ADC parameters for a heater
void ManageTool(GCodeBuffer& gb, StringRef& reply); // Create a new tool definition
void SetToolHeaters(Tool *tool, float temperature); // Set all a tool's heaters to the temperature. For M104...
bool ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling) const; // Wait for the heaters associated with the specified tool to reach their set temperatures
- void GenerateTemperatureReport(StringRef& reply); // Store a standard-format temperature report in reply
+ void GenerateTemperatureReport(StringRef& reply) const; // Store a standard-format temperature report in reply
+ OutputBuffer *GenerateJsonStatusResponse(int type, int seq, ResponseSource source) const; // Generate a M408 response
+ void CheckReportDue(GCodeBuffer& gb, StringRef& reply) const; // Check whether we need to report temperatures or status
void SetAllAxesNotHomed(); // Flag all axes as not homed
void SetPositions(const float positionNow[DRIVES], bool doBedCompensation = true); // Set the current position to be this
@@ -223,7 +232,6 @@ private:
bool ProbeGrid(GCodeBuffer& gb, StringRef& reply); // Start probing the grid, returning true if we didn't because of an error
bool LoadHeightMap(GCodeBuffer& gb, StringRef& reply) const; // Load the height map from file
bool SaveHeightMap(GCodeBuffer& gb, StringRef& reply) const; // Save the height map to file
- void ClearHeightMap() const; // Clear the height map
bool WriteConfigOverrideFile(StringRef& reply, const char *fileName) const; // Write the config-override file
void CopyConfigFinalValues(GCodeBuffer& gb); // Copy the feed rate etc. from the daemon to the input channels
@@ -235,7 +243,13 @@ private:
Platform* const platform; // The RepRap machine
Webserver* const webserver; // The web server class
- GCodeBuffer* gcodeSources[6]; // The various sources of gcodes
+ RegularGCodeInput* httpInput; // These cache incoming G-codes...
+ RegularGCodeInput* telnetInput; // ...
+ FileGCodeInput* fileInput; // ...
+ StreamGCodeInput* serialInput; // ...
+ StreamGCodeInput* auxInput; // ...for the GCodeBuffers below
+
+ GCodeBuffer* gcodeSources[7]; // The various sources of gcodes
GCodeBuffer*& httpGCode = gcodeSources[0];
GCodeBuffer*& telnetGCode = gcodeSources[1];
@@ -243,6 +257,7 @@ private:
GCodeBuffer*& serialGCode = gcodeSources[3];
GCodeBuffer*& auxGCode = gcodeSources[4]; // This one is for the LCD display on the async serial interface
GCodeBuffer*& daemonGCode = gcodeSources[5]; // Used for executing config.g and trigger macro files
+ GCodeBuffer*& queuedGCode = gcodeSources[6];
size_t nextGcodeSource; // The one to check next
const GCodeBuffer* resourceOwners[NumResources]; // Which gcode buffer owns each resource
@@ -323,6 +338,9 @@ private:
uint8_t firmwareUpdateModuleMap; // Bitmap of firmware modules to be updated
bool isFlashing; // Is a new firmware binary going to be flashed?
+ // Code queue
+ GCodeQueue *codeQueue; // Stores certain codes for deferred execution
+
// SHA1 hashing
FileStore *fileBeingHashed;
SHA1Context hash;
@@ -332,6 +350,7 @@ private:
// Misc
float longWait; // Timer for things that happen occasionally (seconds)
uint32_t lastWarningMillis; // When we last sent a warning message for things that can happen very often
+ int8_t lastAuxStatusReportType; // The type of the last status report requested by PanelDue
bool isWaiting; // True if waiting to reach temperature
bool cancelWait; // Set true to cancel waiting
bool displayNoToolWarning; // True if we need to display a 'no tool selected' warning
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index 023ccbb1..d801de2b 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -10,6 +10,7 @@
#include "GCodes.h"
#include "GCodeBuffer.h"
+#include "GCodeQueue.h"
#include "Heating/Heat.h"
#include "Movement/Move.h"
#include "Network.h"
@@ -45,6 +46,13 @@ bool GCodes::ActOnCode(GCodeBuffer& gb, StringRef& reply)
return true;
}
+ // Can we queue this code?
+ if (gb.CanQueueCodes() && codeQueue->QueueCode(gb, segmentsLeft))
+ {
+ HandleReply(gb, false, "");
+ return true;
+ }
+
// G29 string parameters may contain the letter M, and various M-code string parameter may contain the letter G.
// So we now look for the first G, M or T in the command.
switch (gb.GetCommandLetter())
@@ -203,7 +211,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
break;
default: // clear height map
- reprap.GetMove()->AccessBedProbeGrid().ClearGridHeights();
+ reprap.GetMove()->SetIdentityTransform();
break;
}
}
@@ -1210,6 +1218,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
toolNumber += gb.GetToolNumberAdjust();
if (!cancelWait && !ToolHeatersAtSetTemperatures(reprap.GetTool(toolNumber), true))
{
+ CheckReportDue(gb, reply); // check whether we need to send a temperature or status report
isWaiting = true;
return false;
}
@@ -1228,6 +1237,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
if (!reprap.GetHeat()->HeaterAtSetTemperature(heaters[i], true))
{
+ CheckReportDue(gb, reply); // check whether we need to send a temperature or status report
isWaiting = true;
return false;
}
@@ -1244,6 +1254,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
if (!cancelWait && !reprap.GetHeat()->HeaterAtSetTemperature(chamberHeater, true))
{
+ CheckReportDue(gb, reply); // check whether we need to send a temperature or status report
isWaiting = true;
return false;
}
@@ -1254,6 +1265,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
// Wait for all heaters to be ready
if (!seen && !cancelWait && !reprap.GetHeat()->AllHeatersAtSetTemperatures(true))
{
+ CheckReportDue(gb, reply); // check whether we need to send a temperature or status report
isWaiting = true;
return false;
}
@@ -1510,26 +1522,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
}
- // In Marlin emulation mode we should return some sort of undocumented message here every second. Try a standard temperature report.
- if (platform->Emulating() == marlin && gb.GetResponseMessageType() == MessageType::HOST_MESSAGE)
- {
- const uint32_t now = millis();
- if (gb.timerRunning)
- {
- if (now - gb.whenTimerStarted >= 1000)
- {
- gb.whenTimerStarted = now;
- GenerateTemperatureReport(reply);
- reply.cat('\n');
- platform->Message(HOST_MESSAGE, reply.Pointer());
- }
- }
- else
- {
- gb.whenTimerStarted = now;
- gb.timerRunning = true;
- }
- }
+ CheckReportDue(gb, reply); // check whether we need to send a temperature or status report
isWaiting = true;
return false;
}
@@ -1620,7 +1613,27 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
- case 206: // Offset axes - Deprecated
+ case 204: // Set max travel and printing accelerations
+ {
+ bool seen = false;
+ if (gb.Seen('P'))
+ {
+ platform->SetMaxPrintingAcceleration(gb.GetFValue());
+ seen = true;
+ }
+ if (gb.Seen('T'))
+ {
+ platform->SetMaxTravelAcceleration(gb.GetFValue());
+ seen = true;
+ }
+ if (!seen)
+ {
+ reply.printf("Maximum printing acceleration %.1f, maximum travel acceleration %.1f", platform->GetMaxPrintingAcceleration(), platform->GetMaxTravelAcceleration());
+ }
+ }
+ break;
+
+ case 206: // Offset axes - Deprecated
result = OffsetAxes(gb);
break;
@@ -2081,32 +2094,18 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
case 408: // Get status in JSON format
{
- int type = gb.Seen('S') ? gb.GetIValue() : 0;
- int seq = gb.Seen('R') ? gb.GetIValue() : -1;
-
- OutputBuffer *statusResponse = nullptr;
- switch (type)
+ const int type = gb.Seen('S') ? gb.GetIValue() : 0;
+ const int seq = gb.Seen('R') ? gb.GetIValue() : -1;
+ if (&gb == auxGCode)
{
- case 0:
- case 1:
- statusResponse = reprap.GetLegacyStatusResponse(type + 2, seq);
- break;
-
- case 2:
- case 3:
- case 4:
- statusResponse = reprap.GetStatusResponse(type - 1, (&gb == auxGCode) ? ResponseSource::AUX : ResponseSource::Generic);
- break;
-
- case 5:
- statusResponse = reprap.GetConfigResponse();
- break;
+ lastAuxStatusReportType = type;
}
+ OutputBuffer * const statusResponse = GenerateJsonStatusResponse(type, seq, (&gb == auxGCode) ? ResponseSource::AUX : ResponseSource::Generic);
+
if (statusResponse != nullptr)
{
UnlockAll(gb);
- statusResponse->cat('\n');
HandleReply(gb, false, statusResponse);
return true;
}
@@ -2419,35 +2418,35 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 559: // Upload config.g or another gcode file to put in the sys directory
- {
- const char* str = (gb.Seen('P') ? gb.GetString() : platform->GetConfigFile());
- const bool ok = OpenFileToWrite(gb, platform->GetSysDir(), str);
- if (ok)
- {
- reply.printf("Writing to file: %s", str);
- }
- else
{
- reply.printf("Can't open file %s for writing.", str);
- error = true;
+ const char* str = (gb.Seen('P') ? gb.GetString() : platform->GetConfigFile());
+ const bool ok = OpenFileToWrite(gb, platform->GetSysDir(), str);
+ if (ok)
+ {
+ reply.printf("Writing to file: %s", str);
+ }
+ else
+ {
+ reply.printf("Can't open file %s for writing.", str);
+ error = true;
+ }
}
- }
break;
case 560: // Upload reprap.htm or another web interface file
- {
- const char* str = (gb.Seen('P') ? gb.GetString() : INDEX_PAGE_FILE);
- const bool ok = OpenFileToWrite(gb, platform->GetWebDir(), str);
- if (ok)
- {
- reply.printf("Writing to file: %s", str);
- }
- else
{
- reply.printf("Can't open file %s for writing.", str);
- error = true;
+ const char* str = (gb.Seen('P') ? gb.GetString() : INDEX_PAGE_FILE);
+ const bool ok = OpenFileToWrite(gb, platform->GetWebDir(), str);
+ if (ok)
+ {
+ reply.printf("Writing to file: %s", str);
+ }
+ else
+ {
+ reply.printf("Can't open file %s for writing.", str);
+ error = true;
+ }
}
- }
break;
case 561: // Set identity transform (also clears bed probe grid)
diff --git a/src/Heating/Pid.cpp b/src/Heating/Pid.cpp
index 2f4661ec..80f41924 100644
--- a/src/Heating/Pid.cpp
+++ b/src/Heating/Pid.cpp
@@ -664,7 +664,7 @@ void PID::DoTuningStep()
const int peakIndex = GetPeakTempIndex();
if (peakIndex < 0)
{
- if (millis() - tuningPhaseStartTime < 60 * 1000)
+ if (millis() - tuningPhaseStartTime < 120 * 1000) // allow 2 minutes for the bed temperature to start falling
{
return; // still waiting for peak temperature
}
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index 45b007fa..c7273faa 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -327,6 +327,10 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping)
memcpy(normalisedDirectionVector, directionVector, sizeof(normalisedDirectionVector));
Absolute(normalisedDirectionVector, DRIVES);
acceleration = VectorBoxIntersection(normalisedDirectionVector, accelerations, DRIVES);
+ if (xyMoving)
+ {
+ acceleration = min<float>(acceleration, (isPrintingMove) ? reprap.GetPlatform()->GetMaxPrintingAcceleration() : reprap.GetPlatform()->GetMaxTravelAcceleration());
+ }
// 6. Set the speed to the smaller of the requested and maximum speed.
// Also enforce a minimum speed of 0.5mm/sec. We need a minimum speed to avoid overflow in the movement calculations.
diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp
index f95a9d62..1f22546a 100644
--- a/src/Movement/Move.cpp
+++ b/src/Movement/Move.cpp
@@ -9,7 +9,7 @@
#include "Platform.h"
#include "RepRap.h"
-Move::Move(Platform* p, GCodes* g) : currentDda(NULL), grid(zBedProbePoints)
+Move::Move(Platform* p, GCodes* g) : currentDda(NULL), grid(zBedProbePoints), scheduledMoves(0), completedMoves(0)
{
active = false;
@@ -193,6 +193,7 @@ void Move::Spin()
{
ddaRingAddPointer = ddaRingAddPointer->GetNext();
idleCount = 0;
+ scheduledMoves++;
}
}
}
@@ -377,6 +378,7 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
}
(void)dda->Free();
dda = dda->GetNext();
+ scheduledMoves--;
}
while (dda != savedDdaRingAddPointer);
}
@@ -406,6 +408,8 @@ void Move::Diagnostics(MessageType mtype)
numLookaheadUnderruns = numPrepareUnderruns = 0;
longestGcodeWaitInterval = 0;
+ reprap.GetPlatform()->MessageF(mtype, "Scheduled moves: %u, completed moves: %u\n", scheduledMoves, completedMoves);
+
// Show the current probe position heights and type of bed compensation in use
p->Message(mtype, "Bed compensation in use: ");
if (numBedCompensationPoints == 0)
@@ -417,7 +421,8 @@ void Move::Diagnostics(MessageType mtype)
p->MessageF(mtype, "%d point\n", numBedCompensationPoints);
}
p->Message(mtype, "Bed probe heights:");
- for (size_t i = 0; i < MaxProbePoints; ++i)
+ // To keep the response short so that it doesn't get truncates when sending it via HTTP, we only show the first 5 bed probe points
+ for (size_t i = 0; i < 5; ++i)
{
p->MessageF(mtype, " %.3f", ZBedProbePoint(i));
}
@@ -757,6 +762,7 @@ void Move::SetIdentityTransform()
{
numBedCompensationPoints = 0;
grid.ClearGridHeights();
+ grid.UseHeightMap(false);
}
void Move::SetTaperHeight(float h)
@@ -1312,6 +1318,7 @@ void Move::CurrentMoveCompleted()
currentDda->Complete();
currentDda = nullptr;
ddaRingGetPointer = ddaRingGetPointer->GetNext();
+ completedMoves++;
}
// Try to start another move. Must be called with interrupts disabled, to avoid a race condition.
@@ -1430,6 +1437,7 @@ void Move::GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xA
void Move::LiveCoordinates(float m[DRIVES], uint32_t xAxes)
{
// The live coordinates and live endpoints are modified by the ISR, so be careful to get a self-consistent set of them
+ const size_t numAxes = reprap.GetGCodes()->GetNumAxes(); // do this before we disable interrupts
cpu_irq_disable();
if (liveCoordinatesValid)
{
@@ -1440,7 +1448,6 @@ void Move::LiveCoordinates(float m[DRIVES], uint32_t xAxes)
else
{
// Only the extruder coordinates are valid, so we need to convert the motor endpoints to coordinates
- const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
memcpy(m + numAxes, const_cast<const float *>(liveCoordinates + numAxes), sizeof(m[0]) * (DRIVES - numAxes));
int32_t tempEndPoints[MAX_AXES];
memcpy(tempEndPoints, const_cast<const int32_t*>(liveEndPoints), sizeof(tempEndPoints));
diff --git a/src/Movement/Move.h b/src/Movement/Move.h
index 14b3b804..acc5e43f 100644
--- a/src/Movement/Move.h
+++ b/src/Movement/Move.h
@@ -118,6 +118,10 @@ public:
bool IsExtruding() const; // Is filament being extruded?
+ uint32_t GetScheduledMoves() const { return scheduledMoves; } // How many moves have been scheduled?
+ uint32_t GetCompletedMoves() const { return completedMoves; } // How many moves have been completed?
+ void ResetMoveCounters() { scheduledMoves = completedMoves = 0; }
+
HeightMap& AccessBedProbeGrid() { return grid; } // Access the bed probing grid
private:
@@ -195,6 +199,9 @@ private:
int coreXYMode; // 0 = Cartesian, 1 = CoreXY, 2 = CoreXZ, 3 = CoreYZ
float axisFactors[MAX_AXES]; // How much further the motors need to move for each axis movement, on a CoreXY/CoreXZ/CoreYZ machine
unsigned int stepErrors; // count of step errors, for diagnostics
+
+ uint32_t scheduledMoves; // Move counters for the code queue
+ volatile uint32_t completedMoves; // This one is modified by an ISR, hence volatile
};
//******************************************************************************************************
diff --git a/src/Platform.cpp b/src/Platform.cpp
index 4658f777..da7afa52 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -301,6 +301,7 @@ void Platform::Init()
ARRAY_INIT(accelerations, ACCELERATIONS);
ARRAY_INIT(driveStepsPerUnit, DRIVE_STEPS_PER_UNIT);
ARRAY_INIT(instantDvs, INSTANT_DVS);
+ maxPrintingAcceleration = maxTravelAcceleration = 10000.0;
#if !defined(DUET_NG) && !defined(__RADDS__)
// Motor current setting on Duet 0.6 and 0.8.5
@@ -1025,7 +1026,7 @@ void Platform::Beep(int freq, int ms)
}
// Send a short message to the aux channel. There is no flow control on this port, so it can't block for long.
-void Platform::SendMessage(const char* msg)
+void Platform::SendAuxMessage(const char* msg)
{
OutputBuffer *buf;
if (OutputBuffer::Allocate(buf))
@@ -1457,8 +1458,7 @@ void Platform::DisableInterrupts()
//extern "C" uint32_t longestWriteWaitTime, shortestWriteWaitTime, longestReadWaitTime, shortestReadWaitTime;
//extern uint32_t maxRead, maxWrite;
-// This diagnostics function is the first to be called, so it calls Message to start with.
-// All other messages generated by this and other diagnostics functions must call AppendMessage.
+// Return diagnostic information
void Platform::Diagnostics(MessageType mtype)
{
Message(mtype, "=== Platform ===\n");
@@ -1471,14 +1471,12 @@ void Platform::Diagnostics(MessageType mtype)
(char *) 0x20070000;
#endif
const struct mallinfo mi = mallinfo();
- Message(mtype, "Memory usage:\n");
- MessageF(mtype, "Program static ram used: %d\n", &_end - ramstart);
+ MessageF(mtype, "Static ram used: %d\n", &_end - ramstart);
MessageF(mtype, "Dynamic ram used: %d\n", mi.uordblks);
MessageF(mtype, "Recycled dynamic ram: %d\n", mi.fordblks);
uint32_t currentStack, maxStack, neverUsed;
GetStackUsage(&currentStack, &maxStack, &neverUsed);
- MessageF(mtype, "Current stack ram used: %u\n", currentStack);
- MessageF(mtype, "Maximum stack ram used: %u\n", maxStack);
+ MessageF(mtype, "Stack ram used: %u current, %u maximum\n", currentStack, maxStack);
MessageF(mtype, "Never used ram: %u\n", neverUsed);
// Show the up time and reason for the last reset
@@ -1608,7 +1606,7 @@ void Platform::Diagnostics(MessageType mtype)
#endif
// Show current RTC time
- Message(mtype, "Current date and time: ");
+ Message(mtype, "Date/time: ");
struct tm timeInfo;
if (gmtime_r(&realTime, &timeInfo) != nullptr)
{
@@ -1618,7 +1616,7 @@ void Platform::Diagnostics(MessageType mtype)
}
else
{
- Message(mtype, "clock not set\n");
+ Message(mtype, "not set\n");
}
// Debug
@@ -2519,17 +2517,16 @@ void Platform::Message(MessageType type, const char *message)
break;
case HTTP_MESSAGE:
+ reprap.GetWebserver()->HandleGCodeReply(WebSource::HTTP, message);
+ break;
+
case TELNET_MESSAGE:
- // Message that is to be sent to the web
- {
- const WebSource source = (type == HTTP_MESSAGE) ? WebSource::HTTP : WebSource::Telnet;
- reprap.GetWebserver()->HandleGCodeReply(source, message);
- }
+ reprap.GetWebserver()->HandleGCodeReply(WebSource::Telnet, message);
break;
case FIRMWARE_UPDATE_MESSAGE:
Message(HOST_MESSAGE, message); // send message to USB
- SendMessage(message); // send message to aux
+ SendAuxMessage(message); // send message to aux
break;
case GENERIC_MESSAGE:
@@ -2581,12 +2578,11 @@ void Platform::Message(const MessageType type, OutputBuffer *buffer)
break;
case HTTP_MESSAGE:
+ reprap.GetWebserver()->HandleGCodeReply(WebSource::HTTP, buffer);
+ break;
+
case TELNET_MESSAGE:
- // Message that is to be sent to the web
- {
- const WebSource source = (type == HTTP_MESSAGE) ? WebSource::HTTP : WebSource::Telnet;
- reprap.GetWebserver()->HandleGCodeReply(source, buffer);
- }
+ reprap.GetWebserver()->HandleGCodeReply(WebSource::Telnet, buffer);
break;
case GENERIC_MESSAGE:
@@ -2932,48 +2928,6 @@ bool Platform::Inkjet(int bitPattern)
}
#endif
-bool Platform::GCodeAvailable(const SerialSource source) const
-{
- switch (source)
- {
- case SerialSource::USB:
- return SERIAL_MAIN_DEVICE.available() > 0;
-
- case SerialSource::AUX:
- return SERIAL_AUX_DEVICE.available() > 0;
-
- case SerialSource::AUX2:
-#ifdef SERIAL_AUX2_DEVICE
- return SERIAL_AUX2_DEVICE.available() > 0;
-#else
- return false;
-#endif
- }
-
- return false;
-}
-
-char Platform::ReadFromSource(const SerialSource source)
-{
- switch (source)
- {
- case SerialSource::USB:
- return static_cast<char>(SERIAL_MAIN_DEVICE.read());
-
- case SerialSource::AUX:
- return static_cast<char>(SERIAL_AUX_DEVICE.read());
-
- case SerialSource::AUX2:
-#ifdef SERIAL_AUX2_DEVICE
- return static_cast<char>(SERIAL_AUX2_DEVICE.read());
-#else
- return 0;
-#endif
- }
-
- return 0;
-}
-
#ifndef __RADDS__
// CPU temperature
void Platform::GetMcuTemperatures(float& minT, float& currT, float& maxT) const
@@ -3148,8 +3102,9 @@ void Platform::Tick()
switch (tickState)
{
- case 1: // last conversion started was a thermistor
+ case 1:
case 3:
+ // We process a thermistor reading on alternate ticks
if (IsThermistorChannel(currentHeater))
{
// Because we are in the tick ISR and no other ISR reads the averaging filter, we can cast away 'volatile' here
@@ -3170,10 +3125,18 @@ void Platform::Tick()
{
currentHeater = 0;
}
+
+ // If we are not using a simple modulated IR sensor, process the Z probe reading on every tick for a faster response.
+ // If we are using a simple modulated IR sensor then we need to allow the reading to settle after turning the IR emitter on or off,
+ // so on alternate ticks we read it and switch the emitter
+ if (zProbeType != 2)
+ {
+ const_cast<ZProbeAveragingFilter&>((tickState == 1) ? zProbeOnFilter : zProbeOffFilter).ProcessReading(GetRawZProbeReading());
+ }
++tickState;
break;
- case 2: // last conversion started was the Z probe, with IR LED on
+ case 2:
const_cast<ZProbeAveragingFilter&>(zProbeOnFilter).ProcessReading(GetRawZProbeReading());
if (zProbeType == 2) // if using a modulated IR sensor
{
diff --git a/src/Platform.h b/src/Platform.h
index a54c3b71..de7a4363 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -286,14 +286,6 @@ enum class ErrorCode : uint32_t
OutputStackOverflow = 1 << 3
};
-// Different types of hardware-related input-output
-enum class SerialSource
-{
- USB,
- AUX,
- AUX2
-};
-
struct AxisDriversConfig
{
size_t numDrivers; // Number of drivers assigned to each axis
@@ -353,8 +345,6 @@ public:
// Communications and data storage
- bool GCodeAvailable(const SerialSource source) const;
- char ReadFromSource(const SerialSource source);
OutputBuffer *GetAuxGCodeReply(); // Returns cached G-Code reply for AUX devices and clears its reference
void AppendAuxReply(OutputBuffer *buf);
void AppendAuxReply(const char *msg);
@@ -429,6 +419,14 @@ public:
float Acceleration(size_t drive) const;
const float* Accelerations() const;
void SetAcceleration(size_t drive, float value);
+ float GetMaxPrintingAcceleration() const
+ { return maxPrintingAcceleration; }
+ void SetMaxPrintingAcceleration(float acc)
+ { maxPrintingAcceleration = acc; }
+ float GetMaxTravelAcceleration() const
+ { return maxTravelAcceleration; }
+ void SetMaxTravelAcceleration(float acc)
+ { maxTravelAcceleration = acc; }
float MaxFeedrate(size_t drive) const;
const float* MaxFeedrates() const;
void SetMaxFeedrate(size_t drive, float value);
@@ -555,7 +553,7 @@ public:
// AUX device
void Beep(int freq, int ms);
- void SendMessage(const char* msg);
+ void SendAuxMessage(const char* msg);
// Hotend configuration
float GetFilamentWidth() const;
@@ -691,6 +689,8 @@ private:
Pin endStopPins[DRIVES];
float maxFeedrates[DRIVES];
float accelerations[DRIVES];
+ float maxPrintingAcceleration;
+ float maxTravelAcceleration;
float driveStepsPerUnit[DRIVES];
float instantDvs[DRIVES];
float pressureAdvance[MaxExtruders];
diff --git a/src/RADDS/Webserver.h b/src/RADDS/Webserver.h
index de65d6cf..9f81c9f3 100644
--- a/src/RADDS/Webserver.h
+++ b/src/RADDS/Webserver.h
@@ -20,10 +20,7 @@ public:
void Exit() const { };
void Diagnostics(MessageType mtype) const { };
- bool GCodeAvailable(const WebSource source) const { return false; }
- char ReadGCode(const WebSource source) const { return '\0'; }
uint32_t GetReplySeq() const { return (uint32_t)0; }
- uint16_t GetGCodeBufferSpace(const WebSource source) const { return 0; }
void HandleGCodeReply(const WebSource source, OutputBuffer *reply) const;
void HandleGCodeReply(const WebSource source, const char *reply) const { };
diff --git a/src/RepRap.cpp b/src/RepRap.cpp
index 004b620c..f613978c 100644
--- a/src/RepRap.cpp
+++ b/src/RepRap.cpp
@@ -579,7 +579,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
ch = '[';
for (size_t axis = 0; axis < numAxes; axis++)
{
- response->catf("%c%.2f", ch, liveCoordinates[axis]);
+ response->catf("%c%.3f", ch, liveCoordinates[axis]);
ch = ',';
}
}
@@ -1155,7 +1155,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
ch = '[';
for (size_t drive = 0; drive < numAxes; drive++)
{
- response->catf("%c%.2f", ch, liveCoordinates[drive]);
+ response->catf("%c%.3f", ch, liveCoordinates[drive]);
ch = ',';
}
@@ -1453,7 +1453,7 @@ void RepRap::SetMessage(const char *msg)
if (platform->HaveAux())
{
- platform->SendMessage(msg);
+ platform->SendAuxMessage(msg);
}
}
diff --git a/src/Storage/FileStore.cpp b/src/Storage/FileStore.cpp
index df435e9e..d650da2d 100644
--- a/src/Storage/FileStore.cpp
+++ b/src/Storage/FileStore.cpp
@@ -14,10 +14,8 @@ FileStore::FileStore(Platform* p) : platform(p)
void FileStore::Init()
{
- bufferPointer = 0;
inUse = false;
writing = false;
- lastBufferEntry = 0;
openCount = 0;
closeRequested = false;
}
@@ -46,7 +44,6 @@ bool FileStore::Open(const char* directory, const char* fileName, bool write)
? platform->GetMassStorage()->CombineName(directory, fileName)
: fileName;
writing = write;
- lastBufferEntry = FileBufLen;
// Try to create the path of this file if we want to write to it
if (writing)
@@ -90,7 +87,6 @@ bool FileStore::Open(const char* directory, const char* fileName, bool write)
return false;
}
- bufferPointer = (writing) ? 0 : FileBufLen;
inUse = true;
openCount = 1;
return true;
@@ -156,7 +152,6 @@ bool FileStore::Close()
FRESULT fr = f_close(&file);
inUse = false;
writing = false;
- lastBufferEntry = 0;
closeRequested = false;
return ok && fr == FR_OK;
}
@@ -168,27 +163,13 @@ bool FileStore::Seek(FilePosition pos)
platform->Message(GENERIC_MESSAGE, "Error: Attempt to seek on a non-open file.\n");
return false;
}
- if (writing)
- {
- WriteBuffer();
- }
FRESULT fr = f_lseek(&file, pos);
- bufferPointer = (writing) ? 0 : FileBufLen;
return fr == FR_OK;
}
FilePosition FileStore::Position() const
{
- FilePosition pos = file.fptr;
- if (writing)
- {
- pos += bufferPointer;
- }
- else if (bufferPointer < lastBufferEntry)
- {
- pos -= (lastBufferEntry - bufferPointer);
- }
- return pos;
+ return file.fptr;
}
#if 0 // not currently used
@@ -219,63 +200,12 @@ float FileStore::FractionRead() const
return (float)Position() / (float)len;
}
-uint8_t FileStore::Status()
-{
- if (!inUse)
- return (uint8_t)IOStatus::nothing;
-
- if (lastBufferEntry == FileBufLen)
- return (uint8_t)IOStatus::byteAvailable;
-
- if (bufferPointer < lastBufferEntry)
- return (uint8_t)IOStatus::byteAvailable;
-
- return (uint8_t)IOStatus::nothing;
-}
-
-bool FileStore::ReadBuffer()
-{
- FRESULT readStatus = f_read(&file, GetBuffer(), FileBufLen, &lastBufferEntry); // Read a chunk of file
- if (readStatus != FR_OK)
- {
- platform->Message(GENERIC_MESSAGE, "Error: Cannot read file.\n");
- return false;
- }
- bufferPointer = 0;
- return true;
-}
-
-// Single character read via the buffer
+// Single character read
bool FileStore::Read(char& b)
{
- if (!inUse)
- {
- platform->Message(GENERIC_MESSAGE, "Error: Attempt to read from a non-open file.\n");
- return false;
- }
-
- if (bufferPointer >= FileBufLen)
- {
- bool ok = ReadBuffer();
- if (!ok)
- {
- return false;
- }
- }
-
- if (bufferPointer >= lastBufferEntry)
- {
- b = 0; // Good idea?
- return false;
- }
-
- b = (char) GetBuffer()[bufferPointer];
- bufferPointer++;
-
- return true;
+ return Read(&b, sizeof(char));
}
-// Block read, doesn't use the buffer
// Returns the number of bytes read or -1 if the read process failed
int FileStore::Read(char* extBuf, size_t nBytes)
{
@@ -285,7 +215,6 @@ int FileStore::Read(char* extBuf, size_t nBytes)
return -1;
}
- bufferPointer = FileBufLen; // invalidate the buffer
UINT bytes_read;
FRESULT readStatus = f_read(&file, extBuf, nBytes, &bytes_read);
if (readStatus != FR_OK)
@@ -330,33 +259,9 @@ int FileStore::ReadLine(char* buf, size_t nBytes)
return i;
}
-bool FileStore::WriteBuffer()
-{
- if (bufferPointer != 0)
- {
- if (!InternalWriteBlock((const char*)GetBuffer(), bufferPointer))
- {
- return false;
- }
- bufferPointer = 0;
- }
- return true;
-}
-
bool FileStore::Write(char b)
{
- if (!inUse)
- {
- platform->Message(GENERIC_MESSAGE, "Error: Attempt to write byte to a non-open file.\n");
- return false;
- }
- GetBuffer()[bufferPointer] = b;
- bufferPointer++;
- if (bufferPointer >= FileBufLen)
- {
- return WriteBuffer();
- }
- return true;
+ return Write(&b, sizeof(char));
}
bool FileStore::Write(const char* b)
@@ -364,7 +269,6 @@ bool FileStore::Write(const char* b)
return Write(b, strlen(b));
}
-// Direct block write that bypasses the buffer. Used when uploading files.
bool FileStore::Write(const char *s, size_t len)
{
if (!inUse)
@@ -373,15 +277,6 @@ bool FileStore::Write(const char *s, size_t len)
return false;
}
- if (!WriteBuffer())
- {
- return false;
- }
- return InternalWriteBlock(s, len);
-}
-
-bool FileStore::InternalWriteBlock(const char *s, size_t len)
-{
size_t bytesWritten;
uint32_t time = micros();
@@ -393,7 +288,7 @@ bool FileStore::InternalWriteBlock(const char *s, size_t len)
}
if ((writeStatus != FR_OK) || (bytesWritten != len))
{
- platform->Message(GENERIC_MESSAGE, "Error: Cannot write to file. Disc may be full.\n");
+ platform->Message(GENERIC_MESSAGE, "Error: Cannot write to file. Drive may be full.\n");
return false;
}
return true;
@@ -406,10 +301,6 @@ bool FileStore::Flush()
platform->Message(GENERIC_MESSAGE, "Error: Attempt to flush a non-open file.\n");
return false;
}
- if (!WriteBuffer())
- {
- return false;
- }
return f_sync(&file) == FR_OK;
}
diff --git a/src/Storage/FileStore.h b/src/Storage/FileStore.h
index 29ace01c..364bdc8f 100644
--- a/src/Storage/FileStore.h
+++ b/src/Storage/FileStore.h
@@ -8,22 +8,11 @@
const size_t FileBufLen = 256; // 512 would be more efficient, but need to free up some RAM first
-enum class IOStatus : uint8_t
-{
- nothing = 0,
- byteAvailable = 1,
- atEoF = 2,
- clientLive = 4,
- clientConnected = 8
-};
-
class Platform;
class FileStore
{
public:
-
- uint8_t Status(); // Returns OR of IOStatus
bool Read(char& b); // Read 1 byte
int Read(char* buf, size_t nBytes); // Read a block of nBytes length
int ReadLine(char* buf, size_t nBytes); // As Read but stop after '\n' or '\r\n' and null-terminate
@@ -57,17 +46,9 @@ protected:
bool Open(const char* directory, const char* fileName, bool write);
private:
- bool ReadBuffer();
- bool WriteBuffer();
- bool InternalWriteBlock(const char *s, size_t len);
- uint8_t *GetBuffer() { return reinterpret_cast<uint8_t*>(buf32); }
-
- uint32_t buf32[FileBufLen/4];
Platform* platform;
- unsigned int bufferPointer;
FIL file;
- unsigned int lastBufferEntry;
volatile unsigned int openCount;
volatile bool closeRequested;
diff --git a/src/Storage/MassStorage.h b/src/Storage/MassStorage.h
index 2371e671..25ed0aee 100644
--- a/src/Storage/MassStorage.h
+++ b/src/Storage/MassStorage.h
@@ -3,7 +3,7 @@
#include "RepRapFirmware.h"
#include "Pins.h"
-#include "Libraries/FatFs/ff.h"
+#include "Libraries/Fatfs/ff.h"
#include <ctime>
// Info returned by FindFirst/FindNext calls
diff --git a/src/Version.h b/src/Version.h
index ce482ede..fb760e71 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -9,11 +9,11 @@
#define SRC_VERSION_H_
#ifndef VERSION
-# define VERSION "1.18beta3"
+# define VERSION "1.18RC1"
#endif
#ifndef DATE
-# define DATE "2017-03-16"
+# define DATE "2017-03-28"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"