Various bug fixes and enhancements (version 0.59a)

Fixed bug whereby incoming gcodes were URI-decoded twice, which could
cause problems if a comment contain a % character
Fixed bug whereby M109 and M190 waited for all heaters to reach target
temperature instead of just the one involved
Fixed bug whereby files whose names contain an uppercase letter G could
not be uploaded or deleted
Increase buffer size for incoming gcodes
Removed parameter to set max PWM value in M106 command. Max PWM value is
now 255 as per recent RepRapPro change.
Changed the code that deals with dropping characters in unimportant
messages sent to USB such that we only drop characters at the end of a
line (adding ".\n" to whatever we send) or drop whole lines
This commit is contained in:
David Crocker 2014-05-14 22:04:14 +01:00
parent b7701832bc
commit a416c367d2
11 changed files with 791 additions and 723 deletions

View file

@ -24,8 +24,8 @@ Licence: GPL
#define CONFIGURATION_H #define CONFIGURATION_H
#define NAME "RepRapFirmware" #define NAME "RepRapFirmware"
#define VERSION "0.59-dc42" #define VERSION "0.59a-dc42"
#define DATE "2014-05-08" #define DATE "2014-05-14"
#define LAST_AUTHOR "dc42" #define LAST_AUTHOR "dc42"
// Other firmware that we might switch to be compatible with. // Other firmware that we might switch to be compatible with.

1140
GCodes.cpp

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,6 @@ Licence: GPL
#define GCODE_LETTERS { 'X', 'Y', 'Z', 'E', 'F' } // The drives and feedrate in a GCode #define GCODE_LETTERS { 'X', 'Y', 'Z', 'E', 'F' } // The drives and feedrate in a GCode
// Small class to hold an individual GCode and provide functions to allow it to be parsed // Small class to hold an individual GCode and provide functions to allow it to be parsed
class GCodeBuffer class GCodeBuffer
@ -158,6 +157,9 @@ class GCodes
bool DoFileCannedCycles(const char* fileName); bool DoFileCannedCycles(const char* fileName);
bool FileCannedCyclesReturn(); bool FileCannedCyclesReturn();
bool ActOnGcode(GCodeBuffer* gb); bool ActOnGcode(GCodeBuffer* gb);
bool HandleGcode(GCodeBuffer* gb);
bool HandleMcode(GCodeBuffer* gb);
bool HandleTcode(GCodeBuffer* gb);
int SetUpMove(GCodeBuffer* gb); int SetUpMove(GCodeBuffer* gb);
bool DoDwell(GCodeBuffer *gb); bool DoDwell(GCodeBuffer *gb);
bool DoDwellTime(float dwell); bool DoDwellTime(float dwell);
@ -232,9 +234,10 @@ class GCodes
float longWait; float longWait;
bool limitAxes; // Don't think outside the box. bool limitAxes; // Don't think outside the box.
bool axisIsHomed[3]; // these record which of the axes have been homed bool axisIsHomed[3]; // these record which of the axes have been homed
float fanMaxPwm; // the M106 S value that represents 100% fan speed
bool waitingForMoveToComplete; bool waitingForMoveToComplete;
bool coolingInverted; bool coolingInverted;
float speedFactor; // speed factor, including the conversion from mm/min to mm/sec, normally 1/60
float extrusionFactor; // extrusion factor, normally 1.0
}; };
//***************************************************************************************************** //*****************************************************************************************************

View file

@ -63,31 +63,24 @@ void Heat::Diagnostics()
platform->Message(HOST_MESSAGE, "Heat Diagnostics:\n"); platform->Message(HOST_MESSAGE, "Heat Diagnostics:\n");
} }
bool Heat::AllHeatersAtSetTemperatures() bool Heat::AllHeatersAtSetTemperatures() const
{ {
float dt;
for(int8_t heater = 0; heater < HEATERS; heater++) for(int8_t heater = 0; heater < HEATERS; heater++)
{ {
dt = GetTemperature(heater); if (!HeaterAtSetTemperature(heater))
if(pids[heater]->Active())
{
if(GetActiveTemperature(heater) < TEMPERATURE_LOW_SO_DONT_CARE)
dt = 0.0;
else
dt = fabs(dt - GetActiveTemperature(heater));
} else
{
if(GetStandbyTemperature(heater) < TEMPERATURE_LOW_SO_DONT_CARE)
dt = 0.0;
else
dt = fabs(dt - GetStandbyTemperature(heater));
}
if(dt > TEMPERATURE_CLOSE_ENOUGH)
return false; return false;
} }
return true; return true;
} }
//query an individual heater
bool Heat::HeaterAtSetTemperature(int8_t heater) const
{
float dt = GetTemperature(heater);
float target = (pids[heater]->Active()) ? GetActiveTemperature(heater) : GetStandbyTemperature(heater);
return (target < TEMPERATURE_LOW_SO_DONT_CARE) || (fabs(dt - target) <= TEMPERATURE_CLOSE_ENOUGH);
}
//****************************************************************************************************** //******************************************************************************************************
PID::PID(Platform* p, int8_t h) PID::PID(Platform* p, int8_t h)

47
Heat.h
View file

