
Improved speed of integer square root function Fixed bug in GCodeBuffer IsEmpty function (thanks zpl) Changed tool numbers to start at T0 in all /sys files Added sample macro files
353 lines
7.8 KiB
C++
353 lines
7.8 KiB
C++
/*
|
|
* GCodeBuffer.cpp
|
|
*
|
|
* Created on: 6 Feb 2015
|
|
* Author: David
|
|
*/
|
|
|
|
//*************************************************************************************
|
|
|
|
#include "RepRapFirmware.h"
|
|
|
|
// This class stores a single G Code and provides functions to allow it to be parsed
|
|
|
|
GCodeBuffer::GCodeBuffer(Platform* p, const char* id)
|
|
{
|
|
platform = p;
|
|
identity = id;
|
|
writingFileDirectory = NULL; // Has to be done here as Init() is called every line.
|
|
toolNumberAdjust = 0;
|
|
checksumRequired = false;
|
|
}
|
|
|
|
void GCodeBuffer::Init()
|
|
{
|
|
gcodePointer = 0;
|
|
readPointer = -1;
|
|
inComment = false;
|
|
state = idle;
|
|
}
|
|
|
|
int GCodeBuffer::CheckSum() const
|
|
{
|
|
int cs = 0;
|
|
for (int i = 0; gcodeBuffer[i] != '*' && gcodeBuffer[i] != 0; i++)
|
|
{
|
|
cs = cs ^ gcodeBuffer[i];
|
|
}
|
|
cs &= 0xff; // Defensive programming...
|
|
return cs;
|
|
}
|
|
|
|
// Add a byte to the code being assembled. If false is returned, the code is
|
|
// not yet complete. If true, it is complete and ready to be acted upon.
|
|
bool GCodeBuffer::Put(char c)
|
|
{
|
|
if (c == '\r')
|
|
{
|
|
// Ignore carriage return, it messes up filenames sometimes if it appears in macro files etc.
|
|
// Alternatively, we could handle it in the same way as linefeed, and add an optimisation to ignore blank lines.
|
|
return false;
|
|
}
|
|
|
|
if (c == ';')
|
|
{
|
|
inComment = true;
|
|
}
|
|
else if (c == '\n' || !c)
|
|
{
|
|
gcodeBuffer[gcodePointer] = 0;
|
|
if (reprap.Debug(moduleGcodes) && gcodeBuffer[0] && !writingFileDirectory) // Don't bother with blank/comment lines
|
|
{
|
|
platform->Message(HOST_MESSAGE, "%s%s\n", identity, gcodeBuffer);
|
|
}
|
|
|
|
// Deal with line numbers and checksums
|
|
if (Seen('*'))
|
|
{
|
|
int csSent = GetIValue();
|
|
int csHere = CheckSum();
|
|
Seen('N');
|
|
if (csSent != csHere)
|
|
{
|
|
snprintf(gcodeBuffer, GcodeLength, "M998 P%d", GetIValue());
|
|
Init();
|
|
return true;
|
|
}
|
|
|
|
// Strip out the line number and checksum
|
|
gcodePointer = 0;
|
|
while (gcodeBuffer[gcodePointer] != ' ' && gcodeBuffer[gcodePointer])
|
|
{
|
|
gcodePointer++;
|
|
}
|
|
|
|
// Anything there?
|
|
if (!gcodeBuffer[gcodePointer])
|
|
{
|
|
// No...
|
|
gcodeBuffer[0] = 0;
|
|
Init();
|
|
return false;
|
|
}
|
|
|
|
// Yes...
|
|
gcodePointer++;
|
|
int gp2 = 0;
|
|
while (gcodeBuffer[gcodePointer] != '*' && gcodeBuffer[gcodePointer])
|
|
{
|
|
gcodeBuffer[gp2] = gcodeBuffer[gcodePointer++];
|
|
gp2++;
|
|
}
|
|
gcodeBuffer[gp2] = 0;
|
|
}
|
|
else if (checksumRequired)
|
|
{
|
|
gcodeBuffer[0] = 0;
|
|
Init();
|
|
return false;
|
|
}
|
|
Init();
|
|
state = executing;
|
|
return true;
|
|
}
|
|
else if (!inComment || writingFileDirectory)
|
|
{
|
|
gcodeBuffer[gcodePointer++] = c;
|
|
if (gcodePointer >= GcodeLength)
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "G-Code buffer length overflow.\n");
|
|
gcodePointer = 0;
|
|
gcodeBuffer[0] = 0;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool GCodeBuffer::Put(const char *str, size_t len)
|
|
{
|
|
for(size_t i=0; i<=len; i++)
|
|
{
|
|
if (Put(str[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Does this buffer contain any code?
|
|
|
|
bool GCodeBuffer::IsEmpty() const
|
|
{
|
|
const char *buf = gcodeBuffer;
|
|
while (*buf != 0 && strchr(" \t\n\r", *buf) != nullptr)
|
|
{
|
|
buf++;
|
|
}
|
|
return *buf == 0;
|
|
}
|
|
|
|
// Is 'c' in the G Code string?
|
|
// Leave the pointer there for a subsequent read.
|
|
|
|
bool GCodeBuffer::Seen(char c)
|
|
{
|
|
readPointer = 0;
|
|
for (;;)
|
|
{
|
|
char b = gcodeBuffer[readPointer];
|
|
if (b == 0 || b == ';') break;
|
|
if (b == c) return true;
|
|
++readPointer;
|
|
}
|
|
readPointer = -1;
|
|
return false;
|
|
}
|
|
|
|
// Get a float after a G Code letter found by a call to Seen()
|
|
|
|
float GCodeBuffer::GetFValue()
|
|
{
|
|
if (readPointer < 0)
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode float before a search.\n");
|
|
readPointer = -1;
|
|
return 0.0;
|
|
}
|
|
float result = (float) strtod(&gcodeBuffer[readPointer + 1], 0);
|
|
readPointer = -1;
|
|
return result;
|
|
}
|
|
|
|
// Get a :-separated list of floats after a key letter
|
|
|
|
const void GCodeBuffer::GetFloatArray(float a[], int& returnedLength)
|
|
{
|
|
int length = 0;
|
|
if(readPointer < 0)
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode float array before a search.\n");
|
|
readPointer = -1;
|
|
returnedLength = 0;
|
|
return;
|
|
}
|
|
|
|
bool inList = true;
|
|
while(inList)
|
|
{
|
|
if(length >= returnedLength) // Array limit has been set in here
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode float array that is too long: %s\n", gcodeBuffer);
|
|
readPointer = -1;
|
|
returnedLength = 0;
|
|
return;
|
|
}
|
|
a[length] = (float)strtod(&gcodeBuffer[readPointer + 1], 0);
|
|
length++;
|
|
readPointer++;
|
|
while(gcodeBuffer[readPointer] && (gcodeBuffer[readPointer] != ' ') && (gcodeBuffer[readPointer] != LIST_SEPARATOR))
|
|
{
|
|
readPointer++;
|
|
}
|
|
if(gcodeBuffer[readPointer] != LIST_SEPARATOR)
|
|
{
|
|
inList = false;
|
|
}
|
|
}
|
|
|
|
// Special case if there is one entry and returnedLength requests several.
|
|
// Fill the array with the first entry.
|
|
|
|
if(length == 1 && returnedLength > 1)
|
|
{
|
|
for(int i = 1; i < returnedLength; i++)
|
|
{
|
|
a[i] = a[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
returnedLength = length;
|
|
}
|
|
|
|
readPointer = -1;
|
|
}
|
|
|
|
// Get a :-separated list of longs after a key letter
|
|
|
|
const void GCodeBuffer::GetLongArray(long l[], int& returnedLength)
|
|
{
|
|
if(readPointer < 0)
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode long array before a search.\n");
|
|
readPointer = -1;
|
|
return;
|
|
}
|
|
|
|
int length = 0;
|
|
bool inList = true;
|
|
while(inList)
|
|
{
|
|
if(length >= returnedLength) // Array limit has been set in here
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode long array that is too long: %s\n", gcodeBuffer);
|
|
readPointer = -1;
|
|
returnedLength = 0;
|
|
return;
|
|
}
|
|
l[length] = strtol(&gcodeBuffer[readPointer + 1], 0, 0);
|
|
length++;
|
|
readPointer++;
|
|
while(gcodeBuffer[readPointer] && (gcodeBuffer[readPointer] != ' ') && (gcodeBuffer[readPointer] != LIST_SEPARATOR))
|
|
{
|
|
readPointer++;
|
|
}
|
|
if(gcodeBuffer[readPointer] != LIST_SEPARATOR)
|
|
{
|
|
inList = false;
|
|
}
|
|
}
|
|
returnedLength = length;
|
|
readPointer = -1;
|
|
}
|
|
|
|
// Get a string after a G Code letter found by a call to Seen().
|
|
// It will be the whole of the rest of the GCode string, so strings
|
|
// should always be the last parameter.
|
|
|
|
const char* GCodeBuffer::GetString()
|
|
{
|
|
if (readPointer < 0)
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode string before a search.\n");
|
|
readPointer = -1;
|
|
return "";
|
|
}
|
|
const char* result = &gcodeBuffer[readPointer + 1];
|
|
readPointer = -1;
|
|
return result;
|
|
}
|
|
|
|
// This returns a pointer to the end of the buffer where a
|
|
// string starts. It assumes that an M or G search has
|
|
// been done followed by a GetIValue(), so readPointer will
|
|
// be -1. It absorbs "M/Gnnn " (including the space) from the
|
|
// start and returns a pointer to the next location.
|
|
|
|
// This is provided for legacy use, in particular in the M23
|
|
// command that sets the name of a file to be printed. In
|
|
// preference use GetString() which requires the string to have
|
|
// been preceded by a tag letter.
|
|
|
|
const char* GCodeBuffer::GetUnprecedentedString(bool optional)
|
|
{
|
|
readPointer = 0;
|
|
while (gcodeBuffer[readPointer] && gcodeBuffer[readPointer] != ' ')
|
|
{
|
|
readPointer++;
|
|
}
|
|
|
|
if (!gcodeBuffer[readPointer])
|
|
{
|
|
readPointer = -1;
|
|
if (optional)
|
|
{
|
|
return NULL;
|
|
}
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: String expected but not seen.\n");
|
|
return gcodeBuffer; // Good idea?
|
|
}
|
|
|
|
const char* result = &gcodeBuffer[readPointer + 1];
|
|
readPointer = -1;
|
|
return result;
|
|
}
|
|
|
|
// Get an long after a G Code letter
|
|
|
|
long GCodeBuffer::GetLValue()
|
|
{
|
|
if (readPointer < 0)
|
|
{
|
|
platform->Message(BOTH_ERROR_MESSAGE, "GCodes: Attempt to read a GCode int before a search.\n");
|
|
readPointer = -1;
|
|
return 0;
|
|
}
|
|
long result = strtol(&gcodeBuffer[readPointer + 1], 0, 0);
|
|
readPointer = -1;
|
|
return result;
|
|
}
|
|
|
|
// Return true if this buffer contains a poll request or empty request that can be executed while macros etc. from another source are being completed
|
|
bool GCodeBuffer::IsPollRequest()
|
|
{
|
|
return state == executing && (IsEmpty() || (Seen('M') && GetIValue() == 105));
|
|
}
|
|
|
|
// End
|
|
|
|
|
|
|