diff options
author | David Crocker <dcrocker@eschertech.com> | 2017-03-28 20:55:22 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2017-03-28 21:35:35 +0300 |
commit | 66b7ee9ae2189e9ae09ce2c72327d570d551fe2a (patch) | |
tree | 5f5cd70f39b80004436b08f36566cc4f594982be /src/GCodes/GCodeInput.cpp | |
parent | c95223c75d2946f6008a46f92f0d678106c1550c (diff) |
Version 1.18RC1
Merged in chrishamm's code queue and input buffer code
Increased heater tuning timeout for finding peak temperatire from 60 to
120 seconds
Implemented M204
Increased precision of reported coordinates to 3 decimal places
PanelDue status responses continue to be sent while executing
M109/116/190/191 commands
Diffstat (limited to 'src/GCodes/GCodeInput.cpp')
-rw-r--r-- | src/GCodes/GCodeInput.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
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; +} + |