@ -28,15 +28,15 @@ class PID
PID(Platform* p, int8_t h); PID(Platform* p, int8_t h);
void Init(); void Init();
void Spin(); void Spin();
void SetActiveTemperature(const float& t); void SetActiveTemperature(float t);
float GetActiveTemperature(); float GetActiveTemperature() const;
void SetStandbyTemperature(const float& t); void SetStandbyTemperature(float t);
float GetStandbyTemperature(); float GetStandbyTemperature() const;
void Activate(); void Activate();
void Standby(); void Standby();
bool Active(); bool Active() const;
void ResetFault(); void ResetFault();
float GetTemperature(); float GetTemperature() const;
private: private:
@ -61,15 +61,16 @@ class Heat
void Spin(); void Spin();
void Init(); void Init();
void Exit(); void Exit();
void SetActiveTemperature(int8_t heater, const float& t); void SetActiveTemperature(int8_t heater, float t);
float GetActiveTemperature(int8_t heater); float GetActiveTemperature(int8_t heater) const;
void SetStandbyTemperature(int8_t heater, const float& t); void SetStandbyTemperature(int8_t heater, float t);
float GetStandbyTemperature(int8_t heater); float GetStandbyTemperature(int8_t heater) const;
void Activate(int8_t heater); void Activate(int8_t heater);
void Standby(int8_t heater); void Standby(int8_t heater);
float GetTemperature(int8_t heater); float GetTemperature(int8_t heater) const;
void ResetFault(int8_t heater); void ResetFault(int8_t heater);
bool AllHeatersAtSetTemperatures(); bool AllHeatersAtSetTemperatures() const;
bool HeaterAtSetTemperature(int8_t heater) const; // Is a specific heater at temperature within tolerance?
void Diagnostics(); void Diagnostics();
private: private:
@ -85,32 +86,32 @@ class Heat
//*********************************************************************************************************** //***********************************************************************************************************
inline bool PID::Active() inline bool PID::Active() const
{ {
return active; return active;
} }
inline void PID::SetActiveTemperature(const float& t) inline void PID::SetActiveTemperature(float t)
{ {
activeTemperature = t; activeTemperature = t;
} }
inline float PID::GetActiveTemperature() inline float PID::GetActiveTemperature() const
{ {
return activeTemperature; return activeTemperature;
} }
inline void PID::SetStandbyTemperature(const float& t) inline void PID::SetStandbyTemperature(float t)
{ {
standbyTemperature = t; standbyTemperature = t;
} }
inline float PID::GetStandbyTemperature() inline float PID::GetStandbyTemperature() const
{ {
return standbyTemperature; return standbyTemperature;
} }
inline float PID::GetTemperature() inline float PID::GetTemperature() const
{ {
return temperature; return temperature;
} }
@ -132,7 +133,7 @@ inline void PID::ResetFault()
} }
inline void Heat::SetActiveTemperature(int8_t heater, const float& t) inline void Heat::SetActiveTemperature(int8_t heater, float t)
{ {
if (heater >= 0 && heater < HEATERS) if (heater >= 0 && heater < HEATERS)
{ {
@ -140,12 +141,12 @@ inline void Heat::SetActiveTemperature(int8_t heater, const float& t)
} }
} }
inline float Heat::GetActiveTemperature(int8_t heater) inline float Heat::GetActiveTemperature(int8_t heater) const
{ {
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetActiveTemperature() : ABS_ZERO; return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetActiveTemperature() : ABS_ZERO;
} }
inline void Heat::SetStandbyTemperature(int8_t heater, const float& t) inline void Heat::SetStandbyTemperature(int8_t heater, float t)
{ {
if (heater >= 0 && heater < HEATERS) if (heater >= 0 && heater < HEATERS)
{ {
@ -153,12 +154,12 @@ inline void Heat::SetStandbyTemperature(int8_t heater, const float& t)
} }
} }
inline float Heat::GetStandbyTemperature(int8_t heater) inline float Heat::GetStandbyTemperature(int8_t heater) const
{ {
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO; return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
} }
inline float Heat::GetTemperature(int8_t heater) inline float Heat::GetTemperature(int8_t heater) const
{ {
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO; return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
} }

View file

@ -988,12 +988,14 @@ void Platform::SetMotorCurrent(byte drive, float current)
mcp.setVolatileWiper(potWipes[drive], pot); mcp.setVolatileWiper(potWipes[drive], pot);
} }
//Changed to be compatible with existing gcode norms
// M106 S0 = fully off M106 S255 = fully on
void Platform::CoolingFan(float speed) void Platform::CoolingFan(float speed)
{ {
if(coolingFanPin > 0) if(coolingFanPin > 0)
{ {
// The cooling fan output pin gets inverted if HEAT_ON == 0 // The cooling fan output pin gets inverted if HEAT_ON == 0
analogWriteNonDue(coolingFanPin, (uint32_t)( ((HEAT_ON == 0) ? (1.0 - speed) : speed) * 255.0)); analogWriteNonDue(coolingFanPin, (uint32_t)( (HEAT_ON == 0) ? (255.0 - speed) : speed));
} }
} }
@ -1014,25 +1016,6 @@ void Platform::SetInterrupt(float s) // Seconds
NVIC_EnableIRQ(TC3_IRQn); NVIC_EnableIRQ(TC3_IRQn);
} }
int8_t Line::Status() const
{
// if(alternateInput != NULL)
// return alternateInput->Status();
return inputNumChars == 0 ? nothing : byteAvailable;
}
int Line::Read(char& b)
{
// if(alternateInput != NULL)
// return alternateInput->Read(b);
if (inputNumChars == 0) return 0;
b = inBuffer[inputGetIndex];
inputGetIndex = (inputGetIndex + 1) % lineInBufsize;
--inputNumChars;
return 1;
}
//----------------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------------
FileStore* Platform::GetFileStore(const char* directory, const char* fileName, bool write) FileStore* Platform::GetFileStore(const char* directory, const char* fileName, bool write)
@ -1550,12 +1533,32 @@ Line::Line()
{ {
} }
int8_t Line::Status() const
{
// if(alternateInput != NULL)
// return alternateInput->Status();
return inputNumChars == 0 ? nothing : byteAvailable;
}
int Line::Read(char& b)
{
// if(alternateInput != NULL)
// return alternateInput->Read(b);
if (inputNumChars == 0) return 0;
b = inBuffer[inputGetIndex];
inputGetIndex = (inputGetIndex + 1) % lineInBufsize;
--inputNumChars;
return 1;
}
void Line::Init() void Line::Init()
{ {
inputGetIndex = 0; inputGetIndex = 0;
inputNumChars = 0; inputNumChars = 0;
outputGetIndex = 0; outputGetIndex = 0;
outputNumChars = 0; outputNumChars = 0;
ignoringOutputLine = false;
SerialUSB.begin(BAUD_RATE); SerialUSB.begin(BAUD_RATE);
inUsbWrite = false; inUsbWrite = false;
} }
@ -1583,25 +1586,69 @@ void Line::Spin()
TryFlushOutput(); TryFlushOutput();
} }
// Write a character to USB.
// If 'block' is true then we don't return until we have either written it to the USB port of put it in the buffer.
// Otherwise, if the buffer is full then we append ".\n" to the end of it, return immediately and ignore the rest
// of the data we are asked to print until we get a new line.
void Line::Write(char b, bool block) void Line::Write(char b, bool block)
{ {
do if (block)
{ {
TryFlushOutput(); // We failed to print an unimportant message that (unusually) didn't finish in a newline
if (outputNumChars == 0 && SerialUSB.canWrite() != 0) ignoringOutputLine = false;
}
if (ignoringOutputLine)
{
// We have already failed to write some characters of this message line, so don't write any of it.
// But try to start sending again after this line finishes.
if (b == '\n')
{ {
++inUsbWrite; ignoringOutputLine = false;
SerialUSB.write(b);
--inUsbWrite;
break;
} }
else if (outputNumChars < lineOutBufSize) TryFlushOutput(); // this may help free things up
}
else
{
for(;;)
{ {
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = b; TryFlushOutput();
++outputNumChars; if (outputNumChars == 0 && SerialUSB.canWrite() != 0)
break; {
// We can write the character directly into the USB output buffer
++inUsbWrite;
SerialUSB.write(b);
--inUsbWrite;
break;
}
else if ( outputNumChars + 2 < lineOutBufSize // save 2 spaces in the output buffer
|| (outputNumChars < lineOutBufSize && (block || b == '\n')) //...unless doing blocking output or writing newline
)
{
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = b;
++outputNumChars;
break;
}
else if (!block)
{
if (outputNumChars + 2 == lineOutBufSize)
{
// We still have our 2 free characters, so append ".\n" to the line to indicate it was incomplete
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = '.';
++outputNumChars;
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = '\n';
++outputNumChars;
}
else
{
// As we don't have 2 spare characters in the buffer, we can't have written any of the current line.
// So ignore the whole line.
}
ignoringOutputLine = true;
break;
}
} }
} while (block); }
// else discard the character // else discard the character
} }

View file

@ -99,7 +99,7 @@ const unsigned int numZProbeReadingsAveraged = 8; // we average this number of r
#define MAX_FEEDRATES {50.0, 50.0, 3.0, 16.0} // mm/sec #define MAX_FEEDRATES {50.0, 50.0, 3.0, 16.0} // mm/sec
#define ACCELERATIONS {800.0, 800.0, 10.0, 250.0} // mm/sec^2 #define ACCELERATIONS {800.0, 800.0, 10.0, 250.0} // mm/sec^2
#define DRIVE_STEPS_PER_UNIT {87.4890, 87.4890, 4000.0, 420.0} #define DRIVE_STEPS_PER_UNIT {87.4890, 87.4890, 4000.0, 420.0}
#define INSTANT_DVS {10.0, 10.0, 0.2, 2.0} // (mm/sec) #define INSTANT_DVS {10.0, 10.0, 0.2, 2.0} // (mm/sec) these are also the minimum feed rates which is why I (dc42) decreased X/Y from 15 to 10
// AXES // AXES
@ -195,10 +195,8 @@ const unsigned int adDisconnectedVirtual = adDisconnectedReal << adOversampleBit
#define FILE_LIST_BRACKET '"' #define FILE_LIST_BRACKET '"'
#define FILE_LIST_LENGTH (1000) // Maximum length of file list - can't make it much longer unless we also make jsonResponse longer #define FILE_LIST_LENGTH (1000) // Maximum length of file list - can't make it much longer unless we also make jsonResponse longer
#define FLASH_LED 'F' // Type byte of a message that is to flash an LED; the next two bytes define #define FLASH_LED 'F' // Type byte of a message that is to flash an LED; the next two bytes define the frequency and M/S ratio.
// the frequency and M/S ratio. #define DISPLAY_MESSAGE 'L' // Type byte of a message that is to appear on a local display; the L is not displayed; \f and \n should be supported.
#define DISPLAY_MESSAGE 'L' // Type byte of a message that is to appear on a local display; the L is
// not displayed; \f and \n should be supported.
#define HOST_MESSAGE 'H' // Type byte of a message that is to be sent to the host; the H is not sent. #define HOST_MESSAGE 'H' // Type byte of a message that is to be sent to the host; the H is not sent.
#define DEBUG_MESSAGE 'D' // Type byte of a message that is to be sent for debugging; the D is not sent. #define DEBUG_MESSAGE 'D' // Type byte of a message that is to be sent for debugging; the D is not sent.
@ -295,6 +293,7 @@ private:
uint16_t outputNumChars; uint16_t outputNumChars;
uint8_t inUsbWrite; uint8_t inUsbWrite;
bool ignoringOutputLine;
}; };
class MassStorage class MassStorage

View file

@ -365,7 +365,7 @@ void debugPrintf(const char* fmt, ...)
va_start(p, fmt); va_start(p, fmt);
vsnprintf(scratchString, ARRAY_SIZE(scratchString), fmt, p); vsnprintf(scratchString, ARRAY_SIZE(scratchString), fmt, p);
va_end(p); va_end(p);
scratchString[ARRAY_SIZE(scratchString) - 1] = 0; scratchString[ARRAY_UPB(scratchString)] = 0;
reprap.GetPlatform()->Message(DEBUG_MESSAGE, scratchString); reprap.GetPlatform()->Message(DEBUG_MESSAGE, scratchString);
} }

View file

@ -55,7 +55,9 @@ bool StringEquals(const char* s1, const char* s2);
int StringContains(const char* string, const char* match); int StringContains(const char* string, const char* match);
// Macro to give us the number of elements in an array // Macro to give us the number of elements in an array
#define ARRAY_SIZE(_x) (sizeof(_x)/sizeof(_x[0])) #define ARRAY_SIZE(_x) (sizeof(_x)/sizeof(_x[0]))
// Macro to give us the highest valid index into an array i.e. one less than the size
#define ARRAY_UPB(_x) (ARRAY_SIZE(_x) - 1)
extern char scratchString[]; extern char scratchString[];

View file

@ -82,7 +82,7 @@ byte Webserver::ReadGCode()
} }
// Process a received string of gcodes // Process a received string of gcodes
void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb) void Webserver::LoadGcodeBuffer(const char* gc)
{ {
char gcodeTempBuf[GCODE_LENGTH]; char gcodeTempBuf[GCODE_LENGTH];
uint16_t gtp = 0; uint16_t gtp = 0;
@ -97,40 +97,6 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb)
return; return;
} }
if (c == '+' && convertWeb)
{
c = ' ';
}
else if (c == '%' && convertWeb)
{
c = *gc++;
if (c != 0)
{
unsigned char uc;
if (isalpha(c))
{
uc = 16 * (c - 'A' + 10);
}
else
{
uc = 16 * (c - '0');
}
c = *gc++;
if (c != 0)
{
if (isalpha(c))
{
uc += c - 'A' + 10;
}
else
{
uc += c - '0';
}
c = uc;
}
}
}
if (c == '\n') if (c == '\n')
{ {
gcodeTempBuf[gtp] = 0; gcodeTempBuf[gtp] = 0;
@ -145,7 +111,7 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb)
inComment = true; inComment = true;
} }
if (gtp == ARRAY_SIZE(gcodeTempBuf) - 1) if (gtp == ARRAY_UPB(gcodeTempBuf))
{ {
// gcode is too long, we haven't room for another character and a null // gcode is too long, we haven't room for another character and a null
if (c != ' ' && !inComment) if (c != ' ' && !inComment)
@ -219,7 +185,7 @@ void Webserver::ProcessGcode(const char* gc)
{ {
char c; char c;
size_t i = 0; size_t i = 0;
while (i < STRING_LENGTH && configFile->Read(c)) while (i < ARRAY_UPB(gcodeReply) && configFile->Read(c))
{ {
gcodeReply[i++] = c; gcodeReply[i++] = c;
} }
@ -360,7 +326,7 @@ void Webserver::JsonReport(bool ok, const char* request)
{ {
if (ok) if (ok)
{ {
jsonResponse[STRING_LENGTH] = 0; jsonResponse[ARRAY_UPB(jsonResponse)] = 0;
if (reprap.Debug()) if (reprap.Debug())
{ {
platform->Message(HOST_MESSAGE, "JSON response: "); platform->Message(HOST_MESSAGE, "JSON response: ");
@ -395,8 +361,8 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "gcode") && StringStartsWith(clientQualifier, "gcode=")) if (StringStartsWith(request, "gcode") && StringStartsWith(clientQualifier, "gcode="))
{ {
LoadGcodeBuffer(&clientQualifier[6], true); LoadGcodeBuffer(&clientQualifier[6]);
snprintf(jsonResponse, STRING_LENGTH, "{\"buff\":%u}", GetReportedGcodeBufferSpace()); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"buff\":%u}", GetReportedGcodeBufferSpace());
JsonReport(true, request); JsonReport(true, request);
return; return;
} }
@ -404,7 +370,7 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "files")) if (StringStartsWith(request, "files"))
{ {
const char* fileList = platform->GetMassStorage()->FileList(platform->GetGCodeDir(), false); const char* fileList = platform->GetMassStorage()->FileList(platform->GetGCodeDir(), false);
snprintf(jsonResponse, STRING_LENGTH, "{\"files\":[%s]}", fileList); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"files\":[%s]}", fileList);
JsonReport(true, request); JsonReport(true, request);
return; return;
} }
@ -416,11 +382,11 @@ void Webserver::GetJsonResponse(const char* request)
bool found = GetFileInfo(clientQualifier + 5, length, height, filament); bool found = GetFileInfo(clientQualifier + 5, length, height, filament);
if (found) if (found)
{ {
snprintf(jsonResponse, STRING_LENGTH, "{\"size\":%lu,\"height\":\"%.2f\",\"filament\":\"%.1f\"}", length, height, filament); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"size\":%lu,\"height\":\"%.2f\",\"filament\":\"%.1f\"}", length, height, filament);
} }
else else
{ {
snprintf(jsonResponse, STRING_LENGTH, "{}"); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{}");
} }
JsonReport(true, request); JsonReport(true, request);
return; return;
@ -428,7 +394,7 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "name")) if (StringStartsWith(request, "name"))
{ {
snprintf(jsonResponse, STRING_LENGTH, "{\"myName\":\"%s\"}", myName); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"myName\":\"%s\"}", myName);
JsonReport(true, request); JsonReport(true, request);
return; return;
} }
@ -436,21 +402,21 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "password")) if (StringStartsWith(request, "password"))
{ {
CheckPassword(); CheckPassword();
snprintf(jsonResponse, STRING_LENGTH, "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong"); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong");
JsonReport(true, request); JsonReport(true, request);
return; return;
} }
if (StringStartsWith(request, "axes")) if (StringStartsWith(request, "axes"))
{ {
strncpy(jsonResponse, "{\"axes\":", STRING_LENGTH); strncpy(jsonResponse, "{\"axes\":", ARRAY_UPB(jsonResponse));
char ch = '['; char ch = '[';
for (int8_t drive = 0; drive < AXES; drive++) for (int8_t drive = 0; drive < AXES; drive++)
{ {
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.1f\"", ch, platform->AxisLength(drive)); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.1f\"", ch, platform->AxisLength(drive));
ch = ','; ch = ',';
} }
strncat(jsonResponse, "]}", STRING_LENGTH); strncat(jsonResponse, "]}", ARRAY_UPB(jsonResponse));
JsonReport(true, request); JsonReport(true, request);
return; return;
} }
@ -467,36 +433,36 @@ void Webserver::GetStatusResponse(uint8_t type)
// New-style status request // New-style status request
// Send the printing/idle status // Send the printing/idle status
char ch = (reprap.IsStopped()) ? 'S' : (gc->PrintingAFile()) ? 'P' : 'I'; char ch = (reprap.IsStopped()) ? 'S' : (gc->PrintingAFile()) ? 'P' : 'I';
snprintf(jsonResponse, STRING_LENGTH, "{\"status\":\"%c\",\"heaters\":", ch); snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"status\":\"%c\",\"heaters\":", ch);
// Send the heater temperatures // Send the heater temperatures
ch = '['; ch = '[';
for (int8_t heater = 0; heater < HEATERS; heater++) for (int8_t heater = 0; heater < HEATERS; heater++)
{ {
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.1f\"", ch, reprap.GetHeat()->GetTemperature(heater)); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.1f\"", ch, reprap.GetHeat()->GetTemperature(heater));
ch = ','; ch = ',';
} }
// Send XYZ and extruder positions // Send XYZ and extruder positions
float liveCoordinates[DRIVES + 1]; float liveCoordinates[DRIVES + 1];
reprap.GetMove()->LiveCoordinates(liveCoordinates); reprap.GetMove()->LiveCoordinates(liveCoordinates);
strncat(jsonResponse, "],\"pos\":", STRING_LENGTH); // announce the XYZ position strncat(jsonResponse, "],\"pos\":", ARRAY_UPB(jsonResponse)); // announce the XYZ position
ch = '['; ch = '[';
// We currently provide the extruder 0 value here as well as XYZ. This is only expected by V0.69 and V0.70 of the web interface so it can be removed soon. // We currently provide the extruder 0 value here as well as XYZ. This is only expected by V0.69 and V0.70 of the web interface so it can be removed soon.
for (int8_t drive = 0; drive < AXES + 1; drive++) for (int8_t drive = 0; drive < AXES + 1; drive++)
//for (int8_t drive = 0; drive < AXES; drive++) //for (int8_t drive = 0; drive < AXES; drive++)
{ {
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.2f\"", ch, liveCoordinates[drive]); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.2f\"", ch, liveCoordinates[drive]);
ch = ','; ch = ',';
} }
sncatf(jsonResponse, STRING_LENGTH, "],\"extr\":"); // announce the extruder positions sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "],\"extr\":"); // announce the extruder positions
ch = '['; ch = '[';
for (int8_t drive = AXES; drive < DRIVES; drive++) // loop through extruders for (int8_t drive = AXES; drive < DRIVES; drive++) // loop through extruders
{ {
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.3f\"", ch, gc->GetExtruderPosition(drive - AXES)); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.3f\"", ch, gc->GetExtruderPosition(drive - AXES));
ch = ','; ch = ',';
} }
strncat(jsonResponse, "]", STRING_LENGTH); strncat(jsonResponse, "]", ARRAY_UPB(jsonResponse));
} }
else else
{ {
@ -504,10 +470,10 @@ void Webserver::GetStatusResponse(uint8_t type)
// These are all returned in a single vector called "poll". // These are all returned in a single vector called "poll".
// This is a poor choice of format because we can't easily tell which is which unless we already know the number of heaters and extruders. // This is a poor choice of format because we can't easily tell which is which unless we already know the number of heaters and extruders.
char c = (gc->PrintingAFile()) ? 'P' : 'I'; char c = (gc->PrintingAFile()) ? 'P' : 'I';
snprintf(jsonResponse, STRING_LENGTH, "{\"poll\":[\"%c\",", c); // Printing snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"poll\":[\"%c\",", c); // Printing
for (int8_t heater = 0; heater < HEATERS; heater++) for (int8_t heater = 0; heater < HEATERS; heater++)
{ {
sncatf(jsonResponse, STRING_LENGTH, "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater)); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater));
} }
// Send XYZ and extruder positions // Send XYZ and extruder positions
float liveCoordinates[DRIVES + 1]; float liveCoordinates[DRIVES + 1];
@ -515,7 +481,7 @@ void Webserver::GetStatusResponse(uint8_t type)
for (int8_t drive = 0; drive < DRIVES; drive++) // loop through extruders for (int8_t drive = 0; drive < DRIVES; drive++) // loop through extruders
{ {
char ch = (drive == DRIVES - 1) ? ']' : ','; // append ] to the last one but , to the others char ch = (drive == DRIVES - 1) ? ']' : ','; // append ] to the last one but , to the others
sncatf(jsonResponse, STRING_LENGTH, "\"%.2f\"%c", liveCoordinates[drive], ch); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "\"%.2f\"%c", liveCoordinates[drive], ch);
} }
} }
@ -525,43 +491,43 @@ void Webserver::GetStatusResponse(uint8_t type)
switch (platform->GetZProbeSecondaryValues(v1, v2)) switch (platform->GetZProbeSecondaryValues(v1, v2))
{ {
case 1: case 1:
sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d)\"", v0, v1); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"probe\":\"%d (%d)\"", v0, v1);
break; break;
case 2: case 2:
sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2);
break; break;
default: default:
sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d\"", v0); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"probe\":\"%d\"", v0);
break; break;
} }
// Send the amount of buffer space available for gcodes // Send the amount of buffer space available for gcodes
sncatf(jsonResponse, STRING_LENGTH, ",\"buff\":%u", GetReportedGcodeBufferSpace()); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"buff\":%u", GetReportedGcodeBufferSpace());
// Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false. // Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false.
if (type != 0) if (type != 0)
{ {
sncatf(jsonResponse, STRING_LENGTH, ",\"homed\":[%d,%d,%d]", sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"homed\":[%d,%d,%d]",
(gc->GetAxisIsHomed(0)) ? 1 : 0, (gc->GetAxisIsHomed(0)) ? 1 : 0,
(gc->GetAxisIsHomed(1)) ? 1 : 0, (gc->GetAxisIsHomed(1)) ? 1 : 0,
(gc->GetAxisIsHomed(2)) ? 1 : 0); (gc->GetAxisIsHomed(2)) ? 1 : 0);
} }
else else
{ {
sncatf(jsonResponse, STRING_LENGTH, ",\"hx\":%d,\"hy\":%d,\"hz\":%d", sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"hx\":%d,\"hy\":%d,\"hz\":%d",
(gc->GetAxisIsHomed(0)) ? 1 : 0, (gc->GetAxisIsHomed(0)) ? 1 : 0,
(gc->GetAxisIsHomed(1)) ? 1 : 0, (gc->GetAxisIsHomed(1)) ? 1 : 0,
(gc->GetAxisIsHomed(2)) ? 1 : 0); (gc->GetAxisIsHomed(2)) ? 1 : 0);
} }
// Send the response sequence number // Send the response sequence number
sncatf(jsonResponse, STRING_LENGTH, ",\"seq\":%u", (unsigned int) seq); sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"seq\":%u", (unsigned int) seq);
// Send the response to the last command. Do this last because it is long and may need to be truncated. // Send the response to the last command. Do this last because it is long and may need to be truncated.
strncat(jsonResponse, ",\"resp\":\"", STRING_LENGTH); strncat(jsonResponse, ",\"resp\":\"", ARRAY_UPB(jsonResponse));
size_t jp = strnlen(jsonResponse, STRING_LENGTH); size_t jp = strnlen(jsonResponse, ARRAY_UPB(jsonResponse));
const char *p = gcodeReply; const char *p = gcodeReply;
while (*p != 0 && jp < STRING_LENGTH - 2) // leave room for the final '"}' while (*p != 0 && jp < ARRAY_SIZE(jsonResponse) - 3) // leave room for the final '"}\0'
{ {
char c = *p++; char c = *p++;
char esc; char esc;
@ -588,7 +554,7 @@ void Webserver::GetStatusResponse(uint8_t type)
} }
if (esc) if (esc)
{ {
if (jp == STRING_LENGTH - 3) if (jp == ARRAY_SIZE(jsonResponse) - 4)
{ {
break; break;
} }
@ -601,7 +567,7 @@ void Webserver::GetStatusResponse(uint8_t type)
} }
} }
jsonResponse[jp] = 0; jsonResponse[jp] = 0;
strncat(jsonResponse, "\"}", STRING_LENGTH); strncat(jsonResponse, "\"}", ARRAY_UPB(jsonResponse));
} }
/* /*
@ -629,8 +595,8 @@ void Webserver::ParseGetPost()
platform->Message(HOST_MESSAGE, "\n"); platform->Message(HOST_MESSAGE, "\n");
} }
int i = 5; size_t i = 5;
int j = 0; size_t j = 0;
clientRequest[j] = 0; clientRequest[j] = 0;
clientQualifier[0] = 0; clientQualifier[0] = 0;
while (clientLine[i] != ' ' && clientLine[i] != '?') while (clientLine[i] != ' ' && clientLine[i] != '?')
@ -644,13 +610,17 @@ void Webserver::ParseGetPost()
{ {
i++; i++;
j = 0; j = 0;
for(;;) while(j < ARRAY_UPB(clientQualifier))
{ {
char c = clientLine[i++]; char c = clientLine[i++];
if (c == ' ') if (c == ' ')
{ {
break; break;
} }
else if (c == '+')
{
clientQualifier[j++] = ' ';
}
else if (c == '%' && isalnum(clientLine[i]) && isalnum(clientLine[i + 1])) else if (c == '%' && isalnum(clientLine[i]) && isalnum(clientLine[i + 1]))
{ {
c = clientLine[i++]; c = clientLine[i++];
@ -686,7 +656,7 @@ void Webserver::ParseClientLine()
postSeen = false; postSeen = false;
getSeen = true; getSeen = true;
if (!clientRequest[0]) if (!clientRequest[0])
strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH); strncpy(clientRequest, INDEX_PAGE, ARRAY_SIZE(clientRequest));
return; return;
} }
@ -698,7 +668,7 @@ void Webserver::ParseClientLine()
getSeen = false; getSeen = false;
if (!clientRequest[0]) if (!clientRequest[0])
{ {
strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH); strncpy(clientRequest, INDEX_PAGE, ARRAY_SIZE(clientRequest));
} }
return; return;
} }
@ -707,15 +677,15 @@ void Webserver::ParseClientLine()
if (postSeen && ((bnd = StringContains(clientLine, "boundary=")) >= 0)) if (postSeen && ((bnd = StringContains(clientLine, "boundary=")) >= 0))
{ {
if (strlen(&clientLine[bnd]) >= POST_LENGTH - 4) if (strlen(&clientLine[bnd]) >= ARRAY_SIZE(postBoundary) - 4)
{ {
platform->Message(HOST_MESSAGE, "Post boundary buffer overflow.\n"); platform->Message(HOST_MESSAGE, "Post boundary buffer overflow.\n");
return; return;
} }
postBoundary[0] = '-'; postBoundary[0] = '-';
postBoundary[1] = '-'; postBoundary[1] = '-';
strncpy(&postBoundary[2], &clientLine[bnd], POST_LENGTH - 3); strncpy(&postBoundary[2], &clientLine[bnd], ARRAY_SIZE(postBoundary) - 3);
strncat(postBoundary, "--", POST_LENGTH); strncat(postBoundary, "--", ARRAY_SIZE(postBoundary));
return; return;
} }
@ -731,10 +701,11 @@ void Webserver::ParseClientLine()
while (clientLine[bnd] && clientLine[bnd] != '"') while (clientLine[bnd] && clientLine[bnd] != '"')
{ {
postFileName[i++] = clientLine[bnd++]; postFileName[i++] = clientLine[bnd++];
if (i >= POST_LENGTH) if (i >= ARRAY_SIZE(postFileName))
{ {
i = 0; i = 0;
platform->Message(HOST_MESSAGE, "Post filename buffer overflow.\n"); platform->Message(HOST_MESSAGE, "Post filename buffer overflow.\n");
break;
} }
} }
postFileName[i] = 0; postFileName[i] = 0;
@ -805,11 +776,11 @@ bool Webserver::CharFromClient(char c)
clientLineIsBlank = false; clientLineIsBlank = false;
clientLine[clientLinePointer] = c; clientLine[clientLinePointer] = c;
clientLinePointer++; clientLinePointer++;
if (clientLinePointer >= STRING_LENGTH) if (clientLinePointer + 2 >= ARRAY_SIZE(clientLine))
{ {
platform->Message(HOST_MESSAGE, "Client read buffer overflow. Data:\n"); platform->Message(HOST_MESSAGE, "Client read buffer overflow. Data:\n");
clientLine[STRING_LENGTH] = '\n'; // note that clientLine is now STRING_LENGTH+2 characters long to make room for these clientLine[ARRAY_SIZE(clientLine) - 2] = '\n';
clientLine[STRING_LENGTH + 1] = 0; clientLine[ARRAY_SIZE(clientLine) - 1] = 0;
platform->Message(HOST_MESSAGE, clientLine); platform->Message(HOST_MESSAGE, clientLine);
reprap.GetNetwork()->SendAndClose(NULL); // close the connection reprap.GetNetwork()->SendAndClose(NULL); // close the connection
@ -942,20 +913,20 @@ void Webserver::HandleReply(const char *s, bool error)
if (error) if (error)
{ {
strcpy(gcodeReply, "Error: "); strcpy(gcodeReply, "Error: ");
strncat(gcodeReply, s, STRING_LENGTH); strncat(gcodeReply, s, ARRAY_UPB(gcodeReply));
} }
else else
{ {
strncpy(gcodeReply, s, STRING_LENGTH); strncpy(gcodeReply, s, ARRAY_UPB(gcodeReply));
} }
gcodeReply[STRING_LENGTH] = 0; // array is dimensioned to STRING_LENGTH+1 gcodeReply[ARRAY_UPB(gcodeReply)] = 0;
} }
++seq; ++seq;
} }
void Webserver::AppendReply(const char *s) void Webserver::AppendReply(const char *s)
{ {
strncat(gcodeReply, s, STRING_LENGTH); strncat(gcodeReply, s, ARRAY_UPB(gcodeReply));
} }
// Get the actual amount of gcode buffer space we have // Get the actual amount of gcode buffer space we have

View file

@ -33,11 +33,13 @@ Licence: GPL
#define KO_START "rr_" #define KO_START "rr_"
#define KO_FIRST 3 #define KO_FIRST 3
#define POST_LENGTH (1300) // max amount of POST data we can accept
const unsigned int postLength = 1400; // max amount of POST data we can accept
const unsigned int webInputLength = 1400; // max size of web interface requests and related stuff
const unsigned int gcodeBufLength = 2048; // size of our gcode ring buffer, ideally a power of 2 const unsigned int gcodeBufLength = 2048; // size of our gcode ring buffer, ideally a power of 2
const unsigned int minReportedFreeBuf = 100; // the minimum free buffer we report if not zero const unsigned int minReportedFreeBuf = 100; // the minimum free buffer we report if not zero
const unsigned int maxReportedFreeBuf = 900; // the max we own up to having free, to avoid overlong messages const unsigned int maxReportedFreeBuf = 1024; // the max we own up to having free, to avoid overlong messages
const unsigned int jsopnReplyLength = 1200; // size of buffer used to hold JSON reply
class Webserver class Webserver
{ {
@ -62,7 +64,7 @@ class Webserver
void SendFile(const char* nameOfFileToSend); void SendFile(const char* nameOfFileToSend);
void ParseQualifier(); void ParseQualifier();
void CheckPassword(); void CheckPassword();
void LoadGcodeBuffer(const char* gc, bool convertWeb); void LoadGcodeBuffer(const char* gc);
bool PrintHeadString(); bool PrintHeadString();
bool PrintLinkTable(); bool PrintLinkTable();
void GetGCodeList(); void GetGCodeList();
@ -86,18 +88,18 @@ class Webserver
float lastTime; float lastTime;
float longWait; float longWait;
bool receivingPost; bool receivingPost;
char postBoundary[POST_LENGTH]; char postBoundary[postLength];
int boundaryCount; int boundaryCount;
char postFileName[POST_LENGTH]; char postFileName[postLength];
FileStore* postFile; FileStore* postFile;
bool postSeen; bool postSeen;
bool getSeen; bool getSeen;
bool clientLineIsBlank; bool clientLineIsBlank;
char clientLine[STRING_LENGTH+2]; // 2 chars extra so we can append \n\0 char clientLine[webInputLength];
char clientRequest[STRING_LENGTH]; char clientRequest[webInputLength];
char clientQualifier[STRING_LENGTH]; char clientQualifier[webInputLength];
char jsonResponse[STRING_LENGTH+1]; char jsonResponse[jsopnReplyLength];
char gcodeBuffer[gcodeBufLength]; char gcodeBuffer[gcodeBufLength];
unsigned int gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer unsigned int gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
int clientLinePointer; int clientLinePointer;